ruby rake tips

Posted by wxianfeng Tue, 25 Jan 2011 17:59:00 GMT

环境:ruby 1.9.2 + rake 0.8.7 + rails 3.0.3

今天在写 rake 文件 , 提示我没找到 model Class,问题在于我没加 => :environment , 那为什么加上 => :environment 就可以了 , 研究了下rake:

task :test => :environment do 
end

其中后面的 :environment 也是 一个 task , 也就是说在执行 test 这个 task 之前 会执行 :environment 这个 task,有点像rails controller 中的 before_filter ,那么 :environment 这个task 定义在什么地方,rails project中也没有啊 。。。,定义在 rails 源码中了 ,你在 你的project下执行 rake environment 不会报错的

wxianfeng@ubuntu:/usr/local/system/projects/entos/ent_os$ rake environment --trace 
(in /usr/local/system/projects/entos/ent_os)
** Invoke environment (first_time)
** Execute environment

不过 也看不到 输出什么信息,因为只是加载了 运行环境而已 ,那么从哪里执行的,定义在 project 下 Rakefile 中了

# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require File.expand_path('../config/application', __FILE__)
require 'rake'

EntOs::Application.load_tasks

继续跟踪 load_tasks:

最后 找到了 task environment

   def initialize_tasks
      require "rails/tasks"
      task :environment do
        $rails_rake_task = true
        require_environment!
      end
    end

全部文件 here

所以加载了 environment 也就可以 找到 model Class 了

总结:

1,rake 命名格式

rakefile, Rakefile, rakefile.rb, Rakefile.rb 都可以 ,常用 Rakefile

另外 rails 中使用的话,还可以 使用 .rake 后缀的文件

2,设置命名空间

namespace :test do
end

3,设置默认task

task :default => ["test"]

可以指定多个 task,执行 rake 后 ,默认调用的是 default task

4,指定task执行顺序

task :test => [:hello,:world]

5,查看rake 任务

rake -T  
rake --tasks
rake -T db: # 查看db打头的task

6,跟踪task

rake test --trace 

更多 options

wxianfeng@ubuntu:/usr/local/system/projects/entos/ent_os$ rake -h
rake [-f rakefile] {options} targets...

Options are ...
    -C, --classic-namespace          Put Task and FileTask in the top level namespace
    -D, --describe [PATTERN]         Describe the tasks (matching optional PATTERN), then exit.
    -n, --dry-run                    Do a dry run without executing actions.
    -e, --execute CODE               Execute some Ruby code and exit.
    -p, --execute-print CODE         Execute some Ruby code, print the result, then exit.
    -E, --execute-continue CODE      Execute some Ruby code, then continue with normal task processing.
    -I, --libdir LIBDIR              Include LIBDIR in the search path for required modules.
    -P, --prereqs                    Display the tasks and dependencies, then exit.
    -q, --quiet                      Do not log messages to standard output.
    -f, --rakefile [FILE]            Use FILE as the rakefile.
    -R, --rakelibdir RAKELIBDIR,     Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')
        --rakelib
    -r, --require MODULE             Require MODULE before executing rakefile.
        --rules                      Trace the rules resolution.
    -N, --no-search, --nosearch      Do not search parent directories for the Rakefile.
    -s, --silent                     Like --quiet, but also suppresses the 'in directory' announcement.
    -g, --system                     Using system wide (global) rakefiles (usually '~/.rake/*.rake').
    -G, --no-system, --nosystem      Use standard project Rakefile search paths, ignore system wide rakefiles.
    -T, --tasks [PATTERN]            Display the tasks (matching optional PATTERN) with descriptions, then exit.
    -t, --trace                      Turn on invoke/execute tracing, enable full backtrace.
    -v, --verbose                    Log message to standard output.
    -V, --version                    Display the program version.
    -h, -H, --help                   Display this help message.

DEMO:

wxianfeng@ubuntu:~/test/rake$ cat Rakefile 
desc "Default: test"
task :default => ["r:test"]

namespace :r do 
desc "test" 
task :test  do
        puts "test"
end

desc "test1"
task :test1 => :test do
        puts "test1" 
end

desc "test3"
task :test3 => :test4 do
        puts "test3" 
end 
end

SEE:

http://hi.baidu.com/%D0%C7203/blog/item/ebda2dd09f1d698ea1ec9c7a.html http://guides.rubyonrails.org/command_line.html http://ericzouthoughts.wordpress.com/2009/06/20/execute-shell-command-in-ruby-rake-task/


Javascript prototype 属性

Posted by wxianfeng Thu, 20 Jan 2011 20:25:00 GMT

javascript 到底是一个什么样的语言,各人说法不一 ,javascript 之父 说 javascript 在模仿 Java , <<javascript高级语言设计>> 一书说 javascript 是面向过程,对象,函数 三种于一体的语言 , 大多数人说 javascript 是 基于原型的语言 ,甚至还有人 说javascript里一切面向对象,我还认为javascript 是动态语言了,因为javascript对象可以动态的添加属性 ,方法. 每个人的说法都有自己的道理,不管怎么说,,理解 javascript 对象prototype 属性至关重要.

javascript 实现类需要借助Function ,基本的 类 继承 多态 等等特性的都没有原生语法支持 , javascript 是基于 prototype-base 的语言,即基于原型的语言,实现继承 == 都需要借助 prototype 来实现.

为什么说了那么多 ,因为最近 遇到 一个问题 , 所以 研究了 下 javascript prototype

问题简化如下;

Array.prototype.add = function(item) {
    this.push(item);
}  
var months = ["1","2"]
var str = ""
for( var i in months){
  str += months[i];
}
console.log(str);

你认为 上面这段代码的输出结果是什么?

刚开始我认为是 str = 12 ,可是结果是错的 ,如果把扩展的 Array add方法去掉就是 12 了 , 那么问题到底出在什么地方?

Array.prototype.add = function(item) {
    this.push(item);
}  

是给数组对象添加了 add 属性, 只不过这个 add 属性对应一个函数(方法), 而for .. in .. 循环数组的话,会把属性也给循环出来 。。。。所以 最后结果是:

12function (item) { this.push(item); }

正确的写法是:

Array.prototype.add = function(item) {
    this.push(item);
}  
var months = ["1","2"]
var str = ""
for( var i =0,i<months.length,++i){
  str += months[i];
}
console.log(str);

length 返回的是数组元素的长度,不包括属性.

其实 主要 还是prototype 问题

prototype到底是什么?

JavaScript 中的每个对象都有一个 prototype 属性,指向另外一个对象。 使用对象字面量创建的对象的 prototype 指向的是Object.prototype,如var obj = {"name" : "Alex"};中创建的对象obj的 prototype 指向的就是Object.prototype。 而使用 new 操作符创建的对象的 prototype 指向的是其构造器的 prototype 。如var users = new Array();中创建的对象users的 prototype 指向的是Array.prototype。 一个对象 A 的 prototype 指向的是另外一个对象 B,而对象 B 自己的 prototype 又指向另外一个对象 C,这样就形成了一个链条,称为 prototype 链。这个链条会不断继续,一直到Object.prototype。Object.prototype对象的 prototype 值为 null,从而使得该原型链终止.

上面这句话 是从 IBM 那 copy 来的 , 解释的 很清楚...

constructor属性得到什么?

constructor 属性得到 该对象是通过 哪个构造函数 构造出来的,即new

DEMO:

从上面 可以看出 Student 的原型对象是 Person , 所以 s 具有了 sex属性 , 其实这个也是 javascript中继承的 实现方法 , Person的原型对象是 Object , Object的原型对象是 null 这里原型连终止

示意图:

当然 关于 prototype 的用法不仅仅这些 。。例如 javascript 框架 prototype.js 就是基于 javascript prototype 实现的,里面 提供了 很多 Array , String 对象的很好用的 工具方法.... 更多javascript prototype的学习, 请查阅其他资料.

SEE:

http://www.ibm.com/developerworks/cn/web/wa-lo-dojoajax1/ http://www.darkmi.com/blog/archives/981 http://www.blabla.cn/jsexamples/042jsforin.html


ruby Object#tap and Object#returning

Posted by wxianfeng Wed, 19 Jan 2011 03:29:00 GMT

今天在rails中使用 returning 的时候 log 打出warning:

Object#returning has been deprecated in favor of Object#tap

环境是 ruby 1.9.2 + rails 3.0.3

从warn上看是returning不建议使用,建议使用tap方法,那么tap方法和returning方法有什么不同

Object#tap 方法是 ruby1.8.7 以后加入的,Object#returning 方法是 rails添加的

rails 3.0.3 returning 源码:here

rails 2.3.5 returning源码:

class Object
  # Returns +value+ after yielding +value+ to the block. This simplifies the
  # process of constructing an object, performing work on the object, and then
  # returning the object from a method. It is a Ruby-ized realization of the K
  # combinator, courtesy of Mikael Brockman.
  #
  # ==== Examples
  #
  #  # Without returning
  #  def foo
  #    values = []
  #    values << "bar"
  #    values << "baz"
  #    return values
  #  end
  #
  #  foo # => ['bar', 'baz']
  #
  #  # returning with a local variable
  #  def foo
  #    returning values = [] do
  #      values << 'bar'
  #      values << 'baz'
  #    end
  #  end
  #
  #  foo # => ['bar', 'baz']
  #  
  #  # returning with a block argument
  #  def foo
  #    returning [] do |values|
  #      values << 'bar'
  #      values << 'baz'
  #    end
  #  end
  #  
  #  foo # => ['bar', 'baz']
  def returning(value)
    yield(value)
    value
  end

  # Yields <code>x</code> to the block, and then returns <code>x</code>.
  # The primary purpose of this method is to "tap into" a method chain,
  # in order to perform operations on intermediate results within the chain.
  #
  #   (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
  #     tap    { |x| puts "array: #{x.inspect}" }.
  #     select { |x| x%2 == 0 }.
  #     tap    { |x| puts "evens: #{x.inspect}" }.
  #     map    { |x| x*x }.
  #     tap    { |x| puts "squares: #{x.inspect}" }
  def tap
    yield self
    self
  end unless Object.respond_to?(:tap)

  # An elegant way to factor duplication out of options passed to a series of
  # method calls. Each method called in the block, with the block variable as
  # the receiver, will have its options merged with the default +options+ hash
  # provided. Each method called on the block variable must take an options
  # hash as its final argument.
  # 
  #   with_options :order => 'created_at', :class_name => 'Comment' do |post|
  #     post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all
  #     post.has_many :unapproved_comments, :conditions => ['approved = ?', false]
  #     post.has_many :all_comments
  #   end
  #
  # Can also be used with an explicit receiver:
  #
  #   map.with_options :controller => "people" do |people|
  #     people.connect "/people",     :action => "index"
  #     people.connect "/people/:id", :action => "show"
  #   end
  #
  def with_options(options)
    yield ActiveSupport::OptionMerger.new(self, options)
  end
  
  # A duck-type assistant method. For example, Active Support extends Date
  # to define an acts_like_date? method, and extends Time to define
  # acts_like_time?. As a result, we can do "x.acts_like?(:time)" and
  # "x.acts_like?(:date)" to do duck-type-safe comparisons, since classes that
  # we want to act like Time simply need to define an acts_like_time? method.
  def acts_like?(duck)
    respond_to? "acts_like_#{duck}?"
  end

end

可以看到 tap 方法也封装了,为了防止 ruby版本过低 没有tap方法就 添加Object#tap 方法,tap 和 returning本质是一样的,函数体都是调用block闭包,只不过returning需要传递一个参数给闭包,最后返回的就是这个参数,而 tap直接操作self指针,最后返回的也就是self

另外最新rails源码(>rails3.0.3)已经没有 returning方法了,所以以后最好都用tap方法

DEMO1:

require "rubygems"
require "active_support"
# Object#tap 是>ruby1.8.7 有的
# Object#returning 是Rails 封装的方法, rails3.X 已经不建议使用

# Object#tap 可以支持链式(chain)操作
(1..10).tap {
  |x| puts "original: #{x.inspect}"
}.to_a.tap {
  |x| puts "array: #{x.inspect}"
}.select {|x| x%2==0}.tap {
  |x| puts "evens: #{x.inspect}"
}.map {|x| x*x}.tap {
  |x| puts "squares: #{x.inspect}"
}

def object_tap
  {}.tap do |h| # => Hash.new.tap
    h[:a] = 1
    h[:b] =2
  end
end

p object_tap # {:b=>2, :a=>1}

def object_returning
  returning Hash.new do |h| # 注意这里不能用 {}  , 放在 returning 方法后面 当作成 block闭包了
    h[:a] = 1
    h[:b] = 2
  end  
end

p object_returning # {:b=>2, :a=>1}

DEMO2:

require "rubygems"
require "active_support"

class Hash  
  def shift_value_tap_self
    self.tap do |h|
      h.each { |k,v| v.shift if v.is_a?(Array) }
    end
  end
  def shift_value_tap
    {}.tap do |h|
      self.each { |k,v|  v.is_a?(Array) ? h[k] = v.shift : h[k] = v } 
    end
  end

  def shift_value_returning
    returning Hash.new do |h|
      self.each { |k,v|  v.is_a?(Array) ? h[k] = v.shift : h[k] = v } 
    end
  end
  def shift_value_returning_self
    returning self do |h|
      h.each { |k,v| v.shift if v.is_a?(Array) }
    end
  end
end

hsh = {"a"=>[1,2,3],"b"=>["g","f","w"],"c"=>"fuck_china"}
hsh1 = {"a"=>[1,2,3],"b"=>["g","f","w"],"c"=>"fuck_china"}
hsh2 = {"a"=>[1,2,3],"b"=>["g","f","w"],"c"=>"fuck_china"}
hsh3 = {"a"=>[1,2,3],"b"=>["g","f","w"],"c"=>"fuck_china"}
p hsh.shift_value_tap # {"a"=>1, "b"=>"g", "c"=>"fuck_china"}
p hsh1.shift_value_returning # {"a"=>1, "b"=>"g", "c"=>"fuck_china"}
p hsh2.shift_value_tap_self # {"a"=>[2, 3], "b"=>["f", "w"], "c"=>"fuck_china"}
p hsh3.shift_value_returning_self # {"a"=>[2, 3], "b"=>["f", "w"], "c"=>"fuck_china"}

SEE:
http://blog.rubybestpractices.com/posts/gregory/011-tap-that-hash.html
http://www.simonecarletti.com/blog/2010/09/rails-3-beware-the-tap-pattern/
http://fuliang.javaeye.com/blog/857163


Rails try method

Posted by wxianfeng Thu, 13 Jan 2011 19:20:00 GMT

环境:ruby 1.9.2 + rails 3.0.3

我们经常会有这样的操作:

user = User.find_by_login("wxianfeng")  # =>  nil 
user.name # => NoMethodError: undefined method `name' for nil:NilClass

假如 login 为 wxianfeng 不存在 ,会报错:

NoMethodError: undefined method `name' for nil:NilClass

那么建议使用 try 方法避免报错,try 返回的是 nil

user.try(:name) # =>nil 

也就相当于

nil.try(:name) # => nil

看下源码: here

其实就是调用了 __send__ 方法 , __send__ 方法 和 send 方法等价 , 只不过 __send__ 方法 为了防止 有已经存在的 send 方法 , nil 的话 调用 NilClass 的 try 方法

另外 发现 github上 try方法已经重新写了 ,如下: here

class Object
  # Invokes the method identified by the symbol +method+, passing it any arguments
  # and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does.
  #
  # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
  # and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass.
  #
  # If try is called without a method to call, it will yield any given block with the object.
  #
  # ==== Examples
  #
  # Without try
  # @person && @person.name
  # or
  # @person ? @person.name : nil
  #
  # With try
  # @person.try(:name)
  #
  # +try+ also accepts arguments and/or a block, for the method it is trying
  # Person.try(:find, 1)
  # @people.try(:collect) {|p| p.name}
  #
  # Without a method argument try will yield to the block unless the reciever is nil.
  # @person.try { |p| "#{p.first_name} #{p.last_name}" }
  #--
  # +try+ behaves like +Object#send+, unless called on +NilClass+.
  def try(*a, &b)
    if a.empty? && block_given?
      yield self
    else
      __send__(*a, &b)
    end
  end
end

class NilClass #:nodoc:
  def try(*args)
    nil
  end
end

其实只是判断了 if a.empty? && block_given? 这种情况 则直接执行block 内容然后返回,效果一样…..

DEMO:

require "active_support/core_ext/object/try"

class Klass

  def send(*args)
    "helo " + args.join(' ')
  end

  def hello(*args)
    "Hello " + args.join(' ')
  end

  def self.foobar(s)
     "#{s} foobar"
  end
end

k = Klass.new

# __send__ 为了防止有方法名叫send , 建议用 __send__
p k.__send__ :hello, "gentle", "readers"   #=> "Hello gentle readers" 
p k.send "gentle", "readers"   #=> "Helo gentle readers"

# Ruby 里一切皆是对象,类也是对象
# Klass(类) 是 Class 的实例 , Class 是 Object 的实例 , 那么 Klass 也就是 Object 的实例 所以 Klass 可以调用try 方法
p Klass.try(:foobar,"hey") # => "hey foobar"
# k 是Klass 的实例,Klass 的父类是 Object , 所以 k 可以调用 try 方法
p k.try(:send,"bla","bla") # => "helo bla bla"

# class 得到的是 实例关系
# superclass 得到的是 继承关系
p Klass.superclass # Object
p Klass.class # Class
p k.class # Klass

另外 这是 对象nil 那如果 没有那个字段了 , 就会 报 找不到方法的错误

例如:

ruby-1.9.2-p0 > u=User.first
  User Load (175.8ms)  SELECT `users`.* FROM `users` LIMIT 1
 => #<User id: 1, login: "entos", name: "", email: "entos@entos.com", crypted_password: "557c88b0713f63397249f4198368e4a57d6d400f", salt: "4e04ef1cf506595ac3edf6a249791c55995b0f8f", remember_token: nil, remember_token_expires_at: nil, activation_code: nil, activated_at: nil, status: 2, suspend_at: nil, avatar_id: nil, orgunit_id: nil, mobile_phone: nil, last_login_at: nil, language: nil, options: nil, created_at: "2011-02-24 02:55:42", updated_at: "2011-02-24 02:55:42"> 
ruby-1.9.2-p0 > u.hi
NoMethodError: undefined method `hi' for #<User:0x9fcfe00>

建议加上 respond_to? 判断

ruby-1.9.2-p0 > u.respond_to? "hi"
 => false

rails 控制台输出sql

Posted by wxianfeng Thu, 13 Jan 2011 14:14:00 GMT

环境:ruby 1.9.2 + rails 3.0.3

我们经常需要在 rails console 中进行Model的操作,想看执行的sql ,必须到 rails log 中去查看 , 现在 有一个更好的办法,直接输出到 console 中…

在console 运行下面这句话即可:

ActiveRecord::Base.logger = Logger.new(STDOUT)

或者 直接写到 config/appliction.rb 中 ,下次启动console的时候 不需要在写上面语句:

    if Rails.env == 'development'
      ActiveRecord::Base.logger = Logger.new(STDOUT)
    end

DEMO:

wxianfeng@ubuntu:/usr/local/system/projects/entos/ent_os$ rails  c 
Loading development environment (Rails 3.0.3)
ruby-1.9.2-p0 > ActiveRecord::Base.logger = Logger.new(STDOUT)
 => #<Logger:0xadc0730 @progname=nil, @level=0, @default_formatter=#<Logger::Formatter:0xadc071c @datetime_format=nil>, @formatter=nil, @logdev=#<Logger::LogDevice:0xadc06a4 @shift_size=nil, @shift_age=nil, @filename=nil, @dev=#<IO:<STDOUT>>, @mutex=#<Logger::LogDevice::LogDeviceMutex:0xadc0690 @mon_owner=nil, @mon_count=0, @mon_mutex=#<Mutex:0xadc0668>>>> 
ruby-1.9.2-p0 > User.last 
  User Load (0.2ms)  SELECT `users`.* FROM `users` ORDER BY users.id DESC LIMIT 1
 => #<User id: 15, login: "xxxxxx", name: "", email: "xx@zz.com", crypted_password: "471f98733c6d2456df58a354feddcf7af22ea78e", salt: "f03c284f91365a3eeb30a2898b79524694efdac5", remember_token: nil, remember_token_expires_at: nil, activation_code: nil, activated_at: "2011-01-07 08:00:25", status: 2, suspend_at: nil, avatar_id: nil, orgunit_id: nil, mobile_phone: nil, last_login_at: nil, language: nil, options: nil, created_at: "2011-01-07 08:00:17", updated_at: "2011-01-07 08:00:25">

另外 还可以 使用 hirb gem 来让输出格式以表格排列,个人不是太喜欢,原有的方式可以看出数据的返回格式,是集合数组 , 还是单个对象 一清二楚 。。。而hirb 就没有了

SEE:

http://tuohuang.thoughtworkers.org/?p=114


ruby instance_variables

Posted by wxianfeng Mon, 10 Jan 2011 16:52:00 GMT

环境:ruby 1.9.2 + ubuntu 10.10

instance_variables 得到当前 对象已经开辟内存空间的实例变量,疑惑在这里

class Foo

  attr_accessor :sex,:birthday  

end

p Foo.new.instance_variables # => []

刚开始不理解,怎么是 空……..

后来想了想,因为 ruby 是 动态的解释型的语言,如果没有 给实例变量赋值 的话,是不会开辟内存空间的,所以 instance_variables 只能得到已经开辟内存空间的 实例变量,
但是如果是 编译型的静态语言 则不然,例如java ,实例变量 声明了 就会开辟内存空间了

DEMO1:

# ruby version : 1.9.2

class Foo

  attr_accessor :sex,:birthday # attr_accessor ruby里封装的method  

end

p Foo.new.instance_variables # => []

f = Foo.new
p f.sex #=> nil
f.sex = 'M' 
p f.instance_variables #=>  [:@sex]
p f.inspect # => "#<Foo:0x85ead8c @sex=\"M\">"

b = Foo.new
b.birthday = nil # 注意赋值为nil,也开辟了内存空间
p b.instance_variables # => [:@birthday]
p b.inspect # => "#<Foo:0x9dca358 @birthday=nil>"

DEMO2:

class Foo
  
  def initialize
    @name = 'wxianfeng'
  end

  def bar # => as getter method
    @name
  end

end

f = Foo.new
p f.instance_variables #=> [:@name]
p f.inspect #=> "#<Foo:0x8aa7c08 @name=\"wxianfeng\">"
p f.name # =>  undefined method `name' for #<Foo:0x99adf7c @name="wxianfeng">
p f.bar # => "wxianfeng"

ruby 源码:

    # obj.instance_variables    => array
    #
    #
    # Returns an array of instance variable names for the receiver. Note
    # that simply defining an accessor does not create the corresponding
    # instance variable.
    #
    #    class Fred
    #      attr_accessor :a1
    #      def initialize
    #        @iv = 3
    #      end
    #    end
    #    Fred.new.instance_variables   #=> ["@iv"]
    #
    #
    def instance_variables
      # This is just a stub for a builtin Ruby method.
      # See the top of this file for more info.
    end

SEE:
http://www.megasolutions.net/ruby/instance_variables-doesnt-return-unassigned-variables-68358.aspx