天津福康,电子烟,磁疗,远红外线产品。
十一 29

重读AWDWR笔记

分类:tech | 给我留言 |

第9章

  • 使用局部模板。
render :partial => "cart", :o bject => @cart
render :partial => "cart_item", :collection => @cart.items
  • 辅助方法的一个示例。
# app/views/layout/stroe.rhtml
<%= hidden_div_if(@cart.items.empty?, :id => "cart") %>
  <%= render :partial => "cart", :o bject => @cart %>
<div>

# app/controllers/store_controller.rb
def hidden_div_if(condition, attributes = {})
  if condition
    attributes["style"] = "display: none;"
  end
  attrs = tag_options(attributes.stringify_keys)
  "
" end

第10章

  • validates_inclusion_of 方法验证某属性,在指定的列表中存在。防止别人构造不存在的支付方法逃避支付。
PAYMENT_TYPES = [
    ["Check", "check"],
    ["Credit Card", "cc"],
    ["Purchase Order", "po"]
  ]

validates_inclusion_of :pay_type, :in => PAYMENT_TYPES.map {|disp, value| value}
  • 一个向 Order 中填充 Cart 里面订购项目的过程。
# app/controller/stroe_controller.rb
def save_order
  @cart = find_cart
  @order = Order.new(params[:order])
  @order.add_line_items_from_cart(@cart)
  if @order.save
    session[:cart] = nil
    redirect_to_index("Thank you for your order")
  else
    render :action => :checkout
  end
end

# app/models/order.rb
def add_line_items_from_cart(cart)
  cart.items.each do |item|
    li = LineItem.from_cart_item(item)
    line_items << li
  end
end

# app/models/line_item.rb
def self.from_cart_item(cart_item)
  li = self.new
  li.product = cart_item.product
  li.quantity = cart_item.quantity
  li.total_price = cart_item.price
  li
end
十一 29

重读AWDWR笔记

分类:tech | 给我留言 |

第12章

  • 使用 :through 声明,可以通过间接关联来联系两张表。
  • 可以使用 curl 或者 wget 工具来模拟请求xml。
class Product < ActiveRecord::Base
  has_many :o rders, :through => :line_items
end

class Order < ActiveRecord::Base
  has_many :line_itemss
end

class LineItem < ActiveRecord::Base
  belongs_to :o rders
  belongs_to :product
end
curl http://localhost:3000/info/who_bought/1

第11章

  • after_destroy 钩子方法于 delete 同在一个事务中,因此只要该方法里抛出异常,整个事务会回滚。after_destroy 会在 delete 语句执行之后被调用。
  • 这里关键概念是,用异常来表示删除用户的过程中出现了错误。这里的异常同时承担两个任务。首先,在事务内部,异常会导致自动回滚;如果在删除用户之后 user 表为空,抛出异常可以撤销删除操作,恢复最后一个用户。
  • 其次,异常可以把错误信息带回给控制器。
# app/model/user.rb
def after_destroy
  if User.count.zero?
    raise "Can't delete last user."
  end
end

# app/controllers/login_controller.rb
def delete_user
  if request.post?
    user = User.find(params[:id])
    begin
      use.destroy
      flash[:notice] = "User #{user.name} deleted"
    rescue Exception => e
      flash[:notice] = e.message
    end
  end
  redirect_to :action => :list_users
end
  • before_filter 前置过滤器进行访问控制。
  • 利用 session 存储登录前的 uri 做出更有友好的登录系统。
# app/controllers/application.rb
private

def authorize
  unless User.find_by_id(session[:user_id])
    session[:original_uri] = request.request_uri
    flash[:notice] = "Please log in."
    redirect_to :action => :login, :controller => :login
  end
end

# app/controllers/login_controller.rb
before_filter :authorize, :except => :login
  • 登录的一个典型做法。
# app/controllers/login_controller.rb
def login
  session[:user_id] = nil
  if request.post?
    user = User.authenticate(params[:name], params[:password])
    if user
      session[:user_id] = user.id
      redirect_to :action => :index
    else
      flash[:notice] = "Invalid user/password combination"
    end
  end
end
  • 看一下完整的 user model。
# app/models/user.rb
require "digest/sha1"

class User < ActiveRecord::Base
  validates_presence_of :name
  validates_uniqueness_of :name

  attr_accessor :password_confirmation
  validates_confirmation_of :password

  def validate
    errors.add_to_base("Missing password") if hashed_password.blank?
  end

  def self.authenticate(name, password)
    user = self.find_by_name(name)
    if user
      expected_password = encrypted_password(password, user.salt)
      if user.hashed_password != expected_password
        user = nil
      end
    end
  end

  # 'password' is a virtual attribute
  def password
    @password
  end

  def password =(pwd)
    @password = pwd
    return if pwd.blank?
    create_new_salt
    self.hashed_password = User.encrypted_password(self.password, self.salt)
  end

  private

  def self.encrypted_password(password, salt)
    string_to_hash = password + "wibble" + salt
    Digest::SHA1.hexdigest(string_to_hash)
  end

  def create_new_salt
    self.salt = self.object_id.to_s + rand.to_s
  end
end
十一 28

带着restful_authentication运行rspec时,貌似cookies无效的问题

起因:在未做自己开发rspec的时候,只是调试下安装完 restful_authentication 的rspec和部分test时候,出现了几个错误:

1、

Spec::Mocks::MockExpectationError in ‘SessionsController Logging in by cookie fails cookie login with bad cookie’
fails cookie login with bad cookie(Spec::Rails::Example::ControllerExampleGroup::Subclass_1::Subclass_4) expected :cookies with (any args) once, but received it 0 times
./spec/controllers/authenticated_system_spec.rb:85:

2、

‘SessionsController Logging in by cookie logs in with cookie’ FAILED
expected true, got false
./spec/controllers/authenticated_system_spec.rb:81:

3、

‘AccessControlTestController requesting xml; I am logged in and Login is required succeeds’ FAILED
expected “<?xml version=\”1.0\” encoding=\”UTF-8\”?>\n<hash>\n  <success>xml</success>\n</hash>\n”, got “HTTP Basic: Access denied.\n”
./spec/controllers/access_control_spec.rb:65:

这是为什么呢?

经过N个小时的思考和google,还是没有发现问题,但是刚才检查 logged_in?.should be_true 一句时,发现我在application_helper.rb里 竟然定义了个同名的方法。就是这个同名方法,搞乱了正常的测试结果。so,改名后,一切都正常了。

经验:1、学习和使用TDD开发还是需要时间的,2、下次应该不会犯了吧。。。。

十一 28

在windows和netbeans6.5上,准备Rspce开发

分类:tech | 给我留言 |

一、环境

ruby -v

>> ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32]

rails -v

>> Rails 2.1.2

二、需要安装的gem和plugin

gem install ZenTest

因为我的Rails是2.1.2,所以在安装plugin时,用了下面的方法:

ruby script/plugin install git://github.com/dchelimsky/rspec.git
ruby script/plugin install git://github.com/dchelimsky/rspec-rails.git
ruby script/generate rspec

另外还有一些辅助的工具,不在此详述

三、出现的问题

1、netbeans加载autotest时候的环境变量

解决:在系统环境中,需要加入home=当前项目的根文件夹,这样nb就能正常启用autotest了

2、restful_authentication的rspec时,出现mysql的Mysql::Error: Incorrect datetime value错误

mysql配置文件my.ini中,注释掉 sql-mode=”STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION” 一行(我的在87行)

下面添加一行:sql-mode=”"

重启mysql

四:参考文章

RubyTesting

The Basics of Creating Rails Plugins,创建Rails插件的基础教程

RSpec的很重要的中文文档:
http://www.letrails.cn/archives/20
http://www.letrails.cn/archives/advanced-rspec-tutorials-rspec-scaffold
http://www.letrails.cn/archives/11-02-advanced-rspec-tutorials-basics
http://www.letrails.cn/archives/11-07-advanced-rspec-tutorials-mocking

《The Rails Way》第18章:Rspec on Rails

Autotest Rails on Windows

这里提到的 set home=. 方法,是在cmd环境下可以,集成到netbeans中,还得用上面 三(1) 里的方法。(ps:blogspot又打不开了?)

好了,上面只是一篇准备开发Rspec on Rails 时候的笔记,更多的内容还会以笔记形式更新。

新站广告:一汽家园,一汽的生活圈 www.17jiayuan.com ,租房,在线游戏,二手交易,一汽交友圈。

目前状况:正在开发一个Rails项目,没想到和邀请我开发的人想法碰到一起,所以很高兴开发这个项目。项目的主旨是松耦合,高复用。这种程度到了应用的开发也要符合这个要求,这和研究半年多uchome得到的想法很一致。不多说了,等项目上线后,再整理心得吧。

由于个人原因耽误了些进度,在此表示歉意。

User Points,值得参考。

十一 28

重读AWDWR笔记

分类:tech | 给我留言 |

感恩节放假,抓紧时间重读一下AWDWR把一些知识点记录如下。
第6章

  • 迁移习惯 create 来创建表,add 给现有表增加字段。你可以会看到 002_add_price.rb 的迁移。
  • model 中验证方法设置为 protected 是因为该方法必须在特定的模型上下文中调用,不能在外部调用。
protected
def validate
  errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01
end
  • errors.add() 方法第一个参数是字段名称,第二个参数是出错信息的正文。
  • 在将价格和 0.01 比较之前,先检查它是不是 nil。试图将 nil 和数字比较会引发异常。
  • 下面代码演示了如何用正则表达式验证模型属性。
validates_format_of :image_url,
  :with => %r{\.(gif|jpg|png)$}i,
  :message => "must be a URL for a GIF, JPG, or PNG image"
  • cycle 交替的设置两个属性。
  • h() 方法用于对内容进行格式化,去除其中的 HTML。
  • link_to 方法后面的 :confirm =>"Are you sure?"。
  • stylesheet_link_tag
  • 关于渲染表格的方法。
<table>
	<tr>
	<% for column in Product.content_columns %>
		<th&gt<%= column.hunman_name %></th>
	<% end %>
	</tr>
	<tr>
	<% for column in Product.content_columns %>
		<td&gt<%= h product.send(column.name) %></td>
	<% end %>
	</tr>
</table>

第7章

  • number_to_currency(product.price) 格式化价格的方法。
  • 如何使用类方法。
class StoreController < ApplicationController
  def inde
    @products = Product.find_products_sale
  end
end

class Product < ActiveRecord:Base
  def self.find_products_sale
    find(:all, :o rder => "title")
  end
end

第8章

  • 如何把 session 放在数据库中。
  • 在 session 中保存尽可能简单的东西:字符串,数字,等等。应用层面的对象应该放在数据库,然后把它们的主键放入 session,需要时根据 session 中的主键来查找对象。
rake db:sessions:create
rake db:migrate
#environment.rb
config.action_controller.sesson_store = :active_recored_store
  • 下面是一段非常常见的购物代码。
  • 注意下面的 controller 中演示了如何防止构造错误的传递参数。
class StoreController < ApplicationController
  before_filter :find_cart, :except => :empty_cart

  def index
    @products = Product.find_product_for_sale
  end

  def add_to_cart
    begin
      product = Product.find(params[:id])
    rescue ActiveRecord::RecordNotFound
      logger.error("Attempt to access invalid product #{params[:id]}.")
      redirect_to_index unless request.xhr?
    end
  end

  def empty_cart
    session[:cart] = nil
    redirect_to_index
  end

  def checkout
    if @cart.item.empty?
      redirect_to_index("Your cart is empty.")
    else
      @order = Order.new
    end
  end

  def save_order
    @order = Order.new(params[:order])
    @order.add_line_items_from_cart(@cart)
    if @order.save
      session[:cart] = nil
      redirect_to_index("Thank you for your order.")
    else
      render :action => :checkout
    end
  end

  private

  def redirect_to_index(msg = nil)
    flash[:notice] = msg if msg
    redirect_to :action => :index
  end

  def find_cart
    @cart = (session[:cart] ||= Cart.new)
  end
end

class Cart
  attr_reader :items

  def initialize
    @items = []
  end

  def add_product(product)
    current_item = @items.find {|item| item.product == product}
    if current_item
      current_item.increment_quantity
    else
      current_item = CartItem.new(product)
      @items << current_item
    end
    current_item
  end

  def total_items
    @items.sum {|item| item.quantity}
  end

  def total_price
    @items.sum {|item| item.price}
  end
end

class CartItem
  attr_reader :product, :quantity

  def initialize(product)
    @product = product
    @quantity = 1
  end

  def increment_quantity
    @quantity += 1
  end

  def title
    @product.title
  end

  def price
    @product.price * @quantity
  end
end
  • Mac 用户使用 Console.app,在 Application 的 Utilities 中,可以很方便的跟踪日志文件,只要使用 open 命令,并传入日志文件的名称即可。
十一 27

1.3.3 Continuations

Continuations 是一种非常强大的控制流机制。一个 Continuation 代表了调用栈和词法变量的特定状态。它是 Ruby 代码执行过程中特定点的一个快照。不幸的是,在 Ruby1.8 的实现中 Continuations 是如此的慢以至于对于许多的应用来说根本无法使用。在即将到来的 Ruby1.9 虚拟机中情况可能会有所改善,但最好不要期望在使用了 Ruby1.8 的 Continuation 的情况下能够获得好的性能。尽管如此,Continuations 仍然是非常有用的概念,基于 Continuations 的 web 框架提供了 Rails 的一些有趣的选择,因此在这里我们会对它们做一下考察。

Continuations之所以非常强大有下面几个原因:

  • Continuations其实就是一些对象;它们可以被传递在函数之间。
  • 可以从任何位置调用Continuations。只要持有对Continuation的引用,就可以对其进行调用。
  • Continuations是可重入的。可以使用Continuations从一个函数多次返回。

Continuations常常被描述为“结构化的GOTO“。因此,它们应该像任何类型的GOTO概念一样被谨慎的对待。在应用代码中不应该出现或者应该极少出现Continuations;它们通常应该被封装在库文件中。我不这么说是因为我认为开发人员应该被与它们隔离开来。

Continuations是如此普遍的一个概念,以至于通常来说在其之上建立抽象比直接使用它更合理。程序员在构建应用软件的时候应考虑使用”外部迭代(external iterator)“或者”协同程序(coroutine)“(两个都是在Continuations之上的抽象)而不是”continuation“。

SeaSide是一个基于Continuations的Smalltalk Web应用框架。Continuations 在SeaSide 中用于管理会话状态。每个用户的会话都对应一个服务器端的Continuation。当一个请求到达的时候Continuation将被调用而且更多的代码被执行。其结果是整个的事务能够被编写为单独的一段代码流,即使它们跨越了多个HTTP请求。这种能力是由于Smalltalk的Continuation能够被序列化;它们可以被写到一个数据库或者文件系统,然后被取出和反序列化,并在请求中重新调用。Ruby的Continuation不能被序列化。在Ruby中,Continuation只能被装载到内存中而不能被转换为一个字节流。

Borges(http://borges.rubyforge.org/)是直接移植到Ruby的Seaside 2实现。SeaSide与Borges的最根本的区别就是Borges必须将所有当前的Continuations保存在内存中,因为它们不能被序列化。这个巨大的限制很不幸地阻止了Borges在任何规模的Web应用中被成功使用。只要Continuation在任何一种Ruby实现中被支持,这个限制就可以被克服了。

Continuation的能力可以通过下面的Borges示例代码来见证,它用于渲染在线商店的一系列条目:

class SushiNet::StoreItemList < Borges::Component
  def choose(item)
    call SushiNet::StoreItemView.new(item)
  end

  def initialize(items)
    @batcher = Borges::BatchedList.new items, 8
  end
  def render_content_on(r)
    r.list_do @batcher.batch do |item|
      r.anchor item.title do choose item end
    end
    r.render @batcher
  end
end # class SushiNet::StoreItemList

Action的功能render_content_on方法中完成,使用了BatchedList(一个分页器)来渲染一组分页的产品链接。有趣的地方在于anchor方法的调用,它存储了choose方法,当相应的链接被点击时调用。

但是,关于Continuations在Web编程中有多大的用处仍然存在着很大争议,HTTP被设计为无状态的协议,使用Continuations来实现Web事务与无状态的想法正好相反。所有的Continuations都被存在服务器端,这需要占用额外的内存和硬盘空间。需要粘性会话(sticky session)来将一个用户始终导向同一台服务器。因此,如果一台服务器当机,它的所有会话都会丢失。最流行的基于Seaside的应用DabbleDB(http://dabbledb.com)事实上很少使用Continuations。

十一 27

ruby for rails 摘录

分类:tech | 给我留言 |

if 关键字

if condition
  # code here, executed if condition evaluates to true.
end

if x > 100 then puts x end

if x > 100; puts x end

if condition
  # code executed if condition is true.
else
  # code executed if conditions is false.
end

if condition1
  # code executed if condition1 is true.
elsif condition2
  # code executed if condition2 is true.
elsif condition3
  # code executed if neither condition1
  # nor condition2 is true, but condition3 is true.
end

print "Enter a integer: "
n = gets.to_i
if n > 0
  puts "Your number is positive."
elsif n < 0
  puts "Your number is negative."
else
  puts "Your number is zero."
end

puts "Big numbe!" if x > 100

unless

if not (x == 1)
if !(x == 1)
unless x == 1

case 语句以一个表达式开始,然后处理列出得各种可能得匹配。每一个可能的匹配包含在一个 when 表达式中,该表达式由一个或多个可能的匹配和一段代码构成。

print "Exit the program? (yes or no): "
answer = gets.chomp
case answer
when "yes", "y"
  puts "Good-bye"
  exit
when "no", "n"
  puts "OK, we'll continue"
else
  puts "That's an unknow answer -- assuming you meant 'no'"
end

用 loop 方法实现无条件循环。loop 方法不带任何参数,但它可以带一个代码块。代码块包含想要循环执行的代码。代码块可以是大括号 {} 或者用关键字 do 和 end 。当然你也可以使用 break 来中断循环。或者使用 next 来跳到下一个循环。

n = 1
loop do
  n = n + 1
  if unless n == 7
  break if n > 9
end

条件循环可以根据 while 和 until 实现。

n = 1
while n < 11
  puts n
  n = n + 1
end
puts "Done!"

n = 1
begin
  puts n
  n = n + 1
end while n < 11
puts "Done!"

n = 1
until n > 10
  puts n
  n = n + 1
end

n = 1
n = n + 1 until n == 10
puts "We've reached 10!"

基于值列表的循环

celsius = [0, 10, 20, 30, 40, 50, 60, 70]
puts "Celsius\tFahrenheit"
for c in celsius
  puts "c\t#{Temperature.c2f(c)}"
end
十一 17

ruby for rails 摘录

分类:tech | 给我留言 |

class, module, def 标志着切换到新的 self, main 是默认的 self 对象用来引用自己的专门术语。self 作为消息默认的接受者,如果消息接受者是 self ,可以省略接受者和圆点。

如果存在同名的方法名和变量,而且你使用裸词标识符,那么变量具有优先权。要强制 Ruby 将标识符当作方法名,你必须使用 self.talk 或者用参数列表为空的 talk() 来调用方法。

有一个场合,即使是发送消息给当前的 self,也必须使用完整的“对象 圆点 消息”记法,那就是在调用写方法的时候。因为 Ruby 总之将序列“裸词=值”解释为对局部变量的赋值。为了调用当前对象的 venue= 方法,必须显示的给出 self。

private 和 protected 的区别
私有方法意味着该方法不能使用显式的接收者来调用。Ruby 认为你想要发送消息给当前对象 self。所以仅当 self 是一个可以响应消息的对象时,该消息才有对象接收。那啥时候 self 才会是类的实例呢?当执行类的任何一个实例方法时。所以看如下代码

class Baker
  def bake_cake
    @batter = []
    pour_flour
    add_egg
    return Cake.new
  end

  def pour_flour
    @batter.push(Flour.new)
  end

  def add_egg
    @batter.push(Egg.new)
  end

  private :pour_flour, :add_egg
end

总结,当 add_egg 标记为私有的,就是说Baker的实例对象可以将此消息发送给它自己,但是其它任何对象都不可以给该Baker实例对象发送该消息。Ruby 通过禁止对私有方法使用显示的接收者来获得此私有性。

保护方法的规则是:只要默认对象 self 和你想要调用的方法所属的对象是同一个类的实例,你就可以调用该保护方法。如果某个对象调用其所属的保护方法,而该对象所属的类和self所属的类相同,那么该调用是合法的。

class C
  def initialize(n)
    @n = n
  end

  def n
    @n
  end

  def compare(c)
    if c.n > n
      puts "The other object's n is bigger."
    else
      puts "The other object's n is the same or smaller"
    end
  end

  protected :n
end

c1 = C.new(100)
c2 = C.new(201)
c1.compare(c2)
十一 16

ruby for rails 摘录

分类:tech | 给我留言 |

模块没有实例,模块被混含在类中。这样类的实例可以调用定义在模块中的实例方法。混含操作由 include 语句实现。require 或 load 时,加载的内容放在引号里。但是使用 include 时,不使用引号。

module Stacklike
  attr_reader :stack

  def initialize
    @stack = Array.new
  end

  def add_to_stack(obj)
    @stack.push(obj)
  end

  def take_from_stack
    @stack.pop
  end
end
require "stacklike"
class CargoHold
  include Stacklike

  def load_and_report(obj)
    pust obj.object_id
    add_to_stack(obj)
  end

  def unload
    take_from_stack
  end
end

使用名称作为类名,使用形容词作为模块的名。上面的例子演示了如何进一步发挥模块的作用。

当给对象发送它不理解的消息的时候,会触发内建方法 method_missing 调用。

class Bicycle
  attr_reader :gears, :wheels, :seats

  def initialize(gears = 1)
    @wheels = 2
    @seats = 1
    @gears = gears
  end
end

class Tandem < Bicycle
  def initialize(gears)
    super
    @seats = 2
  end
end

以super提升方法查找路径。以裸词的方式调用时,自动向前传递调用 super 的发给你发所获得的参数。这个是默认滴。用空参数表调用 super() 时,不给上一级方法传递任何参数,即使是当前方法的谙熟也不传递。用特定参数 super(a, b, c) 时,传递这些指定的参数。

十一 16

ruby for rails 摘录

分类:tech | 给我留言 |

实例变量使得单个对象可以记忆状态。实例变量的名字以@开头。实例变量仅仅对于它所属的对象来说是可见的。在一个特定类的某一方法中初始化的实例变量,与同一个类的其他方法定义中引用的同名实例变量是同一个。

class c
  def inst_var_init(value)
    puts "Setting an instance variable..."
    @ivar = value
  end

  def inst_var_report
    puts "Inspection the value of the instance variable..."
    puts @ivar
  end
end

初始化对象状态

class c
  def initialize(venue, date)
    @venue = venue
    @date = date
  end

  def venue
    @venue
  end

  def date
    @date
  end
end

=号方法和语法糖衣,Ruby允许定义以等号结束的方法。另外当解释器看到一个裸词后面有等号的时候。它会自动忽略等号前面的空格。从而得到一条单独的消息,例如 price= 。

class Ticket
  def price=(amount)
    @price = amount
  end

  def price
    @price
  end
end

字符串内建的split方法的示例

month, day, year = date.split('/')

ActiveRecord自动生成与数据库表的字段名相对应的设置方法。通过 params 方法,ActiveRecord 收集了所有属于 customer 的值, 并将它们成批地传递到新生成的 Customer 对象中。

customer = Customer.new(params[:customer])

自动生成属性方法,attr_accessor, attr_reader, attr_writer

常量的名字是以一个大写字母开头。可以在类外引用常量,Ticker::VENUES

class Ticket
  VENUES = ["Convention Center", "Fairgrounds", "Town Hall"]
end

每个类有自己的实例方法,可以继承链上面的类的实例方法。定义一个动作,就是给控制器类添加一个实例方法。

  • 信息发布
  • 最新留言
  • 热评日志
我的2010,新的开始啦

稍后把团队的介绍放上,呵呵。
关于 里克
里克:本名李伟,全职SOHO开发,技术顾问
长春互联 技术总监
邮件:liwei@echangchun.net
QQ:5175486
MSN:liwei78@live.com
某高潜力SNS网站团队成员
对iphone和android的开发很感兴趣