首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络技术 > 网络基础 >

Rails源码翻阅(十)在console 使用ActionController:Integration:Session

2012-08-24 
Rails源码阅读(十)在console 使用ActionController::Integration::Session?Rails源码阅读(十)在console 使

Rails源码阅读(十)在console 使用ActionController::Integration::Session

?

Rails源码阅读(十)在console 使用ActionController::Integration::Session

?

ActionController::Integration::Session

在script/console的console_app中,使用的句柄是app,返回ActionController::Integration::Session的一个实例。

?

可以使用的方法1:模拟请求

app的public方法:

delete

delete_via_redirect

follow_redirect!

get

get_via_redirect

head

host!

https!

https?

new

post

post_via_redirect

put

put_via_redirect

redirect?

request_via_redirect

reset!

url_for

xhr

xml_http_request

这些都是模拟http请求的。

例如get方法,模拟get请求,path参数是请求的地址等。

?

    def get(path, parameters = nil, headers = nil)      process :get, path, parameters, headers    end

实验:

?> status = app.get '/login'

=> 200

?

process方法代码分析:

          #核心代码          app = Rack::Lint.new(@application)          status, headers, body = app.call(env)

从@application是ActionDispatcher的实例可以看出,启动了rack,来处理用户的get post等请求。

这段代码之前主要是rack的准备工作;

这段代码之后的代码在处理rack的返回值工作;

另外,为了配合集成测试,需要在处理请求后,可以做assert操作,这就需要能够得到请求的轨迹,也就是请求了哪个controller,哪个action,request参数,response值等,也就是:

?

          if @controller = ActionController::Base.last_instantiation            @request = @controller.request            @response = @controller.response            @controller.send(:set_test_assigns) #这里使用了module ProcessWithTest          else            # Decorate responses from Rack Middleware and Rails Metal            # as an Response for the purposes of integration testing            @response = Response.new            @response.status = status.to_s            @response.headers.replace(@headers)            @response.body = @body          end          # Decorate the response with the standard behavior of the          # TestResponse so that things like assert_response can be          # used in integration tests.          @response.extend(TestResponseBehavior) #response都加了这些功能

? ?请求的返回值是:return @status

?

可以使用的方法2: 单元测试

Included Modules包括:

Test::Unit::Assertions

ActionController::TestCase::Assertions

ActionController::TestProcess

这样,可以在app中使用上面3个测试模块的方法。

例如:

app.assert true

app.assert false

实验:

?> app.assert true

=> nil

>> app.assert false

Test::Unit::AssertionFailedError: <false> is not true.

from /opt/ruby/lib/ruby/1.8/test/unit/assertions.rb:48:in `assert_block'

from /opt/ruby/lib/ruby/1.8/test/unit/assertions.rb:500:in `_wrap_assertion'

from /opt/ruby/lib/ruby/1.8/test/unit/assertions.rb:46:in `assert_block'

from /opt/ruby/lib/ruby/1.8/test/unit/assertions.rb:63:in `assert'

from /opt/ruby/lib/ruby/1.8/test/unit/assertions.rb:495:in `_wrap_assertion'

from /opt/ruby/lib/ruby/1.8/test/unit/assertions.rb:61:in `assert'

from (irb):97

from :0

?

可以使用的方法3:命名路由

初始化的源代码:

?

      # Create and initialize a new Session instance.      def initialize(app = nil)        @application = app || ActionController::Dispatcher.new        reset!      end

?

这里的实例@application是ActionController::Dispatcher的实例,这个实例是rails的入口(详细看前面文)

reset!方法中除了做了些初始化的操作(例如把host设置为"www.example.com"),还包含了命名路由:

?

        unless defined? @named_routes_configured          # install the named routes in this session instance.          klass = class << self; self; end #取到单例类          Routing::Routes.install_helpers(klass) #装载了命名路由          # the helpers are made protected by default--we make them public for          # easier access during testing and troubleshooting.          klass.module_eval { public *Routing::Routes.named_routes.helpers } #!!!          @named_routes_configured = true?

这样,在console中可以使用命名路由了,当然可以测试。例如:

>> app.login_path

=> "/login"

>> app.login_url

=> "http://www.example.com/login"

注意:

klass = class << self; self; end #注意这句与self.class的区别

klass.module_eval { public *Routing::Routes.named_routes.helpers } #这句真不赖

直接把命名路由弄成了共有方法,详细:

Routing::Routes.named_routes.helpers返回一个Array,*星号的作用是展开作为参数,而public正好接收这些参数,真是个好东西

>> ActionController::Routing::Routes.named_routes.helpers.class

=> Array

?

可以使用的方法4:属性方法

[RW]accept The Accept header to send.

[RW]application Rack application to use

[R]body The body of the last request.

[R]controller A reference to the controller instance used by the last request.

[R]cookies A map of the cookies returned by the last response, and which will be sent with the next request.

[R]headers A map of the headers returned by the last response.

[RW]host The hostname used in the last request.

[R]path The URI of the last request.

[RW]remote_addr The remote_addr used in the last request.

[R]request A reference to the request instance used by the last request.

[RW]request_count A running counter of the number of requests processed.

[R]response A reference to the response instance used by the last request.

[R]status The integer HTTP status code of the last request.

[R]status_message The status message that accompanied the status code of the last request.

例如:

?> app.path

=> "/login"

>> app.remote_addr

=> "127.0.0.1"

如何使用好这些属性和其提供的方法,还要看对每一个对象的了解。

?

可以使用的方法5: ActionController::Base引入的方法

?

  [ControllerCapture, ActionController::ProcessWithTest].each do |mod|    unless ActionController::Base < mod      ActionController::Base.class_eval { include mod }    end  end
??其中ControllerCapture,主要作用注释写了已经:
    # A module used to extend ActionController::Base, so that integration tests    # can capture the controller used to satisfy a request.    module ControllerCapture #:nodoc:      def self.included(base)        base.extend(ClassMethods)        base.class_eval do          class << self            alias_method_chain :new, :capture #重写方法,见下面【1】处调用          end        end      end      module ClassMethods #:nodoc:        mattr_accessor :last_instantiation        def clear_last_instantiation!          self.last_instantiation = nil        end        def new_with_capture(*args)          controller = new_without_capture(*args) #这里调用了【1】          self.last_instantiation ||= controller #注意下这里!!!          controller        end      end    end
用处请看process方法内的一处:
          if @controller = ActionController::Base.last_instantiation            @request = @controller.request            @response = @controller.response            @controller.send(:set_test_assigns)          else            # Decorate responses from Rack Middleware and Rails Metal            # as an Response for the purposes of integration testing            @response = Response.new            @response.status = status.to_s            @response.headers.replace(@headers)            @response.body = @body          end
?ProcessWithTest如下,主要作用就是设置可访问的assigns:
  module ProcessWithTest #:nodoc:    def self.included(base)      base.class_eval { attr_reader :assigns }    end    def process_with_test(*args)      process(*args).tap { set_test_assigns } #这里用tap方法了    end    private      def set_test_assigns        @assigns = {}        (instance_variable_names - self.class.protected_instance_variables).each do |var|          name, value = var[1..-1], instance_variable_get(var)          @assigns[name] = value #主要作用就是设置可访问的assigns          response.template.assigns[name] = value if response        end      end  end

附:Tap方法:

?

Yields?x?to the block, and then returns?x. 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}" }original: 1..10array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]evens: [2, 4, 6, 8, 10]squares: [4, 16, 36, 64, 100]=> [4, 16, 36, 64, 100]


====完毕====++++



热点排行