`
fantaxy025025
  • 浏览: 1247461 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类

Rails源码阅读(一)_script/console

 
阅读更多
Rails源码阅读_script/console启动
 
rails中常用的命令script/console,一般来启动rails环境,在这里测试些脚本和函数等。
问题:
#1 启动到底做了什么,如何做到的
#2 console怎么用,能做什么,不能做什么
 
我其实最关心的是,这个命令如何启动了rails环境,这样我们就可以使用rails的很多组件了,例如active_record。如果不熟悉这个的话,我们就还是生活在“黑盒”之中,深感憋屈。
 
详细分析
script/console.rb脚本内容:
#!/usr/bin/env ruby
require File.expand_path('../../config/boot',  __FILE__)
require 'commands/console'
 就两行代码,用了expand_path~
 
script/boot
 
irb的使用
irb是ruby的交互运行控制台,方法是在命令行窗口输入irb。类似python中输入python后使用。
irb最常用的是不带任何参数的默认使用。但是还有另外还有很多的可选项。
 
$ irb -h
Usage:  irb.rb [options] [programfile] [arguments]
  -f    Suppress read of ~/.irbrc 
  -m    Bc mode (load mathn, fraction or matrix are available)
  -d                Set $DEBUG to true (same as `ruby -d')
  -r load-module    Same as `ruby -r'
  -I path           Specify $LOAD_PATH directory
  --inspect    Use `inspect' for output (default except for bc mode)
  --noinspect    Don't use inspect for output
  --readline    Use Readline extension module
  --noreadline    Don't use Readline extension module
  --prompt prompt-mode
  --prompt-mode prompt-mode
   Switch prompt mode. Pre-defined prompt modes are
   `default', `simple', `xmp' and `inf-ruby'
  --inf-ruby-mode   Use prompt appropriate for inf-ruby-mode on emacs. 
   Suppresses --readline. 
  --simple-prompt   Simple prompt mode
  --noprompt    No prompt mode
  --tracer    Display trace for each execution of commands.
  --back-trace-limit n
   Display backtrace top n and tail n. The default
   value is 16. 
  --irb_debug n    Set internal debug level to n (not for popular use)
  -v, --version    Print the version of irb
 
这里重点关注几个:
-r xxx
加载一个文件,类似require ‘xxx’
-I
指定LOAD_PATH
--inspect
交互接口默认输出调用obj.inspect的方法,也就是说你不需要调用inspect方法了
 
commands/console的源码
算是比较短的,简单的就添加了注释
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb' #看平台

require 'optparse'

options = { :sandbox => false, :irb => irb }
OptionParser.new do |opt|
  opt.banner = "Usage: console [environment] [options]"
  opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
  opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v }
  opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v }
  opt.parse!(ARGV)
end

#(1)这里是重点!!
libs =  " -r irb/completion"
libs << %( -r "#{RAILS_ROOT}/config/environment")
libs << " -r console_app"
libs << " -r console_sandbox" if options[:sandbox]
libs << " -r console_with_helpers"

#这里是加载ruby-debug
if options[:debugger]
  begin
    require 'ruby-debug'
    libs << " -r ruby-debug"
    puts "=> Debugger enabled"
  rescue Exception
    puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'"
    exit
  end
end

#这里是切换console的环境(可以简写)
ENV['RAILS_ENV'] = case ARGV.first
  when "p"; "production"
  when "d"; "development"
  when "t"; "test"
  else
    ARGV.first || ENV['RAILS_ENV'] || 'development'
end

#这里是输出提示
if options[:sandbox]
  puts "Loading #{ENV['RAILS_ENV']} environment in sandbox (Rails #{Rails.version})"
  puts "Any modifications you make will be rolled back on exit"
else
  puts "Loading #{ENV['RAILS_ENV']} environment (Rails #{Rails.version})"
end

#(2)这里是重点!!
exec "#{options[:irb]} #{libs} --simple-prompt"
 
整个代码,最重要的地方就2处:
#(1)处是拼接irb使用的字符串libs,libs这个名字虽然是正确的,但感觉不贴切。。。
#(2)处是执行这个字符串
最终,如果什么参数也不是用的话,libs字符串是这样的:
稍微改写一点点代码:
puts "="*50
puts libs
puts "="*50
执行并输出:
lijg@lijg-desktop:~/workruby/practice-2.3.5$ ruby script/console 
Loading development environment (Rails 2.3.5)
==================================================
 -r irb/completion -r "/home/lijg/workruby/practice-2.3.5/config/environment" -r console_app -r console_with_helpers
================================================== 
可以看出:
加载了几个文件:
irb/completion,代码提示不全的
config/environment,这个最重要,加载了rails的环境,下面讨论
console_app,下面讨论
console_with_helpers,下面讨论
 
config/environment文件的作用:
代码:
# Be sure to restart your server when you modify this file

# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = '2.3.5' unless defined? RAILS_GEM_VERSION

# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')

Rails::Initializer.run do |config|
  
end
可以看出,加载这个文件,即会执行Rails::Initializer.run方法。
Rails::Initializer.run的执行详细,请看:Rails源码阅读(三)Rails::Initializer
这个方法主要作用是:加载rails环境。
 
console_app的作用:
提供了几个可以直接使用的方法:
#1 reload!方法
这个最常用,当修改了env配置的时候,需要重新加载。使用这个方法可以不必退出后在启动。
#2 app
app是个方法(我一直以为是个变量呢,ruby这个算好处么?隐藏起来了)
这个方法,还有个参数,用来重新new一个session
>> app.class
=> ActionController::Integration::Session
#3 new_session
根据代码可知,这个跟app是一样的,只不过是重新new一个新实例。
new_session接受block,可以对session进行一些配置(具体配置后面分析)。
上面的app只对配置了session的host。
 
代码:
require 'active_support/test_case'
require 'action_controller'

# work around the at_exit hook in test/unit, which kills IRB
Test::Unit.run = true if Test::Unit.respond_to?(:run=)

# reference the global "app" instance, created on demand. To recreate the
# instance, pass a non-false value as the parameter.
def app(create=false)
  @app_integration_instance = nil if create
  @app_integration_instance ||= new_session do |sess|
    sess.host! "www.example.com"
  end
end

# create a new session. If a block is given, the new session will be yielded
# to the block before being returned.
def new_session
  session = ActionController::Integration::Session.new
  yield session if block_given?
  session
end

#reloads the environment
def reload!
  puts "Reloading..."
  Dispatcher.cleanup_application
  Dispatcher.reload_application
  true
end
 
console_with_helpers
代码很短:
def helper
  @helper ||= ApplicationController.helpers
end

@controller = ApplicationController.new
 
作用:
#1 helper
也是个方法,定义并返回@helper,返回@helper ||= ApplicationController.helpers
这个helper是ActionView::Base的实例,见下面的代码@helper_proxy = ActionView::Base.new
>>  helper.class
=> ActionView::Base
>> @helper.class
=> ActionView::Base
 
这个好处是,可以使用view中的方法等。例如,helper方法等
例如,想知道view的helper方法check_box_tag和check_box的区别,可以在这里试试看(再也不用老刷页面了)
>> helper.check_box_tag :gender, 'male'
=> "<input id=\"gender\" name=\"gender\" type=\"checkbox\" value=\"male\" />"
>>
>> helper.check_box :user, :gender
=> "
*      <input name=\"user[gender]\" type=\"hidden\" value=\"0\" />
*      <input id=\"user_gender\" name=\"user[gender]\" type=\"checkbox\" value=\"1\" />
*    "
 
ApplicationController.helpers的代码:
      # Provides a proxy to access helpers methods from outside the view.
      def helpers
        unless @helper_proxy
          @helper_proxy = ActionView::Base.new
          @helper_proxy.extend master_helper_module
        else
          @helper_proxy
        end
      end
 
helper使用_2
1)由上面的代码:@helper_proxy.extend master_helper_module 知道,在Controller中用helper,helper_method等生成的helper方法也会加入view中;
2)某个Controller对应的helper也会加入view中;
这样helper方法可以访问ApplicationHelper中的方法。
module ApplicationHelper
  def helper_method_ind_application_helper
    puts "June testing. This is #{__method__}"
  end
end
>> helper.helper_method_ind_application_helper
June testing. This is helper_method_ind_application_helper
=> nil 
3)一般情况下不许要。正常开发中ApplicationController中的helper :all 会被注释掉,否则就可以访问任何helper方法了(应该注掉的)
4)一般情况下不许要。如果要访问某个特定的Controller对应的helper方法,需要直接去include(应该是extend),这样的前提是helper方法是独立的,依赖的参数都传进来,而不是直接使用,例如parameters等。
例如:自己的helper模块
module UsersHelper

  def user_help_1(user_name)
    puts "I am June!"
  end

  def user_help_2
    puts "I am June-Lee!"
  end

end
>> helper.send :extend, UsersHelper
=> #<ActionView::Base:0xb6d04bdc @controller=nil, @_current_render=nil, @assigns_added=nil, @_first_render=nil, @assigns={}, @view_paths=[], @helpers=#<ActionView::Base::ProxyModule:0xb6d04b8c>>
>> helper.user_help_2
I am June-Lee!
=> nil
 
#2 @controller
定义了实例变量@controller可以在控制台使用
代码:
@controller = ApplicationController.new
?> @controller.class
=> ApplicationController
 
好处:可以直接使用ApplicationController的方法等。
例如,在ApplicationController加入一个方法:
  def action_a
    puts "This is action: #{__method__}"
  end
那么可以在控制台调用了:
?> @controller.action_a
This is action: action_a
 
console_sandbox
如果启动console加入参数'-s' 或者 '--sandbox',会再加载console_sandbox文件。
作用:Rollback database modifications on exit.
代码:
ActiveRecord::Base.connection.increment_open_transactions
ActiveRecord::Base.connection.begin_db_transaction
at_exit do
  ActiveRecord::Base.connection.rollback_db_transaction
  ActiveRecord::Base.connection.decrement_open_transactions
end
代码即文档!
 
 
==>>总结: 
#1 加载了rails的配置和组建
boot装载了rails的load path,console加载了rails的配置和组建,另外,为了方便,加载了几个重要的辅助文件:
这样:
就可以直接使用active_record, active_resource, active_support了
尤其是active_support带来了很多好用的方法,在irb里不能使用,console里可以使用了。
#2 console_app
提供了方法:
reload!,#reloads the environment
app,返回一个session,sess.host! "www.example.com"
new_session,返回一个session,host没有设置
#3 console_with_helpers
提供了方法:
helper,返回View实例@helper,可以使用helper方法
@controller,ApplicationController.new,用来使用ApplicationController的资源
 
如果想在console中测试路由(请看原文),有几种方法:
在rails3中,好像已经自动加入了这个方法,可以直接使用*1的方法了,不需要include xxx了
*1 include ActionController::UrlWriter
ruby-1.9.2-p136 :002> app.root_path
   =>"/"
*2 use ActionDispatch::Routing
include ActionDispatch::Routing
include Rails.application.routes.url_helpers

# use routes normally
users_path #=> "/users"
*3 r =Rails.application.routes
>> r.recognize_path "/station/index/42.html"=>{:controller=>"station",:action=>"index",:format=>"html",:id=>"42"}

and see what URL is generated for a given controller/action/parameters combination:

>> r.generate :controller =>:station,:action=>:index,:id=>42=>/station/index/42
 
+
+
+
||
+
+
+
 
 
 
 
 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics