capistrano 多机部署

Posted by wxianfeng Thu, 12 Apr 2012 15:39:00 GMT

核心使用 task 指令 实现多机部署

# encoding:utf-8
# >cap local deploy
# >cap remote deploy

set :application, "entos"
set :deploy_to, "/data/projects/entos"

set :scm, "git"
set :repository,  "git@114.255.155.167:entos.git"
set :branch, "master"
set :use_sudo, false
set :rails_env,"production"

task :remote do
  set :user, "entsea"
  set :deploy_via, :remote_cache
  set :copy_exclude, %w(external)
  server "114.255.155.166", :web, :app, :db, :primary => true
end

task :local do
  set :user, 'zzq'
  set :deploy_via, :remote_cache
  set :copy_exclude, %w(external)
  server '192.168.10.105', :web, :app, :db, :primary => true
end

namespace :deploy do
  task :start do; end
  task :stop do; end

  desc "Creating ln -s , example: database.yml"
  task :create_sync do
    run "ln -s #{shared_path}/config/database.yml #{current_path}/config/database.yml"
  end

  desc "Restarting unicorn"
  task :restart, :roles => :app, :except => { :no_release => true } do
    # run "/bin/sh restart_server.sh"
  end
end

after "deploy:symlink", "deploy:create_sync"

正则 ?: 非捕获组

Posted by wxianfeng Wed, 21 Mar 2012 15:03:00 GMT

今天一个同事 问我 正则 里 ?: 什么意思,记得 以前知道的 , 愣是 忘记了 ,查了下 是非捕获组的意思!

捕获组
()内的是分组,可以用 $1,$2…取值
demo:

ruby-1.9.2-p290 :022 >   "abcabc".match(/(abc)/)
 => #<MatchData "abc" 1:"abc"> 
ruby-1.9.2-p290 :023 > $1
 => "abc" 

非捕获组
(?:) 内的不当作分组
demo:

ruby-1.9.2-p290 :024 > "abcabc".match(/(?:abc)/)
 => #<MatchData "abc"> 
ruby-1.9.2-p290 :025 > $1
 => nil 

北京 798 Ruby/Rails 活动

Posted by wxianfeng Sat, 30 Jul 2011 06:36:00 GMT

时间: 2011-07-24
收获:
发现北京ROR的公司不是一般的多,签到单上看到N多公司,技术上没有太大收获,都是介绍性的,没有实战性的,内容主要涉及: mirah , Mongodb,Erlang,Grape

进程:http://www.surveymonkey.com/s/MSY2L7T

PS : 798 很好玩,很有艺术特色

现场:

093

798 入口

059

Ruby活动地方

054

Rails rumble 创始人

049

现场job board

067

现场

061

清一色老外,清一色Mac

056

介绍Mirah

045

798

083

798

043

798

MORE


shell 文件 插入行

Posted by wxianfeng Mon, 11 Jul 2011 10:44:00 GMT

一个rails2.x 的项目,需要迁移到rails3.x , ruby 1.9.2的编码问题,需要在rb文件头添加指定编码, 常见指定方式如下:

#coding:utf-8
#encoding:utf-8
# -*- coding: utf-8 -*-
# -*- encoding: utf-8 -*-

那么多rb文件总不能一个一个加吧,写个shell解决之!!!


ruby yaml 表示数组

Posted by wxianfeng Tue, 01 Mar 2011 13:24:00 GMT

yaml 的语法真是变态 , 表示个数组这么麻烦, 更复杂的数据结构 那不是更麻烦 !!!

yaml 文件:

# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html

one:
  name: MyString
  orgunit_id: 1
  inheritable: false
 # codes 是yaml数组表示方法
 # 缩进只能是两个空格为一级,不能是其他字符
  codes: 
    - 1
    - a
    - 2
    - b
    - 3
    - c

ruby 解析yaml:

ruby-1.9.2-p0 >   file =  "#{Rails.root}/test/fixtures/enumerations.yml"
 => "/usr/local/system/projects/entos/ent_os/test/fixtures/enumerations.yml" 
ruby-1.9.2-p0 > YAML.load File.read(file) 
 => {"one"=>{"name"=>"MyString", "orgunit_id"=>1, "inheritable"=>false, "codes"=>[1, "a", 2, "b", 3, "c"]}} 

不知道怎么写的可以 使用 to_yaml 方法 看一下:

irb(main):001:0> 
=> {"one"=>{"name"=>"MyString", "inheritable"=>false, "orgunit_id"=>1, "codes"=>[1, "a", 2, "b", 3, "c"]}}
irb(main):002:0> require "yaml"
=> true
irb(main):003:0> hsh.to_yaml
=> "--- \none: \n  name: MyString\n  inheritable: false\n  orgunit_id: 1\n  codes: \n  - 1\n  - a\n  - 2\n  - b\n  - 3\n  - c\n"

可读性 更好的 使用 y 方法

ruby-1.9.2-p0 > y hsh
--- 
one: 
  name: MyString
  orgunit_id: 1
  inheritable: false
  codes: 
  - 1
  - a
  - 2
  - b
  - 3
  - c
 => nil 

ruby rake tips

Posted by wxianfeng Wed, 26 Jan 2011 06: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/


ruby string to class

Posted by wxianfeng Thu, 17 Nov 2011 09:53:00 GMT

环境:ruby 1.9.2

有这样一个需求, 给你 任意一个字符串,把它转化为类,网上大多数的 解决办法是 下面三种:

Kernel.const_get(:User) # Object.const_get(:User)
eval(’User’)
‘User’.constantize

但是上面三种方法, 这个 User 事先必须是初始化的,不然会报错,如下:

Object.const_get(:User) # uninitialized constant User (NameError)

所以需要加个判断 这个 Class 有没有初始化,没有的话 再set 一个Class

require "rails/all"

def Kernel.const_missing(name)
  Object.const_set(name,Class.new)
end
#p Kernel.const_get("baoxiaos".to_sym) # wrong constant name baoxiaos (NameError)
p Kernel.const_get("baoxiaos".classify.to_sym) # Baoxiao , const的首字母必须大写
p Kernel.constants # []
p Object.constants.include?(:UBaoxiao) # true

刚开始把这个 const 定义在 Object 里:

require "rails/all"

def Object.const_missing(name)
  Object.const_set(name,Class.new)
end

#p Object.const_get("baoxiaos".to_sym) # wrong constant name baoxiaos (NameError)
p Object.const_get("baoxiaos".classify.to_sym)

发现 用在 rails 中后 , rails 处处报错,可能 怪我重写了 Object.const_missing 导致的 ……..

上面方法的原理是 当找不到 const的时候 执行了

  Object.const_set(name,Class.new)

那么 name 就变成是 Class 的实例了 , 为什么定义在 Kernel 中的 const_missing ,Object 也可以访问到,需要注意 Object,Kernel 之间的关系,Object 是 inluce Kernel 的,如下:

class Object
include Kernel
end

SEE:

http://niczsoft.com/2010/01/string-to-class-in-ruby-on-rails/
http://www.ruby-forum.com/topic/96222


Rails try method

Posted by wxianfeng Fri, 14 Jan 2011 08: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 Fri, 14 Jan 2011 03: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


Rails源码 attr_internal

Posted by wxianfeng Fri, 06 Jan 2012 07:34:00 GMT

环境:ruby 1.9.2 + rails 3.0.3 + ubuntu 10.10

params在rails中很常用,特别在表单提交的时候,params 产生的是一个Hash ,里面构造通过 form域的name构造 ,产生不同的 params 内容,今天 在看rails params 实现的时候 发现通过 attr_internal 的方法实现,params方法 的源码:

    def params
      @_params ||= request.parameters
    end

发现其实是从 request 这个方法得到的,那么request方法又是怎么定义的:

attr_internal :headers, :response, :request

就是 用了 attr_internal 方法

看下 整个 metal.rb文件: here

发现了 response,headers,session(借助delegate委派) ,status,params == 都是通过 attr_internal 实现的,来看看 attr_internal 到底是何须人也 :

源码: here

class Module
  # Declares an attribute reader backed by an internally-named instance variable.
  def attr_internal_reader(*attrs)
    attrs.each do |attr|
      module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end", __FILE__, __LINE__
    end
  end

  # Declares an attribute writer backed by an internally-named instance variable.
  def attr_internal_writer(*attrs)
    attrs.each do |attr|
      module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end", __FILE__, __LINE__
    end
  end

  # Declares an attribute reader and writer backed by an internally-named instance
  # variable.
  def attr_internal_accessor(*attrs)
    attr_internal_reader(*attrs)
    attr_internal_writer(*attrs)
  end

  alias_method :attr_internal, :attr_internal_accessor

  class << self; attr_accessor :attr_internal_naming_format end
  self.attr_internal_naming_format = '@_%s'

  private
    def attr_internal_ivar_name(attr)
      Module.attr_internal_naming_format % attr
    end
end

发现其实就是通过 module_eval 给 对象 添加了 settet , getter 方法而已,但是命名格式是这样的:

  self.attr_internal_naming_format = '@_%s' 

DEMO:

require "active_support/core_ext/module/attr_internal"

class Foo
  
  attr_accessor :sex,:birthday # attr_accessor ruby里封装的method
  attr_internal :name,:city # attr_internal rails 封装的

  def bar
    name # call getter method # => @_name
  end

end

f = Foo.new
f.name = 'wxianfeng'
p f.instance_variables # => [:@_name]
p f.name # => "wxianfeng"
p f # => #<Foo:0x8630e18 @_name="wxianfeng">
p f.bar # => "wxianfeng"

所以 attr_internal 和 attr_accessor 其实是 等价的,只不过 从字面意思上看是内部变量(闭包变量的写法) ,attr_internal 希望你 通过方法名来调用,不用 @_%s 这个写法 来调用


所以 其实 一般我们在 controller 用的 request 方法 其实 可以直接这样写 @_request ,

request #=> @_request
params # => @_request.parameters
params # => @_params
headers #=> @_headers
status #=> @_status
.
.
.

但是一般 不建议这样写

还发现 这些和 http相关的东西都定义在 metal 模块, metal 是 rails 链接 rack 的中间件,源码中的解释:

ActionController::Metal provides a way to get a valid Rack application from a controller.

Rack 是一个 ruby实现的web server,封装了 http的请求和响应等,例如 rails,sinatra == 都是在 rack 基础上实现的……

有机会很有必要 深入学习下…

SEE:

http://rubyonrailswin.wordpress.com/2007/03/07/actioncontroller-and-what-the-heck-is-attr_internal/
http://www.oschina.net/p/rack