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
作用请看:
Rails源码阅读(零)_config/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
+
+
+
||
+
+
+