Checkout of Instiki Trunk 1/21/2007.
This commit is contained in:
commit
69b62b6f33
1138 changed files with 139586 additions and 0 deletions
320
vendor/rails/actionpack/lib/action_controller/assertions.rb
vendored
Normal file
320
vendor/rails/actionpack/lib/action_controller/assertions.rb
vendored
Normal file
|
@ -0,0 +1,320 @@
|
|||
require 'test/unit'
|
||||
require 'test/unit/assertions'
|
||||
require 'rexml/document'
|
||||
require File.dirname(__FILE__) + "/vendor/html-scanner/html/document"
|
||||
|
||||
module Test #:nodoc:
|
||||
module Unit #:nodoc:
|
||||
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
|
||||
# can be used against. These collections are:
|
||||
#
|
||||
# * assigns: Instance variables assigned in the action that are available for the view.
|
||||
# * session: Objects being saved in the session.
|
||||
# * flash: The flash objects currently in the session.
|
||||
# * cookies: Cookies being sent to the user on this request.
|
||||
#
|
||||
# These collections can be used just like any other hash:
|
||||
#
|
||||
# assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
|
||||
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
|
||||
# assert flash.empty? # makes sure that there's nothing in the flash
|
||||
#
|
||||
# For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To
|
||||
# appease our yearning for symbols, though, an alternative accessor has been deviced using a method call instead of index referencing.
|
||||
# So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work.
|
||||
#
|
||||
# On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url.
|
||||
#
|
||||
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
|
||||
# action call which can then be asserted against.
|
||||
#
|
||||
# == Manipulating the request collections
|
||||
#
|
||||
# The collections described above link to the response, so you can test if what the actions were expected to do happened. But
|
||||
# sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions
|
||||
# and cookies, though. For sessions, you just do:
|
||||
#
|
||||
# @request.session[:key] = "value"
|
||||
#
|
||||
# For cookies, you need to manually create the cookie, like this:
|
||||
#
|
||||
# @request.cookies["key"] = CGI::Cookie.new("key", "value")
|
||||
#
|
||||
# == Testing named routes
|
||||
#
|
||||
# If you're using named routes, they can be easily tested using the original named routes methods straight in the test case.
|
||||
# Example:
|
||||
#
|
||||
# assert_redirected_to page_url(:title => 'foo')
|
||||
module Assertions
|
||||
# Asserts that the response is one of the following types:
|
||||
#
|
||||
# * <tt>:success</tt>: Status code was 200
|
||||
# * <tt>:redirect</tt>: Status code was in the 300-399 range
|
||||
# * <tt>:missing</tt>: Status code was 404
|
||||
# * <tt>:error</tt>: Status code was in the 500-599 range
|
||||
#
|
||||
# You can also pass an explicit status code number as the type, like assert_response(501)
|
||||
def assert_response(type, message = nil)
|
||||
clean_backtrace do
|
||||
if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
|
||||
assert_block("") { true } # to count the assertion
|
||||
elsif type.is_a?(Fixnum) && @response.response_code == type
|
||||
assert_block("") { true } # to count the assertion
|
||||
else
|
||||
assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial,
|
||||
# such that assert_redirected_to(:controller => "weblog") will also match the redirection of
|
||||
# redirect_to(:controller => "weblog", :action => "show") and so on.
|
||||
def assert_redirected_to(options = {}, message=nil)
|
||||
clean_backtrace do
|
||||
assert_response(:redirect, message)
|
||||
|
||||
if options.is_a?(String)
|
||||
msg = build_message(message, "expected a redirect to <?>, found one to <?>", options, @response.redirect_url)
|
||||
url_regexp = %r{^(\w+://.*?(/|$|\?))(.*)$}
|
||||
eurl, epath, url, path = [options, @response.redirect_url].collect do |url|
|
||||
u, p = (url_regexp =~ url) ? [$1, $3] : [nil, url]
|
||||
[u, (p[0..0] == '/') ? p : '/' + p]
|
||||
end.flatten
|
||||
|
||||
assert_equal(eurl, url, msg) if eurl && url
|
||||
assert_equal(epath, path, msg) if epath && path
|
||||
else
|
||||
@response_diff = options.diff(@response.redirected_to) if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
|
||||
msg = build_message(message, "response is not a redirection to all of the options supplied (redirection is <?>)#{', difference: <?>' if @response_diff}",
|
||||
@response.redirected_to || @response.redirect_url, @response_diff)
|
||||
|
||||
assert_block(msg) do
|
||||
if options.is_a?(Symbol)
|
||||
@response.redirected_to == options
|
||||
else
|
||||
options.keys.all? do |k|
|
||||
if k == :controller then options[k] == ActionController::Routing.controller_relative_to(@response.redirected_to[k], @controller.class.controller_path)
|
||||
else options[k] == (@response.redirected_to[k].respond_to?(:to_param) ? @response.redirected_to[k].to_param : @response.redirected_to[k] unless @response.redirected_to[k].nil?)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Asserts that the request was rendered with the appropriate template file.
|
||||
def assert_template(expected = nil, message=nil)
|
||||
clean_backtrace do
|
||||
rendered = expected ? @response.rendered_file(!expected.include?('/')) : @response.rendered_file
|
||||
msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered)
|
||||
assert_block(msg) do
|
||||
if expected.nil?
|
||||
!@response.rendered_with_file?
|
||||
else
|
||||
expected == rendered
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Asserts that the routing of the given path was handled correctly and that the parsed options match.
|
||||
def assert_recognizes(expected_options, path, extras={}, message=nil)
|
||||
clean_backtrace do
|
||||
path = "/#{path}" unless path[0..0] == '/'
|
||||
# Load routes.rb if it hasn't been loaded.
|
||||
ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
|
||||
|
||||
# Assume given controller
|
||||
request = ActionController::TestRequest.new({}, {}, nil)
|
||||
request.path = path
|
||||
ActionController::Routing::Routes.recognize!(request)
|
||||
|
||||
expected_options = expected_options.clone
|
||||
extras.each_key { |key| expected_options.delete key } unless extras.nil?
|
||||
|
||||
expected_options.stringify_keys!
|
||||
msg = build_message(message, "The recognized options <?> did not match <?>",
|
||||
request.path_parameters, expected_options)
|
||||
assert_block(msg) { request.path_parameters == expected_options }
|
||||
end
|
||||
end
|
||||
|
||||
# Asserts that the provided options can be used to generate the provided path.
|
||||
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
|
||||
clean_backtrace do
|
||||
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
|
||||
# Load routes.rb if it hasn't been loaded.
|
||||
ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
|
||||
|
||||
generated_path, extra_keys = ActionController::Routing::Routes.generate(options, extras)
|
||||
found_extras = options.reject {|k, v| ! extra_keys.include? k}
|
||||
|
||||
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
|
||||
assert_block(msg) { found_extras == extras }
|
||||
|
||||
msg = build_message(message, "The generated path <?> did not match <?>", generated_path,
|
||||
expected_path)
|
||||
assert_block(msg) { expected_path == generated_path }
|
||||
end
|
||||
end
|
||||
|
||||
# Asserts that path and options match both ways; in other words, the URL generated from
|
||||
# options is the same as path, and also that the options recognized from path are the same as options
|
||||
def assert_routing(path, options, defaults={}, extras={}, message=nil)
|
||||
assert_recognizes(options, path, extras, message)
|
||||
|
||||
controller, default_controller = options[:controller], defaults[:controller]
|
||||
if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
|
||||
options[:controller] = "/#{controller}"
|
||||
end
|
||||
|
||||
assert_generates(path, options, defaults, extras, message)
|
||||
end
|
||||
|
||||
# Asserts that there is a tag/node/element in the body of the response
|
||||
# that meets all of the given conditions. The +conditions+ parameter must
|
||||
# be a hash of any of the following keys (all are optional):
|
||||
#
|
||||
# * <tt>:tag</tt>: the node type must match the corresponding value
|
||||
# * <tt>:attributes</tt>: a hash. The node's attributes must match the
|
||||
# corresponding values in the hash.
|
||||
# * <tt>:parent</tt>: a hash. The node's parent must match the
|
||||
# corresponding hash.
|
||||
# * <tt>:child</tt>: a hash. At least one of the node's immediate children
|
||||
# must meet the criteria described by the hash.
|
||||
# * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
|
||||
# meet the criteria described by the hash.
|
||||
# * <tt>:descendant</tt>: a hash. At least one of the node's descendants
|
||||
# must meet the criteria described by the hash.
|
||||
# * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
|
||||
# meet the criteria described by the hash.
|
||||
# * <tt>:after</tt>: a hash. The node must be after any sibling meeting
|
||||
# the criteria described by the hash, and at least one sibling must match.
|
||||
# * <tt>:before</tt>: a hash. The node must be before any sibling meeting
|
||||
# the criteria described by the hash, and at least one sibling must match.
|
||||
# * <tt>:children</tt>: a hash, for counting children of a node. Accepts
|
||||
# the keys:
|
||||
# * <tt>:count</tt>: either a number or a range which must equal (or
|
||||
# include) the number of children that match.
|
||||
# * <tt>:less_than</tt>: the number of matching children must be less
|
||||
# than this number.
|
||||
# * <tt>:greater_than</tt>: the number of matching children must be
|
||||
# greater than this number.
|
||||
# * <tt>:only</tt>: another hash consisting of the keys to use
|
||||
# to match on the children, and only matching children will be
|
||||
# counted.
|
||||
# * <tt>:content</tt>: the textual content of the node must match the
|
||||
# given value. This will not match HTML tags in the body of a
|
||||
# tag--only text.
|
||||
#
|
||||
# Conditions are matched using the following algorithm:
|
||||
#
|
||||
# * if the condition is a string, it must be a substring of the value.
|
||||
# * if the condition is a regexp, it must match the value.
|
||||
# * if the condition is a number, the value must match number.to_s.
|
||||
# * if the condition is +true+, the value must not be +nil+.
|
||||
# * if the condition is +false+ or +nil+, the value must be +nil+.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # assert that there is a "span" tag
|
||||
# assert_tag :tag => "span"
|
||||
#
|
||||
# # assert that there is a "span" tag with id="x"
|
||||
# assert_tag :tag => "span", :attributes => { :id => "x" }
|
||||
#
|
||||
# # assert that there is a "span" tag using the short-hand
|
||||
# assert_tag :span
|
||||
#
|
||||
# # assert that there is a "span" tag with id="x" using the short-hand
|
||||
# assert_tag :span, :attributes => { :id => "x" }
|
||||
#
|
||||
# # assert that there is a "span" inside of a "div"
|
||||
# assert_tag :tag => "span", :parent => { :tag => "div" }
|
||||
#
|
||||
# # assert that there is a "span" somewhere inside a table
|
||||
# assert_tag :tag => "span", :ancestor => { :tag => "table" }
|
||||
#
|
||||
# # assert that there is a "span" with at least one "em" child
|
||||
# assert_tag :tag => "span", :child => { :tag => "em" }
|
||||
#
|
||||
# # assert that there is a "span" containing a (possibly nested)
|
||||
# # "strong" tag.
|
||||
# assert_tag :tag => "span", :descendant => { :tag => "strong" }
|
||||
#
|
||||
# # assert that there is a "span" containing between 2 and 4 "em" tags
|
||||
# # as immediate children
|
||||
# assert_tag :tag => "span",
|
||||
# :children => { :count => 2..4, :only => { :tag => "em" } }
|
||||
#
|
||||
# # get funky: assert that there is a "div", with an "ul" ancestor
|
||||
# # and an "li" parent (with "class" = "enum"), and containing a
|
||||
# # "span" descendant that contains text matching /hello world/
|
||||
# assert_tag :tag => "div",
|
||||
# :ancestor => { :tag => "ul" },
|
||||
# :parent => { :tag => "li",
|
||||
# :attributes => { :class => "enum" } },
|
||||
# :descendant => { :tag => "span",
|
||||
# :child => /hello world/ }
|
||||
#
|
||||
# <strong>Please note</strong: #assert_tag and #assert_no_tag only work
|
||||
# with well-formed XHTML. They recognize a few tags as implicitly self-closing
|
||||
# (like br and hr and such) but will not work correctly with tags
|
||||
# that allow optional closing tags (p, li, td). <em>You must explicitly
|
||||
# close all of your tags to use these assertions.</em>
|
||||
def assert_tag(*opts)
|
||||
clean_backtrace do
|
||||
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
|
||||
tag = find_tag(opts)
|
||||
assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
# Identical to #assert_tag, but asserts that a matching tag does _not_
|
||||
# exist. (See #assert_tag for a full discussion of the syntax.)
|
||||
def assert_no_tag(*opts)
|
||||
clean_backtrace do
|
||||
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
|
||||
tag = find_tag(opts)
|
||||
assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
# test 2 html strings to be equivalent, i.e. identical up to reordering of attributes
|
||||
def assert_dom_equal(expected, actual, message="")
|
||||
clean_backtrace do
|
||||
expected_dom = HTML::Document.new(expected).root
|
||||
actual_dom = HTML::Document.new(actual).root
|
||||
full_message = build_message(message, "<?> expected to be == to\n<?>.", expected_dom.to_s, actual_dom.to_s)
|
||||
assert_block(full_message) { expected_dom == actual_dom }
|
||||
end
|
||||
end
|
||||
|
||||
# negated form of +assert_dom_equivalent+
|
||||
def assert_dom_not_equal(expected, actual, message="")
|
||||
clean_backtrace do
|
||||
expected_dom = HTML::Document.new(expected).root
|
||||
actual_dom = HTML::Document.new(actual).root
|
||||
full_message = build_message(message, "<?> expected to be != to\n<?>.", expected_dom.to_s, actual_dom.to_s)
|
||||
assert_block(full_message) { expected_dom != actual_dom }
|
||||
end
|
||||
end
|
||||
|
||||
# ensures that the passed record is valid by active record standards. returns the error messages if not
|
||||
def assert_valid(record)
|
||||
clean_backtrace do
|
||||
assert record.valid?, record.errors.full_messages.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
def clean_backtrace(&block)
|
||||
yield
|
||||
rescue AssertionFailedError => e
|
||||
path = File.expand_path(__FILE__)
|
||||
raise AssertionFailedError, e.message, e.backtrace.reject { |line| File.expand_path(line) =~ /#{path}/ }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
1060
vendor/rails/actionpack/lib/action_controller/base.rb
vendored
Executable file
1060
vendor/rails/actionpack/lib/action_controller/base.rb
vendored
Executable file
File diff suppressed because it is too large
Load diff
92
vendor/rails/actionpack/lib/action_controller/benchmarking.rb
vendored
Normal file
92
vendor/rails/actionpack/lib/action_controller/benchmarking.rb
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
require 'benchmark'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
# The benchmarking module times the performance of actions and reports to the logger. If the Active Record
|
||||
# package has been included, a separate timing section for database calls will be added as well.
|
||||
module Benchmarking #:nodoc:
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
|
||||
base.class_eval do
|
||||
alias_method :perform_action_without_benchmark, :perform_action
|
||||
alias_method :perform_action, :perform_action_with_benchmark
|
||||
|
||||
alias_method :render_without_benchmark, :render
|
||||
alias_method :render, :render_with_benchmark
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it
|
||||
# (unless <tt>use_silence</tt> is set to false).
|
||||
#
|
||||
# The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it
|
||||
# easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
|
||||
# will only be conducted if the log level is low enough.
|
||||
def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
|
||||
if logger && logger.level == log_level
|
||||
result = nil
|
||||
seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
|
||||
logger.add(log_level, "#{title} (#{'%.5f' % seconds})")
|
||||
result
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
# Silences the logger for the duration of the block.
|
||||
def silence
|
||||
old_logger_level, logger.level = logger.level, Logger::ERROR if logger
|
||||
yield
|
||||
ensure
|
||||
logger.level = old_logger_level if logger
|
||||
end
|
||||
end
|
||||
|
||||
def render_with_benchmark(options = nil, deprecated_status = nil, &block)
|
||||
unless logger
|
||||
render_without_benchmark(options, deprecated_status, &block)
|
||||
else
|
||||
db_runtime = ActiveRecord::Base.connection.reset_runtime if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
|
||||
|
||||
render_output = nil
|
||||
@rendering_runtime = Benchmark::measure{ render_output = render_without_benchmark(options, deprecated_status, &block) }.real
|
||||
|
||||
if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
|
||||
@db_rt_before_render = db_runtime
|
||||
@db_rt_after_render = ActiveRecord::Base.connection.reset_runtime
|
||||
@rendering_runtime -= @db_rt_after_render
|
||||
end
|
||||
|
||||
render_output
|
||||
end
|
||||
end
|
||||
|
||||
def perform_action_with_benchmark
|
||||
unless logger
|
||||
perform_action_without_benchmark
|
||||
else
|
||||
runtime = [Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001].max
|
||||
log_message = "Completed in #{sprintf("%.5f", runtime)} (#{(1 / runtime).floor} reqs/sec)"
|
||||
log_message << rendering_runtime(runtime) if @rendering_runtime
|
||||
log_message << active_record_runtime(runtime) if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
|
||||
log_message << " | #{headers["Status"]}"
|
||||
log_message << " [#{complete_request_uri rescue "unknown"}]"
|
||||
logger.info(log_message)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def rendering_runtime(runtime)
|
||||
" | Rendering: #{sprintf("%.5f", @rendering_runtime)} (#{sprintf("%d", (@rendering_runtime * 100) / runtime)}%)"
|
||||
end
|
||||
|
||||
def active_record_runtime(runtime)
|
||||
db_runtime = ActiveRecord::Base.connection.reset_runtime
|
||||
db_runtime += @db_rt_before_render if @db_rt_before_render
|
||||
db_runtime += @db_rt_after_render if @db_rt_after_render
|
||||
db_percentage = (db_runtime * 100) / runtime
|
||||
" | DB: #{sprintf("%.5f", db_runtime)} (#{sprintf("%d", db_percentage)}%)"
|
||||
end
|
||||
end
|
||||
end
|
555
vendor/rails/actionpack/lib/action_controller/caching.rb
vendored
Normal file
555
vendor/rails/actionpack/lib/action_controller/caching.rb
vendored
Normal file
|
@ -0,0 +1,555 @@
|
|||
require 'fileutils'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
# Caching is a cheap way of speeding up slow applications by keeping the result of calculations, renderings, and database calls
|
||||
# around for subsequent requests. Action Controller affords you three approaches in varying levels of granularity: Page, Action, Fragment.
|
||||
#
|
||||
# You can read more about each approach and the sweeping assistance by clicking the modules below.
|
||||
#
|
||||
# Note: To turn off all caching and sweeping, set Base.perform_caching = false.
|
||||
module Caching
|
||||
def self.included(base) #:nodoc:
|
||||
base.send(:include, Pages, Actions, Fragments, Sweeping)
|
||||
|
||||
base.class_eval do
|
||||
@@perform_caching = true
|
||||
cattr_accessor :perform_caching
|
||||
end
|
||||
end
|
||||
|
||||
# Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server
|
||||
# can serve without going through the Action Pack. This can be as much as 100 times faster than going through the process of dynamically
|
||||
# generating the content. Unfortunately, this incredible speed-up is only available to stateless pages where all visitors
|
||||
# are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are a great fit
|
||||
# for this approach, but account-based systems where people log in and manipulate their own data are often less likely candidates.
|
||||
#
|
||||
# Specifying which actions to cache is done through the <tt>caches</tt> class method:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# caches_page :show, :new
|
||||
# end
|
||||
#
|
||||
# This will generate cache files such as weblog/show/5 and weblog/new, which match the URLs used to trigger the dynamic
|
||||
# generation. This is how the web server is able pick up a cache file when it exists and otherwise let the request pass on to
|
||||
# the Action Pack to generate it.
|
||||
#
|
||||
# Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
|
||||
# is not restored before another hit is made against it. The API for doing so mimics the options from url_for and friends:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# def update
|
||||
# List.update(params[:list][:id], params[:list])
|
||||
# expire_page :action => "show", :id => params[:list][:id]
|
||||
# redirect_to :action => "show", :id => params[:list][:id]
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
|
||||
# expired.
|
||||
#
|
||||
# == Setting the cache directory
|
||||
#
|
||||
# The cache directory should be the document root for the web server and is set using Base.page_cache_directory = "/document/root".
|
||||
# For Rails, this directory has already been set to RAILS_ROOT + "/public".
|
||||
#
|
||||
# == Setting the cache extension
|
||||
#
|
||||
# By default, the cache extension is .html, which makes it easy for the cached files to be picked up by the web server. If you want
|
||||
# something else, like .php or .shtml, just set Base.page_cache_extension.
|
||||
module Pages
|
||||
def self.included(base) #:nodoc:
|
||||
base.extend(ClassMethods)
|
||||
base.class_eval do
|
||||
@@page_cache_directory = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/public" : ""
|
||||
cattr_accessor :page_cache_directory
|
||||
|
||||
@@page_cache_extension = '.html'
|
||||
cattr_accessor :page_cache_extension
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Expires the page that was cached with the +path+ as a key. Example:
|
||||
# expire_page "/lists/show"
|
||||
def expire_page(path)
|
||||
return unless perform_caching
|
||||
|
||||
benchmark "Expired page: #{page_cache_file(path)}" do
|
||||
File.delete(page_cache_path(path)) if File.exists?(page_cache_path(path))
|
||||
end
|
||||
end
|
||||
|
||||
# Manually cache the +content+ in the key determined by +path+. Example:
|
||||
# cache_page "I'm the cached content", "/lists/show"
|
||||
def cache_page(content, path)
|
||||
return unless perform_caching
|
||||
|
||||
benchmark "Cached page: #{page_cache_file(path)}" do
|
||||
FileUtils.makedirs(File.dirname(page_cache_path(path)))
|
||||
File.open(page_cache_path(path), "wb+") { |f| f.write(content) }
|
||||
end
|
||||
end
|
||||
|
||||
# Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
|
||||
# matches the triggering url.
|
||||
def caches_page(*actions)
|
||||
return unless perform_caching
|
||||
actions.each do |action|
|
||||
class_eval "after_filter { |c| c.cache_page if c.action_name == '#{action}' }"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def page_cache_file(path)
|
||||
name = ((path.empty? || path == "/") ? "/index" : URI.unescape(path))
|
||||
name << page_cache_extension unless (name.split('/').last || name).include? '.'
|
||||
return name
|
||||
end
|
||||
|
||||
def page_cache_path(path)
|
||||
page_cache_directory + page_cache_file(path)
|
||||
end
|
||||
end
|
||||
|
||||
# Expires the page that was cached with the +options+ as a key. Example:
|
||||
# expire_page :controller => "lists", :action => "show"
|
||||
def expire_page(options = {})
|
||||
return unless perform_caching
|
||||
if options[:action].is_a?(Array)
|
||||
options[:action].dup.each do |action|
|
||||
self.class.expire_page(url_for(options.merge({ :only_path => true, :skip_relative_url_root => true, :action => action })))
|
||||
end
|
||||
else
|
||||
self.class.expire_page(url_for(options.merge({ :only_path => true, :skip_relative_url_root => true })))
|
||||
end
|
||||
end
|
||||
|
||||
# Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of @response.body is used
|
||||
# If no options are provided, the current +options+ for this action is used. Example:
|
||||
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
|
||||
def cache_page(content = nil, options = {})
|
||||
return unless perform_caching && caching_allowed
|
||||
self.class.cache_page(content || @response.body, url_for(options.merge({ :only_path => true, :skip_relative_url_root => true })))
|
||||
end
|
||||
|
||||
private
|
||||
def caching_allowed
|
||||
!@request.post? && @response.headers['Status'] && @response.headers['Status'].to_i < 400
|
||||
end
|
||||
end
|
||||
|
||||
# Action caching is similar to page caching by the fact that the entire output of the response is cached, but unlike page caching,
|
||||
# every request still goes through the Action Pack. The key benefit of this is that filters are run before the cache is served, which
|
||||
# allows for authentication and other restrictions on whether someone is allowed to see the cache. Example:
|
||||
#
|
||||
# class ListsController < ApplicationController
|
||||
# before_filter :authenticate, :except => :public
|
||||
# caches_page :public
|
||||
# caches_action :show, :feed
|
||||
# end
|
||||
#
|
||||
# In this example, the public action doesn't require authentication, so it's possible to use the faster page caching method. But both the
|
||||
# show and feed action are to be shielded behind the authenticate filter, so we need to implement those as action caches.
|
||||
#
|
||||
# Action caching internally uses the fragment caching and an around filter to do the job. The fragment cache is named according to both
|
||||
# the current host and the path. So a page that is accessed at http://david.somewhere.com/lists/show/1 will result in a fragment named
|
||||
# "david.somewhere.com/lists/show/1". This allows the cacher to differentiate between "david.somewhere.com/lists/" and
|
||||
# "jamis.somewhere.com/lists/" -- which is a helpful way of assisting the subdomain-as-account-key pattern.
|
||||
module Actions
|
||||
def self.append_features(base) #:nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
base.send(:attr_accessor, :rendered_action_cache)
|
||||
end
|
||||
|
||||
module ClassMethods #:nodoc:
|
||||
def caches_action(*actions)
|
||||
return unless perform_caching
|
||||
around_filter(ActionCacheFilter.new(*actions))
|
||||
end
|
||||
end
|
||||
|
||||
def expire_action(options = {})
|
||||
return unless perform_caching
|
||||
if options[:action].is_a?(Array)
|
||||
options[:action].dup.each do |action|
|
||||
expire_fragment(url_for(options.merge({ :action => action })).split("://").last)
|
||||
end
|
||||
else
|
||||
expire_fragment(url_for(options).split("://").last)
|
||||
end
|
||||
end
|
||||
|
||||
class ActionCacheFilter #:nodoc:
|
||||
def initialize(*actions)
|
||||
@actions = actions
|
||||
end
|
||||
|
||||
def before(controller)
|
||||
return unless @actions.include?(controller.action_name.intern)
|
||||
if cache = controller.read_fragment(controller.url_for.split("://").last)
|
||||
controller.rendered_action_cache = true
|
||||
controller.send(:render_text, cache)
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def after(controller)
|
||||
return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache
|
||||
controller.write_fragment(controller.url_for.split("://").last, controller.response.body)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
|
||||
# certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
|
||||
# parties. The caching is doing using the cache helper available in the Action View. A template with caching might look something like:
|
||||
#
|
||||
# <b>Hello <%= @name %></b>
|
||||
# <% cache do %>
|
||||
# All the topics in the system:
|
||||
# <%= render_collection_of_partials "topic", Topic.find_all %>
|
||||
# <% end %>
|
||||
#
|
||||
# This cache will bind to the name of action that called it. So you would be able to invalidate it using
|
||||
# <tt>expire_fragment(:controller => "topics", :action => "list")</tt> -- if that was the controller/action used. This is not too helpful
|
||||
# if you need to cache multiple fragments per action or if the action itself is cached using <tt>caches_action</tt>. So instead we should
|
||||
# qualify the name of the action used with something like:
|
||||
#
|
||||
# <% cache(:action => "list", :action_suffix => "all_topics") do %>
|
||||
#
|
||||
# That would result in a name such as "/topics/list/all_topics", which wouldn't conflict with any action cache and neither with another
|
||||
# fragment using a different suffix. Note that the URL doesn't have to really exist or be callable. We're just using the url_for system
|
||||
# to generate unique cache names that we can refer to later for expirations. The expiration call for this example would be
|
||||
# <tt>expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")</tt>.
|
||||
#
|
||||
# == Fragment stores
|
||||
#
|
||||
# In order to use the fragment caching, you need to designate where the caches should be stored. This is done by assigning a fragment store
|
||||
# of which there are four different kinds:
|
||||
#
|
||||
# * FileStore: Keeps the fragments on disk in the +cache_path+, which works well for all types of environments and shares the fragments for
|
||||
# all the web server processes running off the same application directory.
|
||||
# * MemoryStore: Keeps the fragments in memory, which is fine for WEBrick and for FCGI (if you don't care that each FCGI process holds its
|
||||
# own fragment store). It's not suitable for CGI as the process is thrown away at the end of each request. It can potentially also take
|
||||
# up a lot of memory since each process keeps all the caches in memory.
|
||||
# * DRbStore: Keeps the fragments in the memory of a separate, shared DRb process. This works for all environments and only keeps one cache
|
||||
# around for all processes, but requires that you run and manage a separate DRb process.
|
||||
# * MemCacheStore: Works like DRbStore, but uses Danga's MemCache instead.
|
||||
# Requires the ruby-memcache library: gem install ruby-memcache.
|
||||
#
|
||||
# Configuration examples (MemoryStore is the default):
|
||||
#
|
||||
# ActionController::Base.fragment_cache_store = :memory_store
|
||||
# ActionController::Base.fragment_cache_store = :file_store, "/path/to/cache/directory"
|
||||
# ActionController::Base.fragment_cache_store = :drb_store, "druby://localhost:9192"
|
||||
# ActionController::Base.fragment_cache_store = :mem_cache_store, "localhost"
|
||||
# ActionController::Base.fragment_cache_store = MyOwnStore.new("parameter")
|
||||
module Fragments
|
||||
def self.append_features(base) #:nodoc:
|
||||
super
|
||||
base.class_eval do
|
||||
@@fragment_cache_store = MemoryStore.new
|
||||
cattr_reader :fragment_cache_store
|
||||
|
||||
def self.fragment_cache_store=(store_option)
|
||||
store, *parameters = *([ store_option ].flatten)
|
||||
@@fragment_cache_store = if store.is_a?(Symbol)
|
||||
store_class_name = (store == :drb_store ? "DRbStore" : store.to_s.camelize)
|
||||
store_class = ActionController::Caching::Fragments.const_get(store_class_name)
|
||||
store_class.new(*parameters)
|
||||
else
|
||||
store
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fragment_cache_key(name)
|
||||
name.is_a?(Hash) ? url_for(name).split("://").last : name
|
||||
end
|
||||
|
||||
# Called by CacheHelper#cache
|
||||
def cache_erb_fragment(block, name = {}, options = nil)
|
||||
unless perform_caching then block.call; return end
|
||||
|
||||
buffer = eval("_erbout", block.binding)
|
||||
|
||||
if cache = read_fragment(name, options)
|
||||
buffer.concat(cache)
|
||||
else
|
||||
pos = buffer.length
|
||||
block.call
|
||||
write_fragment(name, buffer[pos..-1], options)
|
||||
end
|
||||
end
|
||||
|
||||
def write_fragment(name, content, options = nil)
|
||||
return unless perform_caching
|
||||
|
||||
key = fragment_cache_key(name)
|
||||
self.class.benchmark "Cached fragment: #{key}" do
|
||||
fragment_cache_store.write(key, content, options)
|
||||
end
|
||||
content
|
||||
end
|
||||
|
||||
def read_fragment(name, options = nil)
|
||||
return unless perform_caching
|
||||
|
||||
key = fragment_cache_key(name)
|
||||
self.class.benchmark "Fragment read: #{key}" do
|
||||
fragment_cache_store.read(key, options)
|
||||
end
|
||||
end
|
||||
|
||||
# Name can take one of three forms:
|
||||
# * String: This would normally take the form of a path like "pages/45/notes"
|
||||
# * Hash: Is treated as an implicit call to url_for, like { :controller => "pages", :action => "notes", :id => 45 }
|
||||
# * Regexp: Will destroy all the matched fragments, example: %r{pages/\d*/notes} Ensure you do not specify start and finish in the regex (^$) because the actual filename matched looks like ./cache/filename/path.cache
|
||||
def expire_fragment(name, options = nil)
|
||||
return unless perform_caching
|
||||
|
||||
key = fragment_cache_key(name)
|
||||
|
||||
if key.is_a?(Regexp)
|
||||
self.class.benchmark "Expired fragments matching: #{key.source}" do
|
||||
fragment_cache_store.delete_matched(key, options)
|
||||
end
|
||||
else
|
||||
self.class.benchmark "Expired fragment: #{key}" do
|
||||
fragment_cache_store.delete(key, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Deprecated -- just call expire_fragment with a regular expression
|
||||
def expire_matched_fragments(matcher = /.*/, options = nil) #:nodoc:
|
||||
expire_fragment(matcher, options)
|
||||
end
|
||||
|
||||
|
||||
class UnthreadedMemoryStore #:nodoc:
|
||||
def initialize #:nodoc:
|
||||
@data = {}
|
||||
end
|
||||
|
||||
def read(name, options=nil) #:nodoc:
|
||||
@data[name]
|
||||
end
|
||||
|
||||
def write(name, value, options=nil) #:nodoc:
|
||||
@data[name] = value
|
||||
end
|
||||
|
||||
def delete(name, options=nil) #:nodoc:
|
||||
@data.delete(name)
|
||||
end
|
||||
|
||||
def delete_matched(matcher, options=nil) #:nodoc:
|
||||
@data.delete_if { |k,v| k =~ matcher }
|
||||
end
|
||||
end
|
||||
|
||||
module ThreadSafety #:nodoc:
|
||||
def read(name, options=nil) #:nodoc:
|
||||
@mutex.synchronize { super }
|
||||
end
|
||||
|
||||
def write(name, value, options=nil) #:nodoc:
|
||||
@mutex.synchronize { super }
|
||||
end
|
||||
|
||||
def delete(name, options=nil) #:nodoc:
|
||||
@mutex.synchronize { super }
|
||||
end
|
||||
|
||||
def delete_matched(matcher, options=nil) #:nodoc:
|
||||
@mutex.synchronize { super }
|
||||
end
|
||||
end
|
||||
|
||||
class MemoryStore < UnthreadedMemoryStore #:nodoc:
|
||||
def initialize #:nodoc:
|
||||
super
|
||||
if ActionController::Base.allow_concurrency
|
||||
@mutex = Mutex.new
|
||||
MemoryStore.send(:include, ThreadSafety)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DRbStore < MemoryStore #:nodoc:
|
||||
attr_reader :address
|
||||
|
||||
def initialize(address = 'druby://localhost:9192')
|
||||
super()
|
||||
@address = address
|
||||
@data = DRbObject.new(nil, address)
|
||||
end
|
||||
end
|
||||
|
||||
class MemCacheStore < MemoryStore #:nodoc:
|
||||
attr_reader :addresses
|
||||
|
||||
def initialize(*addresses)
|
||||
super()
|
||||
addresses = addresses.flatten
|
||||
addresses = ["localhost"] if addresses.empty?
|
||||
@addresses = addresses
|
||||
@data = MemCache.new(*addresses)
|
||||
end
|
||||
end
|
||||
|
||||
class UnthreadedFileStore #:nodoc:
|
||||
attr_reader :cache_path
|
||||
|
||||
def initialize(cache_path)
|
||||
@cache_path = cache_path
|
||||
end
|
||||
|
||||
def write(name, value, options = nil) #:nodoc:
|
||||
ensure_cache_path(File.dirname(real_file_path(name)))
|
||||
File.open(real_file_path(name), "wb+") { |f| f.write(value) }
|
||||
rescue => e
|
||||
Base.logger.error "Couldn't create cache directory: #{name} (#{e.message})" if Base.logger
|
||||
end
|
||||
|
||||
def read(name, options = nil) #:nodoc:
|
||||
File.open(real_file_path(name), 'rb') { |f| f.read } rescue nil
|
||||
end
|
||||
|
||||
def delete(name, options) #:nodoc:
|
||||
File.delete(real_file_path(name))
|
||||
rescue SystemCallError => e
|
||||
# If there's no cache, then there's nothing to complain about
|
||||
end
|
||||
|
||||
def delete_matched(matcher, options) #:nodoc:
|
||||
search_dir(@cache_path) do |f|
|
||||
if f =~ matcher
|
||||
begin
|
||||
File.delete(f)
|
||||
rescue Object => e
|
||||
# If there's no cache, then there's nothing to complain about
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def real_file_path(name)
|
||||
'%s/%s.cache' % [@cache_path, name.gsub('?', '.').gsub(':', '.')]
|
||||
end
|
||||
|
||||
def ensure_cache_path(path)
|
||||
FileUtils.makedirs(path) unless File.exists?(path)
|
||||
end
|
||||
|
||||
def search_dir(dir, &callback)
|
||||
Dir.foreach(dir) do |d|
|
||||
next if d == "." || d == ".."
|
||||
name = File.join(dir, d)
|
||||
if File.directory?(name)
|
||||
search_dir(name, &callback)
|
||||
else
|
||||
callback.call name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class FileStore < UnthreadedFileStore #:nodoc:
|
||||
def initialize(cache_path)
|
||||
super(cache_path)
|
||||
if ActionController::Base.allow_concurrency
|
||||
@mutex = Mutex.new
|
||||
FileStore.send(:include, ThreadSafety)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
|
||||
# They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
|
||||
#
|
||||
# class ListSweeper < ActionController::Caching::Sweeper
|
||||
# observe List, Item
|
||||
#
|
||||
# def after_save(record)
|
||||
# list = record.is_a?(List) ? record : record.list
|
||||
# expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
|
||||
# expire_action(:controller => "lists", :action => "all")
|
||||
# list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
|
||||
#
|
||||
# class ListsController < ApplicationController
|
||||
# caches_action :index, :show, :public, :feed
|
||||
# cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
|
||||
# end
|
||||
#
|
||||
# In the example above, four actions are cached and three actions are responsible for expiring those caches.
|
||||
module Sweeping
|
||||
def self.append_features(base) #:nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
module ClassMethods #:nodoc:
|
||||
def cache_sweeper(*sweepers)
|
||||
return unless perform_caching
|
||||
configuration = sweepers.last.is_a?(Hash) ? sweepers.pop : {}
|
||||
sweepers.each do |sweeper|
|
||||
observer(sweeper)
|
||||
|
||||
sweeper_instance = Object.const_get(Inflector.classify(sweeper)).instance
|
||||
|
||||
if sweeper_instance.is_a?(Sweeper)
|
||||
around_filter(sweeper_instance, :only => configuration[:only])
|
||||
else
|
||||
after_filter(sweeper_instance, :only => configuration[:only])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
|
||||
class Sweeper < ActiveRecord::Observer #:nodoc:
|
||||
attr_accessor :controller
|
||||
|
||||
# ActiveRecord::Observer will mark this class as reloadable even though it should not be.
|
||||
# However, subclasses of ActionController::Caching::Sweeper should be Reloadable
|
||||
include Reloadable::Subclasses
|
||||
|
||||
def before(controller)
|
||||
self.controller = controller
|
||||
callback(:before)
|
||||
end
|
||||
|
||||
def after(controller)
|
||||
callback(:after)
|
||||
# Clean up, so that the controller can be collected after this request
|
||||
self.controller = nil
|
||||
end
|
||||
|
||||
private
|
||||
def callback(timing)
|
||||
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
|
||||
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
|
||||
|
||||
send(controller_callback_method_name) if respond_to?(controller_callback_method_name)
|
||||
send(action_callback_method_name) if respond_to?(action_callback_method_name)
|
||||
end
|
||||
|
||||
def method_missing(method, *arguments)
|
||||
return if @controller.nil?
|
||||
@controller.send(method, *arguments)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
43
vendor/rails/actionpack/lib/action_controller/cgi_ext/cgi_ext.rb
vendored
Executable file
43
vendor/rails/actionpack/lib/action_controller/cgi_ext/cgi_ext.rb
vendored
Executable file
|
@ -0,0 +1,43 @@
|
|||
require 'cgi'
|
||||
require 'cgi/session'
|
||||
require 'cgi/session/pstore'
|
||||
require 'action_controller/cgi_ext/cgi_methods'
|
||||
|
||||
# Wrapper around the CGIMethods that have been secluded to allow testing without
|
||||
# an instantiated CGI object
|
||||
class CGI #:nodoc:
|
||||
class << self
|
||||
alias :escapeHTML_fail_on_nil :escapeHTML
|
||||
|
||||
def escapeHTML(string)
|
||||
escapeHTML_fail_on_nil(string) unless string.nil?
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a parameter hash including values from both the request (POST/GET)
|
||||
# and the query string with the latter taking precedence.
|
||||
def parameters
|
||||
request_parameters.update(query_parameters)
|
||||
end
|
||||
|
||||
def query_parameters
|
||||
CGIMethods.parse_query_parameters(query_string)
|
||||
end
|
||||
|
||||
def request_parameters
|
||||
CGIMethods.parse_request_parameters(params, env_table)
|
||||
end
|
||||
|
||||
def redirect(where)
|
||||
header({
|
||||
"Status" => "302 Moved",
|
||||
"location" => "#{where}"
|
||||
})
|
||||
end
|
||||
|
||||
def session(parameters = nil)
|
||||
parameters = {} if parameters.nil?
|
||||
parameters['database_manager'] = CGI::Session::PStore
|
||||
CGI::Session.new(self, parameters)
|
||||
end
|
||||
end
|
217
vendor/rails/actionpack/lib/action_controller/cgi_ext/cgi_methods.rb
vendored
Executable file
217
vendor/rails/actionpack/lib/action_controller/cgi_ext/cgi_methods.rb
vendored
Executable file
|
@ -0,0 +1,217 @@
|
|||
require 'cgi'
|
||||
require 'action_controller/vendor/xml_simple'
|
||||
require 'action_controller/vendor/xml_node'
|
||||
|
||||
# Static methods for parsing the query and request parameters that can be used in
|
||||
# a CGI extension class or testing in isolation.
|
||||
class CGIMethods #:nodoc:
|
||||
public
|
||||
# Returns a hash with the pairs from the query string. The implicit hash construction that is done in
|
||||
# parse_request_params is not done here.
|
||||
def CGIMethods.parse_query_parameters(query_string)
|
||||
parsed_params = {}
|
||||
|
||||
query_string.split(/[&;]/).each { |p|
|
||||
# Ignore repeated delimiters.
|
||||
next if p.empty?
|
||||
|
||||
k, v = p.split('=',2)
|
||||
v = nil if (v && v.empty?)
|
||||
|
||||
k = CGI.unescape(k) if k
|
||||
v = CGI.unescape(v) if v
|
||||
|
||||
unless k.include?(?[)
|
||||
parsed_params[k] = v
|
||||
else
|
||||
keys = split_key(k)
|
||||
last_key = keys.pop
|
||||
last_key = keys.pop if (use_array = last_key.empty?)
|
||||
parent = keys.inject(parsed_params) {|h, k| h[k] ||= {}}
|
||||
|
||||
if use_array then (parent[last_key] ||= []) << v
|
||||
else parent[last_key] = v
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
parsed_params
|
||||
end
|
||||
|
||||
# Returns the request (POST/GET) parameters in a parsed form where pairs such as "customer[address][street]" /
|
||||
# "Somewhere cool!" are translated into a full hash hierarchy, like
|
||||
# { "customer" => { "address" => { "street" => "Somewhere cool!" } } }
|
||||
def CGIMethods.parse_request_parameters(params)
|
||||
parsed_params = {}
|
||||
|
||||
for key, value in params
|
||||
value = [value] if key =~ /.*\[\]$/
|
||||
unless key.include?('[')
|
||||
# much faster to test for the most common case first (GET)
|
||||
# and avoid the call to build_deep_hash
|
||||
parsed_params[key] = get_typed_value(value[0])
|
||||
else
|
||||
build_deep_hash(get_typed_value(value[0]), parsed_params, get_levels(key))
|
||||
end
|
||||
end
|
||||
|
||||
parsed_params
|
||||
end
|
||||
|
||||
def self.parse_formatted_request_parameters(mime_type, raw_post_data)
|
||||
params = case strategy = ActionController::Base.param_parsers[mime_type]
|
||||
when Proc
|
||||
strategy.call(raw_post_data)
|
||||
when :xml_simple
|
||||
raw_post_data.blank? ? nil :
|
||||
typecast_xml_value(XmlSimple.xml_in(raw_post_data,
|
||||
'forcearray' => false,
|
||||
'forcecontent' => true,
|
||||
'keeproot' => true,
|
||||
'contentkey' => '__content__'))
|
||||
when :yaml
|
||||
YAML.load(raw_post_data)
|
||||
when :xml_node
|
||||
node = XmlNode.from_xml(raw_post_data)
|
||||
{ node.node_name => node }
|
||||
end
|
||||
|
||||
dasherize_keys(params || {})
|
||||
rescue Object => e
|
||||
{ "exception" => "#{e.message} (#{e.class})", "backtrace" => e.backtrace,
|
||||
"raw_post_data" => raw_post_data, "format" => mime_type }
|
||||
end
|
||||
|
||||
def self.typecast_xml_value(value)
|
||||
case value
|
||||
when Hash
|
||||
if value.has_key?("__content__")
|
||||
content = translate_xml_entities(value["__content__"])
|
||||
case value["type"]
|
||||
when "integer" then content.to_i
|
||||
when "boolean" then content == "true"
|
||||
when "datetime" then Time.parse(content)
|
||||
when "date" then Date.parse(content)
|
||||
else content
|
||||
end
|
||||
else
|
||||
value.empty? ? nil : value.inject({}) do |h,(k,v)|
|
||||
h[k] = typecast_xml_value(v)
|
||||
h
|
||||
end
|
||||
end
|
||||
when Array
|
||||
value.map! { |i| typecast_xml_value(i) }
|
||||
case value.length
|
||||
when 0 then nil
|
||||
when 1 then value.first
|
||||
else value
|
||||
end
|
||||
else
|
||||
raise "can't typecast #{value.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.translate_xml_entities(value)
|
||||
value.gsub(/</, "<").
|
||||
gsub(/>/, ">").
|
||||
gsub(/"/, '"').
|
||||
gsub(/'/, "'").
|
||||
gsub(/&/, "&")
|
||||
end
|
||||
|
||||
def self.dasherize_keys(params)
|
||||
case params.class.to_s
|
||||
when "Hash"
|
||||
params.inject({}) do |h,(k,v)|
|
||||
h[k.to_s.tr("-", "_")] = dasherize_keys(v)
|
||||
h
|
||||
end
|
||||
when "Array"
|
||||
params.map { |v| dasherize_keys(v) }
|
||||
else
|
||||
params
|
||||
end
|
||||
end
|
||||
|
||||
# Splits the given key into several pieces. Example keys are 'name', 'person[name]',
|
||||
# 'person[name][first]', and 'people[]'. In each instance, an Array instance is returned.
|
||||
# 'person[name][first]' produces ['person', 'name', 'first']; 'people[]' produces ['people', '']
|
||||
def CGIMethods.split_key(key)
|
||||
if /^([^\[]+)((?:\[[^\]]*\])+)$/ =~ key
|
||||
keys = [$1]
|
||||
|
||||
keys.concat($2[1..-2].split(']['))
|
||||
keys << '' if key[-2..-1] == '[]' # Have to add it since split will drop empty strings
|
||||
|
||||
keys
|
||||
else
|
||||
[key]
|
||||
end
|
||||
end
|
||||
|
||||
def CGIMethods.get_typed_value(value)
|
||||
# test most frequent case first
|
||||
if value.is_a?(String)
|
||||
value
|
||||
elsif value.respond_to?(:content_type) && ! value.content_type.blank?
|
||||
# Uploaded file
|
||||
unless value.respond_to?(:full_original_filename)
|
||||
class << value
|
||||
alias_method :full_original_filename, :original_filename
|
||||
|
||||
# Take the basename of the upload's original filename.
|
||||
# This handles the full Windows paths given by Internet Explorer
|
||||
# (and perhaps other broken user agents) without affecting
|
||||
# those which give the lone filename.
|
||||
# The Windows regexp is adapted from Perl's File::Basename.
|
||||
def original_filename
|
||||
if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
|
||||
md.captures.first
|
||||
else
|
||||
File.basename full_original_filename
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return the same value after overriding original_filename.
|
||||
value
|
||||
|
||||
elsif value.respond_to?(:read)
|
||||
# Value as part of a multipart request
|
||||
value.read
|
||||
elsif value.class == Array
|
||||
value.collect { |v| CGIMethods.get_typed_value(v) }
|
||||
else
|
||||
# other value (neither string nor a multipart request)
|
||||
value.to_s
|
||||
end
|
||||
end
|
||||
|
||||
PARAMS_HASH_RE = /^([^\[]+)(\[.*\])?(.)?.*$/
|
||||
def CGIMethods.get_levels(key)
|
||||
all, main, bracketed, trailing = PARAMS_HASH_RE.match(key).to_a
|
||||
if main.nil?
|
||||
[]
|
||||
elsif trailing
|
||||
[key]
|
||||
elsif bracketed
|
||||
[main] + bracketed.slice(1...-1).split('][')
|
||||
else
|
||||
[main]
|
||||
end
|
||||
end
|
||||
|
||||
def CGIMethods.build_deep_hash(value, hash, levels)
|
||||
if levels.length == 0
|
||||
value
|
||||
elsif hash.nil?
|
||||
{ levels.first => CGIMethods.build_deep_hash(value, nil, levels[1..-1]) }
|
||||
else
|
||||
hash.update({ levels.first => CGIMethods.build_deep_hash(value, hash[levels.first], levels[1..-1]) })
|
||||
end
|
||||
end
|
||||
end
|
125
vendor/rails/actionpack/lib/action_controller/cgi_ext/cookie_performance_fix.rb
vendored
Normal file
125
vendor/rails/actionpack/lib/action_controller/cgi_ext/cookie_performance_fix.rb
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
CGI.module_eval { remove_const "Cookie" }
|
||||
|
||||
class CGI #:nodoc:
|
||||
# This is a cookie class that fixes the performance problems with the default one that ships with 1.8.1 and below.
|
||||
# It replaces the inheritance on SimpleDelegator with DelegateClass(Array) following the suggestion from Matz on
|
||||
# http://groups.google.com/groups?th=e3a4e68ba042f842&seekm=c3sioe%241qvm%241%40news.cybercity.dk#link14
|
||||
class Cookie < DelegateClass(Array)
|
||||
# Create a new CGI::Cookie object.
|
||||
#
|
||||
# The contents of the cookie can be specified as a +name+ and one
|
||||
# or more +value+ arguments. Alternatively, the contents can
|
||||
# be specified as a single hash argument. The possible keywords of
|
||||
# this hash are as follows:
|
||||
#
|
||||
# name:: the name of the cookie. Required.
|
||||
# value:: the cookie's value or list of values.
|
||||
# path:: the path for which this cookie applies. Defaults to the
|
||||
# base directory of the CGI script.
|
||||
# domain:: the domain for which this cookie applies.
|
||||
# expires:: the time at which this cookie expires, as a +Time+ object.
|
||||
# secure:: whether this cookie is a secure cookie or not (default to
|
||||
# false). Secure cookies are only transmitted to HTTPS
|
||||
# servers.
|
||||
#
|
||||
# These keywords correspond to attributes of the cookie object.
|
||||
def initialize(name = '', *value)
|
||||
if name.kind_of?(String)
|
||||
@name = name
|
||||
@value = Array(value)
|
||||
@domain = nil
|
||||
@expires = nil
|
||||
@secure = false
|
||||
@path = nil
|
||||
else
|
||||
@name = name['name']
|
||||
@value = Array(name['value'])
|
||||
@domain = name['domain']
|
||||
@expires = name['expires']
|
||||
@secure = name['secure'] || false
|
||||
@path = name['path']
|
||||
end
|
||||
|
||||
unless @name
|
||||
raise ArgumentError, "`name' required"
|
||||
end
|
||||
|
||||
# simple support for IE
|
||||
unless @path
|
||||
%r|^(.*/)|.match(ENV['SCRIPT_NAME'])
|
||||
@path = ($1 or '')
|
||||
end
|
||||
|
||||
super(@value)
|
||||
end
|
||||
|
||||
def __setobj__(obj)
|
||||
@_dc_obj = obj
|
||||
end
|
||||
|
||||
attr_accessor("name", "value", "path", "domain", "expires")
|
||||
attr_reader("secure")
|
||||
|
||||
# Set whether the Cookie is a secure cookie or not.
|
||||
#
|
||||
# +val+ must be a boolean.
|
||||
def secure=(val)
|
||||
@secure = val if val == true or val == false
|
||||
@secure
|
||||
end
|
||||
|
||||
# Convert the Cookie to its string representation.
|
||||
def to_s
|
||||
buf = ""
|
||||
buf << @name << '='
|
||||
|
||||
if @value.kind_of?(String)
|
||||
buf << CGI::escape(@value)
|
||||
else
|
||||
buf << @value.collect{|v| CGI::escape(v) }.join("&")
|
||||
end
|
||||
|
||||
if @domain
|
||||
buf << '; domain=' << @domain
|
||||
end
|
||||
|
||||
if @path
|
||||
buf << '; path=' << @path
|
||||
end
|
||||
|
||||
if @expires
|
||||
buf << '; expires=' << CGI::rfc1123_date(@expires)
|
||||
end
|
||||
|
||||
if @secure == true
|
||||
buf << '; secure'
|
||||
end
|
||||
|
||||
buf
|
||||
end
|
||||
|
||||
# Parse a raw cookie string into a hash of cookie-name=>Cookie
|
||||
# pairs.
|
||||
#
|
||||
# cookies = CGI::Cookie::parse("raw_cookie_string")
|
||||
# # { "name1" => cookie1, "name2" => cookie2, ... }
|
||||
#
|
||||
def self.parse(raw_cookie)
|
||||
cookies = Hash.new([])
|
||||
|
||||
if raw_cookie
|
||||
raw_cookie.split(/; ?/).each do |pairs|
|
||||
name, values = pairs.split('=',2)
|
||||
next unless name and values
|
||||
name = CGI::unescape(name)
|
||||
values = values.split('&').collect!{|v| CGI::unescape(v) }
|
||||
unless cookies.has_key?(name)
|
||||
cookies[name] = new(name, *values)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
cookies
|
||||
end
|
||||
end # class Cookie
|
||||
end
|
74
vendor/rails/actionpack/lib/action_controller/cgi_ext/raw_post_data_fix.rb
vendored
Normal file
74
vendor/rails/actionpack/lib/action_controller/cgi_ext/raw_post_data_fix.rb
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
class CGI #:nodoc:
|
||||
# Add @request.env['RAW_POST_DATA'] for the vegans.
|
||||
module QueryExtension
|
||||
# Initialize the data from the query.
|
||||
#
|
||||
# Handles multipart forms (in particular, forms that involve file uploads).
|
||||
# Reads query parameters in the @params field, and cookies into @cookies.
|
||||
def initialize_query()
|
||||
@cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
|
||||
|
||||
#fix some strange request environments
|
||||
if method = env_table['REQUEST_METHOD']
|
||||
method = method.to_s.downcase.intern
|
||||
else
|
||||
method = :get
|
||||
end
|
||||
|
||||
if method == :post && (boundary = multipart_form_boundary)
|
||||
@multipart = true
|
||||
@params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
|
||||
else
|
||||
@multipart = false
|
||||
@params = CGI::parse(read_query_params(method) || "")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
unless defined?(MULTIPART_FORM_BOUNDARY_RE)
|
||||
MULTIPART_FORM_BOUNDARY_RE = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n #"
|
||||
end
|
||||
|
||||
def multipart_form_boundary
|
||||
MULTIPART_FORM_BOUNDARY_RE.match(env_table['CONTENT_TYPE']).to_a.pop
|
||||
end
|
||||
|
||||
if defined? MOD_RUBY
|
||||
def read_params_from_query
|
||||
Apache::request.args || ''
|
||||
end
|
||||
else
|
||||
def read_params_from_query
|
||||
# fixes CGI querystring parsing for lighttpd
|
||||
env_qs = env_table['QUERY_STRING']
|
||||
if env_qs.blank? && !(uri = env_table['REQUEST_URI']).blank?
|
||||
uri.split('?', 2)[1] || ''
|
||||
else
|
||||
env_qs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def read_params_from_post
|
||||
stdinput.binmode if stdinput.respond_to?(:binmode)
|
||||
content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
|
||||
# fix for Safari Ajax postings that always append \000
|
||||
content.chop! if content[-1] == 0
|
||||
content.gsub! /&_=$/, ''
|
||||
env_table['RAW_POST_DATA'] = content.freeze
|
||||
end
|
||||
|
||||
def read_query_params(method)
|
||||
case method
|
||||
when :get
|
||||
read_params_from_query
|
||||
when :post, :put
|
||||
read_params_from_post
|
||||
when :cmd
|
||||
read_from_cmdline
|
||||
else # when :head, :delete, :options
|
||||
read_params_from_query
|
||||
end
|
||||
end
|
||||
end # module QueryExtension
|
||||
end
|
207
vendor/rails/actionpack/lib/action_controller/cgi_process.rb
vendored
Normal file
207
vendor/rails/actionpack/lib/action_controller/cgi_process.rb
vendored
Normal file
|
@ -0,0 +1,207 @@
|
|||
require 'action_controller/cgi_ext/cgi_ext'
|
||||
require 'action_controller/cgi_ext/cookie_performance_fix'
|
||||
require 'action_controller/cgi_ext/raw_post_data_fix'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
class Base
|
||||
# Process a request extracted from an CGI object and return a response. Pass false as <tt>session_options</tt> to disable
|
||||
# sessions (large performance increase if sessions are not needed). The <tt>session_options</tt> are the same as for CGI::Session:
|
||||
#
|
||||
# * <tt>:database_manager</tt> - standard options are CGI::Session::FileStore, CGI::Session::MemoryStore, and CGI::Session::PStore
|
||||
# (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in
|
||||
# lib/action_controller/session.
|
||||
# * <tt>:session_key</tt> - the parameter name used for the session id. Defaults to '_session_id'.
|
||||
# * <tt>:session_id</tt> - the session id to use. If not provided, then it is retrieved from the +session_key+ parameter
|
||||
# of the request, or automatically generated for a new session.
|
||||
# * <tt>:new_session</tt> - if true, force creation of a new session. If not set, a new session is only created if none currently
|
||||
# exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set,
|
||||
# an ArgumentError is raised.
|
||||
# * <tt>:session_expires</tt> - the time the current session expires, as a +Time+ object. If not set, the session will continue
|
||||
# indefinitely.
|
||||
# * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the
|
||||
# server.
|
||||
# * <tt>:session_secure</tt> - if +true+, this session will only work over HTTPS.
|
||||
# * <tt>:session_path</tt> - the path for which this session applies. Defaults to the directory of the CGI script.
|
||||
def self.process_cgi(cgi = CGI.new, session_options = {})
|
||||
new.process_cgi(cgi, session_options)
|
||||
end
|
||||
|
||||
def process_cgi(cgi, session_options = {}) #:nodoc:
|
||||
process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out
|
||||
end
|
||||
end
|
||||
|
||||
class CgiRequest < AbstractRequest #:nodoc:
|
||||
attr_accessor :cgi, :session_options
|
||||
|
||||
DEFAULT_SESSION_OPTIONS = {
|
||||
:database_manager => CGI::Session::PStore,
|
||||
:prefix => "ruby_sess.",
|
||||
:session_path => "/"
|
||||
} unless const_defined?(:DEFAULT_SESSION_OPTIONS)
|
||||
|
||||
def initialize(cgi, session_options = {})
|
||||
@cgi = cgi
|
||||
@session_options = session_options
|
||||
@env = @cgi.send(:env_table)
|
||||
super()
|
||||
end
|
||||
|
||||
def query_string
|
||||
if (qs = @cgi.query_string) && !qs.empty?
|
||||
qs
|
||||
elsif uri = @env['REQUEST_URI']
|
||||
parts = uri.split('?')
|
||||
parts.shift
|
||||
parts.join('?')
|
||||
else
|
||||
@env['QUERY_STRING'] || ''
|
||||
end
|
||||
end
|
||||
|
||||
def query_parameters
|
||||
(qs = self.query_string).empty? ? {} : CGIMethods.parse_query_parameters(qs)
|
||||
end
|
||||
|
||||
def request_parameters
|
||||
@request_parameters ||=
|
||||
if ActionController::Base.param_parsers.has_key?(content_type)
|
||||
CGIMethods.parse_formatted_request_parameters(content_type, @env['RAW_POST_DATA'])
|
||||
else
|
||||
CGIMethods.parse_request_parameters(@cgi.params)
|
||||
end
|
||||
end
|
||||
|
||||
def cookies
|
||||
@cgi.cookies.freeze
|
||||
end
|
||||
|
||||
def host_with_port
|
||||
if forwarded = env["HTTP_X_FORWARDED_HOST"]
|
||||
forwarded.split(/,\s?/).last
|
||||
elsif http_host = env['HTTP_HOST']
|
||||
http_host
|
||||
elsif server_name = env['SERVER_NAME']
|
||||
server_name
|
||||
else
|
||||
"#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
|
||||
end
|
||||
end
|
||||
|
||||
def host
|
||||
host_with_port[/^[^:]+/]
|
||||
end
|
||||
|
||||
def port
|
||||
if host_with_port =~ /:(\d+)$/
|
||||
$1.to_i
|
||||
else
|
||||
standard_port
|
||||
end
|
||||
end
|
||||
|
||||
def session
|
||||
unless @session
|
||||
if @session_options == false
|
||||
@session = Hash.new
|
||||
else
|
||||
stale_session_check! do
|
||||
if session_options_with_string_keys['new_session'] == true
|
||||
@session = new_session
|
||||
else
|
||||
@session = CGI::Session.new(@cgi, session_options_with_string_keys)
|
||||
end
|
||||
@session['__valid_session']
|
||||
end
|
||||
end
|
||||
end
|
||||
@session
|
||||
end
|
||||
|
||||
def reset_session
|
||||
@session.delete if CGI::Session === @session
|
||||
@session = new_session
|
||||
end
|
||||
|
||||
def method_missing(method_id, *arguments)
|
||||
@cgi.send(method_id, *arguments) rescue super
|
||||
end
|
||||
|
||||
private
|
||||
# Delete an old session if it exists then create a new one.
|
||||
def new_session
|
||||
if @session_options == false
|
||||
Hash.new
|
||||
else
|
||||
CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
|
||||
CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
|
||||
end
|
||||
end
|
||||
|
||||
def stale_session_check!
|
||||
yield
|
||||
rescue ArgumentError => argument_error
|
||||
if argument_error.message =~ %r{undefined class/module (\w+)}
|
||||
begin
|
||||
Module.const_missing($1)
|
||||
rescue LoadError, NameError => const_error
|
||||
raise ActionController::SessionRestoreError, <<end_msg
|
||||
Session contains objects whose class definition isn\'t available.
|
||||
Remember to require the classes for all objects kept in the session.
|
||||
(Original exception: #{const_error.message} [#{const_error.class}])
|
||||
end_msg
|
||||
end
|
||||
|
||||
retry
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
def session_options_with_string_keys
|
||||
@session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).inject({}) { |options, (k,v)| options[k.to_s] = v; options }
|
||||
end
|
||||
end
|
||||
|
||||
class CgiResponse < AbstractResponse #:nodoc:
|
||||
def initialize(cgi)
|
||||
@cgi = cgi
|
||||
super()
|
||||
end
|
||||
|
||||
def out(output = $stdout)
|
||||
convert_content_type!(@headers)
|
||||
output.binmode if output.respond_to?(:binmode)
|
||||
output.sync = false if output.respond_to?(:sync=)
|
||||
|
||||
begin
|
||||
output.write(@cgi.header(@headers))
|
||||
|
||||
if @cgi.send(:env_table)['REQUEST_METHOD'] == 'HEAD'
|
||||
return
|
||||
elsif @body.respond_to?(:call)
|
||||
@body.call(self, output)
|
||||
else
|
||||
output.write(@body)
|
||||
end
|
||||
|
||||
output.flush if output.respond_to?(:flush)
|
||||
rescue Errno::EPIPE => e
|
||||
# lost connection to the FCGI process -- ignore the output, then
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def convert_content_type!(headers)
|
||||
if header = headers.delete("Content-Type")
|
||||
headers["type"] = header
|
||||
end
|
||||
if header = headers.delete("Content-type")
|
||||
headers["type"] = header
|
||||
end
|
||||
if header = headers.delete("content-type")
|
||||
headers["type"] = header
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
235
vendor/rails/actionpack/lib/action_controller/code_generation.rb
vendored
Normal file
235
vendor/rails/actionpack/lib/action_controller/code_generation.rb
vendored
Normal file
|
@ -0,0 +1,235 @@
|
|||
module ActionController
|
||||
module CodeGeneration #:nodoc:
|
||||
class GenerationError < StandardError #:nodoc:
|
||||
end
|
||||
|
||||
class Source #:nodoc:
|
||||
attr_reader :lines, :indentation_level
|
||||
IndentationString = ' '
|
||||
def initialize
|
||||
@lines, @indentation_level = [], 0
|
||||
end
|
||||
def line(line)
|
||||
@lines << (IndentationString * @indentation_level + line)
|
||||
end
|
||||
alias :<< :line
|
||||
|
||||
def indent
|
||||
@indentation_level += 1
|
||||
yield
|
||||
ensure
|
||||
@indentation_level -= 1
|
||||
end
|
||||
|
||||
def to_s() lines.join("\n") end
|
||||
end
|
||||
|
||||
class CodeGenerator #:nodoc:
|
||||
attr_accessor :source, :locals
|
||||
def initialize(source = nil)
|
||||
@locals = []
|
||||
@source = source || Source.new
|
||||
end
|
||||
|
||||
BeginKeywords = %w(if unless begin until while def).collect {|kw| kw.to_sym}
|
||||
ResumeKeywords = %w(elsif else rescue).collect {|kw| kw.to_sym}
|
||||
Keywords = BeginKeywords + ResumeKeywords
|
||||
|
||||
def method_missing(keyword, *text)
|
||||
if Keywords.include? keyword
|
||||
if ResumeKeywords.include? keyword
|
||||
raise GenerationError, "Can only resume with #{keyword} immediately after an end" unless source.lines.last =~ /^\s*end\s*$/
|
||||
source.lines.pop # Remove the 'end'
|
||||
end
|
||||
|
||||
line "#{keyword} #{text.join ' '}"
|
||||
begin source.indent { yield(self.dup) }
|
||||
ensure line 'end'
|
||||
end
|
||||
else
|
||||
super(keyword, *text)
|
||||
end
|
||||
end
|
||||
|
||||
def line(*args) self.source.line(*args) end
|
||||
alias :<< :line
|
||||
def indent(*args, &block) source(*args, &block) end
|
||||
def to_s() source.to_s end
|
||||
|
||||
def share_locals_with(other)
|
||||
other.locals = self.locals = (other.locals | locals)
|
||||
end
|
||||
|
||||
FieldsToDuplicate = [:locals]
|
||||
def dup
|
||||
copy = self.class.new(source)
|
||||
self.class::FieldsToDuplicate.each do |sym|
|
||||
value = self.send(sym)
|
||||
value = value.dup unless value.nil? || value.is_a?(Numeric)
|
||||
copy.send("#{sym}=", value)
|
||||
end
|
||||
return copy
|
||||
end
|
||||
end
|
||||
|
||||
class RecognitionGenerator < CodeGenerator #:nodoc:
|
||||
Attributes = [:after, :before, :current, :results, :constants, :depth, :move_ahead, :finish_statement]
|
||||
attr_accessor(*Attributes)
|
||||
FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes
|
||||
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
@after, @before = [], []
|
||||
@current = nil
|
||||
@results, @constants = {}, {}
|
||||
@depth = 0
|
||||
@move_ahead = nil
|
||||
@finish_statement = Proc.new {|hash_expr| hash_expr}
|
||||
end
|
||||
|
||||
def if_next_matches(string, &block)
|
||||
test = Routing.test_condition(next_segment(true), string)
|
||||
self.if(test, &block)
|
||||
end
|
||||
|
||||
def move_forward(places = 1)
|
||||
dup = self.dup
|
||||
dup.depth += 1
|
||||
dup.move_ahead = places
|
||||
yield dup
|
||||
end
|
||||
|
||||
def next_segment(assign_inline = false, default = nil)
|
||||
if locals.include?(segment_name)
|
||||
code = segment_name
|
||||
else
|
||||
code = "#{segment_name} = #{path_name}[#{index_name}]"
|
||||
if assign_inline
|
||||
code = "(#{code})"
|
||||
else
|
||||
line(code)
|
||||
code = segment_name
|
||||
end
|
||||
|
||||
locals << segment_name
|
||||
end
|
||||
code = "(#{code} || #{default.inspect})" if default
|
||||
|
||||
return code.to_s
|
||||
end
|
||||
|
||||
def segment_name() "segment#{depth}".to_sym end
|
||||
def path_name() :path end
|
||||
def index_name
|
||||
move_ahead, @move_ahead = @move_ahead, nil
|
||||
move_ahead ? "index += #{move_ahead}" : 'index'
|
||||
end
|
||||
|
||||
def continue
|
||||
dup = self.dup
|
||||
dup.before << dup.current
|
||||
dup.current = dup.after.shift
|
||||
dup.go
|
||||
end
|
||||
|
||||
def go
|
||||
if current then current.write_recognition(self)
|
||||
else self.finish
|
||||
end
|
||||
end
|
||||
|
||||
def result(key, expression, delay = false)
|
||||
unless delay
|
||||
line "#{key}_value = #{expression}"
|
||||
expression = "#{key}_value"
|
||||
end
|
||||
results[key] = expression
|
||||
end
|
||||
def constant_result(key, object)
|
||||
constants[key] = object
|
||||
end
|
||||
|
||||
def finish(ensure_traversal_finished = true)
|
||||
pairs = []
|
||||
(results.keys + constants.keys).uniq.each do |key|
|
||||
pairs << "#{key.to_s.inspect} => #{results[key] ? results[key] : constants[key].inspect}"
|
||||
end
|
||||
hash_expr = "{#{pairs.join(', ')}}"
|
||||
|
||||
statement = finish_statement.call(hash_expr)
|
||||
if ensure_traversal_finished then self.if("! #{next_segment(true)}") {|gp| gp << statement}
|
||||
else self << statement
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class GenerationGenerator < CodeGenerator #:nodoc:
|
||||
Attributes = [:after, :before, :current, :segments]
|
||||
attr_accessor(*Attributes)
|
||||
FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes
|
||||
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
@after, @before = [], []
|
||||
@current = nil
|
||||
@segments = []
|
||||
end
|
||||
|
||||
def hash_name() 'hash' end
|
||||
def local_name(key) "#{key}_value" end
|
||||
|
||||
def hash_value(key, assign = true, default = nil)
|
||||
if locals.include?(local_name(key)) then code = local_name(key)
|
||||
else
|
||||
code = "hash[#{key.to_sym.inspect}]"
|
||||
if assign
|
||||
code = "(#{local_name(key)} = #{code})"
|
||||
locals << local_name(key)
|
||||
end
|
||||
end
|
||||
code = "(#{code} || (#{default.inspect}))" if default
|
||||
return code
|
||||
end
|
||||
|
||||
def expire_for_keys(*keys)
|
||||
return if keys.empty?
|
||||
conds = keys.collect {|key| "expire_on[#{key.to_sym.inspect}]"}
|
||||
line "not_expired, #{hash_name} = false, options if not_expired && #{conds.join(' && ')}"
|
||||
end
|
||||
|
||||
def add_segment(*segments)
|
||||
d = dup
|
||||
d.segments.concat segments
|
||||
yield d
|
||||
end
|
||||
|
||||
def go
|
||||
if current then current.write_generation(self)
|
||||
else self.finish
|
||||
end
|
||||
end
|
||||
|
||||
def continue
|
||||
d = dup
|
||||
d.before << d.current
|
||||
d.current = d.after.shift
|
||||
d.go
|
||||
end
|
||||
|
||||
def finish
|
||||
line %("/#{segments.join('/')}")
|
||||
end
|
||||
|
||||
def check_conditions(conditions)
|
||||
tests = []
|
||||
generator = nil
|
||||
conditions.each do |key, condition|
|
||||
tests << (generator || self).hash_value(key, true) if condition.is_a? Regexp
|
||||
tests << Routing.test_condition((generator || self).hash_value(key, false), condition)
|
||||
generator = self.dup unless generator
|
||||
end
|
||||
return tests.join(' && ')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
186
vendor/rails/actionpack/lib/action_controller/components.rb
vendored
Normal file
186
vendor/rails/actionpack/lib/action_controller/components.rb
vendored
Normal file
|
@ -0,0 +1,186 @@
|
|||
module ActionController #:nodoc:
|
||||
# Components allow you to call other actions for their rendered response while executing another action. You can either delegate
|
||||
# the entire response rendering or you can mix a partial response in with your other content.
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# # Performs a method and then lets hello_world output its render
|
||||
# def delegate_action
|
||||
# do_other_stuff_before_hello_world
|
||||
# render_component :controller => "greeter", :action => "hello_world", :params => { :person => "david" }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# class GreeterController < ActionController::Base
|
||||
# def hello_world
|
||||
# render :text => "#{params[:person]} says, Hello World!"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# The same can be done in a view to do a partial rendering:
|
||||
#
|
||||
# Let's see a greeting:
|
||||
# <%= render_component :controller => "greeter", :action => "hello_world" %>
|
||||
#
|
||||
# It is also possible to specify the controller as a class constant, bypassing the inflector
|
||||
# code to compute the controller class at runtime:
|
||||
#
|
||||
# <%= render_component :controller => GreeterController, :action => "hello_world" %>
|
||||
#
|
||||
# == When to use components
|
||||
#
|
||||
# Components should be used with care. They're significantly slower than simply splitting reusable parts into partials and
|
||||
# conceptually more complicated. Don't use components as a way of separating concerns inside a single application. Instead,
|
||||
# reserve components to those rare cases where you truly have reusable view and controller elements that can be employed
|
||||
# across many applications at once.
|
||||
#
|
||||
# So to repeat: Components are a special-purpose approach that can often be replaced with better use of partials and filters.
|
||||
module Components
|
||||
def self.included(base) #:nodoc:
|
||||
base.send :include, InstanceMethods
|
||||
base.extend(ClassMethods)
|
||||
|
||||
base.helper do
|
||||
def render_component(options)
|
||||
@controller.send(:render_component_as_string, options)
|
||||
end
|
||||
end
|
||||
|
||||
# If this controller was instantiated to process a component request,
|
||||
# +parent_controller+ points to the instantiator of this controller.
|
||||
base.send :attr_accessor, :parent_controller
|
||||
|
||||
base.class_eval do
|
||||
alias_method :process_cleanup_without_components, :process_cleanup
|
||||
alias_method :process_cleanup, :process_cleanup_with_components
|
||||
|
||||
alias_method :set_session_options_without_components, :set_session_options
|
||||
alias_method :set_session_options, :set_session_options_with_components
|
||||
|
||||
alias_method :flash_without_components, :flash
|
||||
alias_method :flash, :flash_with_components
|
||||
|
||||
alias_method :component_request?, :parent_controller
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Track parent controller to identify component requests
|
||||
def process_with_components(request, response, parent_controller = nil) #:nodoc:
|
||||
controller = new
|
||||
controller.parent_controller = parent_controller
|
||||
controller.process(request, response)
|
||||
end
|
||||
|
||||
# Set the template root to be one directory behind the root dir of the controller. Examples:
|
||||
# /code/weblog/components/admin/users_controller.rb with Admin::UsersController
|
||||
# will use /code/weblog/components as template root
|
||||
# and find templates in /code/weblog/components/admin/users/
|
||||
#
|
||||
# /code/weblog/components/admin/parties/users_controller.rb with Admin::Parties::UsersController
|
||||
# will also use /code/weblog/components as template root
|
||||
# and find templates in /code/weblog/components/admin/parties/users/
|
||||
def uses_component_template_root
|
||||
path_of_calling_controller = File.dirname(caller[0].split(/:\d+:/).first)
|
||||
path_of_controller_root = path_of_calling_controller.sub(/#{controller_path.split("/")[0..-2]}$/, "") # " (for ruby-mode)
|
||||
|
||||
self.template_root = path_of_controller_root
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
# Extracts the action_name from the request parameters and performs that action.
|
||||
def process_with_components(request, response, method = :perform_action, *arguments) #:nodoc:
|
||||
flash.discard if component_request?
|
||||
process_without_components(request, response, method, *arguments)
|
||||
end
|
||||
|
||||
protected
|
||||
# Renders the component specified as the response for the current method
|
||||
def render_component(options) #:doc:
|
||||
component_logging(options) do
|
||||
render_text(component_response(options, true).body, response.headers["Status"])
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the component response as a string
|
||||
def render_component_as_string(options) #:doc:
|
||||
component_logging(options) do
|
||||
response = component_response(options, false)
|
||||
|
||||
if redirected = response.redirected_to
|
||||
render_component_as_string(redirected)
|
||||
else
|
||||
response.body
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def flash_with_components(refresh = false) #:nodoc:
|
||||
if @flash.nil? || refresh
|
||||
@flash =
|
||||
if @parent_controller
|
||||
@parent_controller.flash
|
||||
else
|
||||
flash_without_components
|
||||
end
|
||||
end
|
||||
|
||||
@flash
|
||||
end
|
||||
|
||||
private
|
||||
def component_response(options, reuse_response)
|
||||
klass = component_class(options)
|
||||
request = request_for_component(klass.controller_name, options)
|
||||
response = reuse_response ? @response : @response.dup
|
||||
|
||||
klass.process_with_components(request, response, self)
|
||||
end
|
||||
|
||||
# determine the controller class for the component request
|
||||
def component_class(options)
|
||||
if controller = options[:controller]
|
||||
controller.is_a?(Class) ? controller : "#{controller.camelize}Controller".constantize
|
||||
else
|
||||
self.class
|
||||
end
|
||||
end
|
||||
|
||||
# Create a new request object based on the current request.
|
||||
# The new request inherits the session from the current request,
|
||||
# bypassing any session options set for the component controller's class
|
||||
def request_for_component(controller_name, options)
|
||||
request = @request.dup
|
||||
request.session = @request.session
|
||||
|
||||
request.instance_variable_set(
|
||||
:@parameters,
|
||||
(options[:params] || {}).with_indifferent_access.update(
|
||||
"controller" => controller_name, "action" => options[:action], "id" => options[:id]
|
||||
)
|
||||
)
|
||||
|
||||
request
|
||||
end
|
||||
|
||||
def component_logging(options)
|
||||
if logger
|
||||
logger.info "Start rendering component (#{options.inspect}): "
|
||||
result = yield
|
||||
logger.info "\n\nEnd of component rendering"
|
||||
result
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def set_session_options_with_components(request)
|
||||
set_session_options_without_components(request) unless component_request?
|
||||
end
|
||||
|
||||
def process_cleanup_with_components
|
||||
process_cleanup_without_components unless component_request?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
77
vendor/rails/actionpack/lib/action_controller/cookies.rb
vendored
Normal file
77
vendor/rails/actionpack/lib/action_controller/cookies.rb
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
module ActionController #:nodoc:
|
||||
# Cookies are read and written through ActionController#cookies. The cookies being read are what were received along with the request,
|
||||
# the cookies being written are what will be sent out with the response. Cookies are read by value (so you won't get the cookie object
|
||||
# itself back -- just the value it holds). Examples for writing:
|
||||
#
|
||||
# cookies[:user_name] = "david" # => Will set a simple session cookie
|
||||
# cookies[:login] = { :value => "XJ-122", :expires => Time.now + 360} # => Will set a cookie that expires in 1 hour
|
||||
#
|
||||
# Examples for reading:
|
||||
#
|
||||
# cookies[:user_name] # => "david"
|
||||
# cookies.size # => 2
|
||||
#
|
||||
# Example for deleting:
|
||||
#
|
||||
# cookies.delete :user_name
|
||||
#
|
||||
# All the option symbols for setting cookies are:
|
||||
#
|
||||
# * <tt>value</tt> - the cookie's value or list of values (as an array).
|
||||
# * <tt>path</tt> - the path for which this cookie applies. Defaults to the root of the application.
|
||||
# * <tt>domain</tt> - the domain for which this cookie applies.
|
||||
# * <tt>expires</tt> - the time at which this cookie expires, as a +Time+ object.
|
||||
# * <tt>secure</tt> - whether this cookie is a secure cookie or not (default to false).
|
||||
# Secure cookies are only transmitted to HTTPS servers.
|
||||
module Cookies
|
||||
protected
|
||||
# Returns the cookie container, which operates as described above.
|
||||
def cookies
|
||||
CookieJar.new(self)
|
||||
end
|
||||
|
||||
# Deprecated cookie writer method
|
||||
def cookie(*options)
|
||||
@response.headers["cookie"] << CGI::Cookie.new(*options)
|
||||
end
|
||||
end
|
||||
|
||||
class CookieJar < Hash #:nodoc:
|
||||
def initialize(controller)
|
||||
@controller, @cookies = controller, controller.instance_variable_get("@cookies")
|
||||
super()
|
||||
update(@cookies)
|
||||
end
|
||||
|
||||
# Returns the value of the cookie by +name+ -- or nil if no such cookie exists. You set new cookies using either the cookie method
|
||||
# or cookies[]= (for simple name/value cookies without options).
|
||||
def [](name)
|
||||
@cookies[name.to_s].value.first if @cookies[name.to_s] && @cookies[name.to_s].respond_to?(:value)
|
||||
end
|
||||
|
||||
def []=(name, options)
|
||||
if options.is_a?(Hash)
|
||||
options = options.inject({}) { |options, pair| options[pair.first.to_s] = pair.last; options }
|
||||
options["name"] = name.to_s
|
||||
else
|
||||
options = { "name" => name.to_s, "value" => options }
|
||||
end
|
||||
|
||||
set_cookie(options)
|
||||
end
|
||||
|
||||
# Removes the cookie on the client machine by setting the value to an empty string
|
||||
# and setting its expiration date into the past
|
||||
def delete(name)
|
||||
set_cookie("name" => name.to_s, "value" => "", "expires" => Time.at(0))
|
||||
end
|
||||
|
||||
private
|
||||
def set_cookie(options) #:doc:
|
||||
options["path"] = "/" unless options["path"]
|
||||
cookie = CGI::Cookie.new(options)
|
||||
@controller.logger.info "Cookie set: #{cookie}" unless @controller.logger.nil?
|
||||
@controller.response.headers["cookie"] << cookie
|
||||
end
|
||||
end
|
||||
end
|
83
vendor/rails/actionpack/lib/action_controller/dependencies.rb
vendored
Normal file
83
vendor/rails/actionpack/lib/action_controller/dependencies.rb
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
module ActionController #:nodoc:
|
||||
module Dependencies #:nodoc:
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Dependencies control what classes are needed for the controller to run its course. This is an alternative to doing explicit
|
||||
# +require+ statements that bring a number of benefits. It's more succinct, communicates what type of dependency we're talking about,
|
||||
# can trigger special behavior (as in the case of +observer+), and enables Rails to be clever about reloading in cached environments
|
||||
# like FCGI. Example:
|
||||
#
|
||||
# class ApplicationController < ActionController::Base
|
||||
# model :account, :company, :person, :project, :category
|
||||
# helper :access_control
|
||||
# service :notifications, :billings
|
||||
# observer :project_change_observer
|
||||
# end
|
||||
#
|
||||
# Please note that a controller like ApplicationController will automatically attempt to require_dependency on a model of its
|
||||
# singuralized name and a helper of its name. If nothing is found, no error is raised. This is especially useful for concrete
|
||||
# controllers like PostController:
|
||||
#
|
||||
# class PostController < ApplicationController
|
||||
# # model :post (already required)
|
||||
# # helper :post (already required)
|
||||
# end
|
||||
#
|
||||
# Also note, that if the models follow the pattern of just 1 class per file in the form of MyClass => my_class.rb, then these
|
||||
# classes don't have to be required as Active Support will auto-require them.
|
||||
module ClassMethods #:nodoc:
|
||||
# Specifies a variable number of models that this controller depends on. Models are normally Active Record classes or a similar
|
||||
# backend for modelling entity classes.
|
||||
def model(*models)
|
||||
require_dependencies(:model, models)
|
||||
depend_on(:model, models)
|
||||
end
|
||||
|
||||
# Specifies a variable number of services that this controller depends on. Services are normally singletons or factories, like
|
||||
# Action Mailer service or a Payment Gateway service.
|
||||
def service(*services)
|
||||
require_dependencies(:service, services)
|
||||
depend_on(:service, services)
|
||||
end
|
||||
|
||||
# Specifies a variable number of observers that are to govern when this controller is handling actions. The observers will
|
||||
# automatically have .instance called on them to make them active on assignment.
|
||||
def observer(*observers)
|
||||
require_dependencies(:observer, observers)
|
||||
depend_on(:observer, observers)
|
||||
instantiate_observers(observers)
|
||||
end
|
||||
|
||||
# Returns an array of symbols that specify the dependencies on a given layer. For the example at the top, calling
|
||||
# <tt>ApplicationController.dependencies_on(:model)</tt> would return <tt>[:account, :company, :person, :project, :category]</tt>
|
||||
def dependencies_on(layer)
|
||||
read_inheritable_attribute("#{layer}_dependencies")
|
||||
end
|
||||
|
||||
def depend_on(layer, dependencies) #:nodoc:
|
||||
write_inheritable_array("#{layer}_dependencies", dependencies)
|
||||
end
|
||||
|
||||
private
|
||||
def instantiate_observers(observers)
|
||||
observers.flatten.each { |observer| Object.const_get(Inflector.classify(observer.to_s)).instance }
|
||||
end
|
||||
|
||||
def require_dependencies(layer, dependencies)
|
||||
dependencies.flatten.each do |dependency|
|
||||
begin
|
||||
require_dependency(dependency.to_s)
|
||||
rescue LoadError => e
|
||||
raise LoadError.new("Missing #{layer} #{dependency}.rb").copy_blame!(e)
|
||||
rescue Object => exception
|
||||
exception.blame_file! "=> #{layer} #{dependency}.rb"
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
204
vendor/rails/actionpack/lib/action_controller/deprecated_assertions.rb
vendored
Normal file
204
vendor/rails/actionpack/lib/action_controller/deprecated_assertions.rb
vendored
Normal file
|
@ -0,0 +1,204 @@
|
|||
require 'test/unit'
|
||||
require 'test/unit/assertions'
|
||||
require 'rexml/document'
|
||||
|
||||
module Test #:nodoc:
|
||||
module Unit #:nodoc:
|
||||
module Assertions
|
||||
def assert_success(message=nil) #:nodoc:
|
||||
assert_response(:success, message)
|
||||
end
|
||||
|
||||
def assert_redirect(message=nil) #:nodoc:
|
||||
assert_response(:redirect, message)
|
||||
end
|
||||
|
||||
def assert_rendered_file(expected=nil, message=nil) #:nodoc:
|
||||
assert_template(expected, message)
|
||||
end
|
||||
|
||||
# ensure that the session has an object with the specified name
|
||||
def assert_session_has(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is not in the session <?>", key, @response.session)
|
||||
assert_block(msg) { @response.has_session_object?(key) }
|
||||
end
|
||||
|
||||
# ensure that the session has no object with the specified name
|
||||
def assert_session_has_no(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is in the session <?>", key, @response.session)
|
||||
assert_block(msg) { !@response.has_session_object?(key) }
|
||||
end
|
||||
|
||||
def assert_session_equal(expected = nil, key = nil, message = nil) #:nodoc:
|
||||
msg = build_message(message, "<?> expected in session['?'] but was <?>", expected, key, @response.session[key])
|
||||
assert_block(msg) { expected == @response.session[key] }
|
||||
end
|
||||
|
||||
# -- cookie assertions ---------------------------------------------------
|
||||
|
||||
def assert_no_cookie(key = nil, message = nil) #:nodoc:
|
||||
actual = @response.cookies[key]
|
||||
msg = build_message(message, "<?> not expected in cookies['?']", actual, key)
|
||||
assert_block(msg) { actual.nil? or actual.empty? }
|
||||
end
|
||||
|
||||
def assert_cookie_equal(expected = nil, key = nil, message = nil) #:nodoc:
|
||||
actual = @response.cookies[key]
|
||||
actual = actual.first if actual
|
||||
msg = build_message(message, "<?> expected in cookies['?'] but was <?>", expected, key, actual)
|
||||
assert_block(msg) { expected == actual }
|
||||
end
|
||||
|
||||
# -- flash assertions ---------------------------------------------------
|
||||
|
||||
# ensure that the flash has an object with the specified name
|
||||
def assert_flash_has(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is not in the flash <?>", key, @response.flash)
|
||||
assert_block(msg) { @response.has_flash_object?(key) }
|
||||
end
|
||||
|
||||
# ensure that the flash has no object with the specified name
|
||||
def assert_flash_has_no(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is in the flash <?>", key, @response.flash)
|
||||
assert_block(msg) { !@response.has_flash_object?(key) }
|
||||
end
|
||||
|
||||
# ensure the flash exists
|
||||
def assert_flash_exists(message=nil) #:nodoc:
|
||||
msg = build_message(message, "the flash does not exist <?>", @response.session['flash'] )
|
||||
assert_block(msg) { @response.has_flash? }
|
||||
end
|
||||
|
||||
# ensure the flash does not exist
|
||||
def assert_flash_not_exists(message=nil) #:nodoc:
|
||||
msg = build_message(message, "the flash exists <?>", @response.flash)
|
||||
assert_block(msg) { !@response.has_flash? }
|
||||
end
|
||||
|
||||
# ensure the flash is empty but existent
|
||||
def assert_flash_empty(message=nil) #:nodoc:
|
||||
msg = build_message(message, "the flash is not empty <?>", @response.flash)
|
||||
assert_block(msg) { !@response.has_flash_with_contents? }
|
||||
end
|
||||
|
||||
# ensure the flash is not empty
|
||||
def assert_flash_not_empty(message=nil) #:nodoc:
|
||||
msg = build_message(message, "the flash is empty")
|
||||
assert_block(msg) { @response.has_flash_with_contents? }
|
||||
end
|
||||
|
||||
def assert_flash_equal(expected = nil, key = nil, message = nil) #:nodoc:
|
||||
msg = build_message(message, "<?> expected in flash['?'] but was <?>", expected, key, @response.flash[key])
|
||||
assert_block(msg) { expected == @response.flash[key] }
|
||||
end
|
||||
|
||||
|
||||
# ensure our redirection url is an exact match
|
||||
def assert_redirect_url(url=nil, message=nil) #:nodoc:
|
||||
assert_redirect(message)
|
||||
msg = build_message(message, "<?> is not the redirected location <?>", url, @response.redirect_url)
|
||||
assert_block(msg) { @response.redirect_url == url }
|
||||
end
|
||||
|
||||
# ensure our redirection url matches a pattern
|
||||
def assert_redirect_url_match(pattern=nil, message=nil) #:nodoc:
|
||||
assert_redirect(message)
|
||||
msg = build_message(message, "<?> was not found in the location: <?>", pattern, @response.redirect_url)
|
||||
assert_block(msg) { @response.redirect_url_match?(pattern) }
|
||||
end
|
||||
|
||||
|
||||
# -- template assertions ------------------------------------------------
|
||||
|
||||
# ensure that a template object with the given name exists
|
||||
def assert_template_has(key=nil, message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is not a template object", key )
|
||||
assert_block(msg) { @response.has_template_object?(key) }
|
||||
end
|
||||
|
||||
# ensure that a template object with the given name does not exist
|
||||
def assert_template_has_no(key=nil,message=nil) #:nodoc:
|
||||
msg = build_message(message, "<?> is a template object <?>", key, @response.template_objects[key])
|
||||
assert_block(msg) { !@response.has_template_object?(key) }
|
||||
end
|
||||
|
||||
# ensures that the object assigned to the template on +key+ is equal to +expected+ object.
|
||||
def assert_template_equal(expected = nil, key = nil, message = nil) #:nodoc:
|
||||
msg = build_message(message, "<?> expected in assigns['?'] but was <?>", expected, key, @response.template.assigns[key.to_s])
|
||||
assert_block(msg) { expected == @response.template.assigns[key.to_s] }
|
||||
end
|
||||
alias_method :assert_assigned_equal, :assert_template_equal
|
||||
|
||||
# Asserts that the template returns the +expected+ string or array based on the XPath +expression+.
|
||||
# This will only work if the template rendered a valid XML document.
|
||||
def assert_template_xpath_match(expression=nil, expected=nil, message=nil) #:nodoc:
|
||||
xml, matches = REXML::Document.new(@response.body), []
|
||||
xml.elements.each(expression) { |e| matches << e.text }
|
||||
if matches.empty? then
|
||||
msg = build_message(message, "<?> not found in document", expression)
|
||||
flunk(msg)
|
||||
return
|
||||
elsif matches.length < 2 then
|
||||
matches = matches.first
|
||||
end
|
||||
|
||||
msg = build_message(message, "<?> found <?>, not <?>", expression, matches, expected)
|
||||
assert_block(msg) { matches == expected }
|
||||
end
|
||||
|
||||
# Assert the template object with the given name is an Active Record descendant and is valid.
|
||||
def assert_valid_record(key = nil, message = nil) #:nodoc:
|
||||
record = find_record_in_template(key)
|
||||
msg = build_message(message, "Active Record is invalid <?>)", record.errors.full_messages)
|
||||
assert_block(msg) { record.valid? }
|
||||
end
|
||||
|
||||
# Assert the template object with the given name is an Active Record descendant and is invalid.
|
||||
def assert_invalid_record(key = nil, message = nil) #:nodoc:
|
||||
record = find_record_in_template(key)
|
||||
msg = build_message(message, "Active Record is valid)")
|
||||
assert_block(msg) { !record.valid? }
|
||||
end
|
||||
|
||||
# Assert the template object with the given name is an Active Record descendant and the specified column(s) are valid.
|
||||
def assert_valid_column_on_record(key = nil, columns = "", message = nil) #:nodoc:
|
||||
record = find_record_in_template(key)
|
||||
record.send(:validate)
|
||||
|
||||
cols = glue_columns(columns)
|
||||
cols.delete_if { |col| !record.errors.invalid?(col) }
|
||||
msg = build_message(message, "Active Record has invalid columns <?>)", cols.join(",") )
|
||||
assert_block(msg) { cols.empty? }
|
||||
end
|
||||
|
||||
# Assert the template object with the given name is an Active Record descendant and the specified column(s) are invalid.
|
||||
def assert_invalid_column_on_record(key = nil, columns = "", message = nil) #:nodoc:
|
||||
record = find_record_in_template(key)
|
||||
record.send(:validate)
|
||||
|
||||
cols = glue_columns(columns)
|
||||
cols.delete_if { |col| record.errors.invalid?(col) }
|
||||
msg = build_message(message, "Active Record has valid columns <?>)", cols.join(",") )
|
||||
assert_block(msg) { cols.empty? }
|
||||
end
|
||||
|
||||
private
|
||||
def glue_columns(columns)
|
||||
cols = []
|
||||
cols << columns if columns.class == String
|
||||
cols += columns if columns.class == Array
|
||||
cols
|
||||
end
|
||||
|
||||
def find_record_in_template(key = nil)
|
||||
assert_template_has(key)
|
||||
record = @response.template_objects[key]
|
||||
|
||||
assert_not_nil(record)
|
||||
assert_kind_of ActiveRecord::Base, record
|
||||
|
||||
return record
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
17
vendor/rails/actionpack/lib/action_controller/deprecated_redirects.rb
vendored
Normal file
17
vendor/rails/actionpack/lib/action_controller/deprecated_redirects.rb
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
module ActionController
|
||||
class Base
|
||||
protected
|
||||
# Deprecated in favor of calling redirect_to directly with the path.
|
||||
def redirect_to_path(path) #:nodoc:
|
||||
redirect_to(path)
|
||||
end
|
||||
|
||||
# Deprecated in favor of calling redirect_to directly with the url. If the resource has moved permanently, it's possible to pass
|
||||
# true as the second parameter and the browser will get "301 Moved Permanently" instead of "302 Found". This can also be done through
|
||||
# just setting the headers["Status"] to "301 Moved Permanently" before using the redirect_to.
|
||||
def redirect_to_url(url, permanently = false) #:nodoc:
|
||||
headers["Status"] = "301 Moved Permanently" if permanently
|
||||
redirect_to(url)
|
||||
end
|
||||
end
|
||||
end
|
34
vendor/rails/actionpack/lib/action_controller/deprecated_request_methods.rb
vendored
Normal file
34
vendor/rails/actionpack/lib/action_controller/deprecated_request_methods.rb
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
module ActionController
|
||||
class AbstractRequest
|
||||
# Determine whether the body of a HTTP call is URL-encoded (default)
|
||||
# or matches one of the registered param_parsers.
|
||||
#
|
||||
# For backward compatibility, the post format is extracted from the
|
||||
# X-Post-Data-Format HTTP header if present.
|
||||
def post_format
|
||||
case content_type.to_s
|
||||
when 'application/xml'
|
||||
:xml
|
||||
when 'application/x-yaml'
|
||||
:yaml
|
||||
else
|
||||
:url_encoded
|
||||
end
|
||||
end
|
||||
|
||||
# Is this a POST request formatted as XML or YAML?
|
||||
def formatted_post?
|
||||
post? && (post_format == :yaml || post_format == :xml)
|
||||
end
|
||||
|
||||
# Is this a POST request formatted as XML?
|
||||
def xml_post?
|
||||
post? && post_format == :xml
|
||||
end
|
||||
|
||||
# Is this a POST request formatted as YAML?
|
||||
def yaml_post?
|
||||
post? && post_format == :yaml
|
||||
end
|
||||
end
|
||||
end
|
444
vendor/rails/actionpack/lib/action_controller/filters.rb
vendored
Normal file
444
vendor/rails/actionpack/lib/action_controller/filters.rb
vendored
Normal file
|
@ -0,0 +1,444 @@
|
|||
module ActionController #:nodoc:
|
||||
module Filters #:nodoc:
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, ActionController::Filters::InstanceMethods)
|
||||
end
|
||||
|
||||
# Filters enable controllers to run shared pre and post processing code for its actions. These filters can be used to do
|
||||
# authentication, caching, or auditing before the intended action is performed. Or to do localization or output
|
||||
# compression after the action has been performed.
|
||||
#
|
||||
# Filters have access to the request, response, and all the instance variables set by other filters in the chain
|
||||
# or by the action (in the case of after filters). Additionally, it's possible for a pre-processing <tt>before_filter</tt>
|
||||
# to halt the processing before the intended action is processed by returning false or performing a redirect or render.
|
||||
# This is especially useful for filters like authentication where you're not interested in allowing the action to be
|
||||
# performed if the proper credentials are not in order.
|
||||
#
|
||||
# == Filter inheritance
|
||||
#
|
||||
# Controller inheritance hierarchies share filters downwards, but subclasses can also add new filters without
|
||||
# affecting the superclass. For example:
|
||||
#
|
||||
# class BankController < ActionController::Base
|
||||
# before_filter :audit
|
||||
#
|
||||
# private
|
||||
# def audit
|
||||
# # record the action and parameters in an audit log
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# class VaultController < BankController
|
||||
# before_filter :verify_credentials
|
||||
#
|
||||
# private
|
||||
# def verify_credentials
|
||||
# # make sure the user is allowed into the vault
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Now any actions performed on the BankController will have the audit method called before. On the VaultController,
|
||||
# first the audit method is called, then the verify_credentials method. If the audit method returns false, then
|
||||
# verify_credentials and the intended action are never called.
|
||||
#
|
||||
# == Filter types
|
||||
#
|
||||
# A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first
|
||||
# is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of
|
||||
# the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form.
|
||||
#
|
||||
# Using an external class makes for more easily reused generic filters, such as output compression. External filter classes
|
||||
# are implemented by having a static +filter+ method on any class and then passing this class to the filter method. Example:
|
||||
#
|
||||
# class OutputCompressionFilter
|
||||
# def self.filter(controller)
|
||||
# controller.response.body = compress(controller.response.body)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# class NewspaperController < ActionController::Base
|
||||
# after_filter OutputCompressionFilter
|
||||
# end
|
||||
#
|
||||
# The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can
|
||||
# manipulate them as it sees fit.
|
||||
#
|
||||
# The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation.
|
||||
# Or just as a quick test. It works like this:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# before_filter { |controller| false if controller.params["stop_action"] }
|
||||
# end
|
||||
#
|
||||
# As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables.
|
||||
# This means that the block has access to both the request and response objects complete with convenience methods for params,
|
||||
# session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call
|
||||
# and returns 1 or -1 on arity will do (such as a Proc or an Method object).
|
||||
#
|
||||
# == Filter chain ordering
|
||||
#
|
||||
# Using <tt>before_filter</tt> and <tt>after_filter</tt> appends the specified filters to the existing chain. That's usually
|
||||
# just fine, but some times you care more about the order in which the filters are executed. When that's the case, you
|
||||
# can use <tt>prepend_before_filter</tt> and <tt>prepend_after_filter</tt>. Filters added by these methods will be put at the
|
||||
# beginning of their respective chain and executed before the rest. For example:
|
||||
#
|
||||
# class ShoppingController
|
||||
# before_filter :verify_open_shop
|
||||
#
|
||||
# class CheckoutController
|
||||
# prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock
|
||||
#
|
||||
# The filter chain for the CheckoutController is now <tt>:ensure_items_in_cart, :ensure_items_in_stock,</tt>
|
||||
# <tt>:verify_open_shop</tt>. So if either of the ensure filters return false, we'll never get around to see if the shop
|
||||
# is open or not.
|
||||
#
|
||||
# You may pass multiple filter arguments of each type as well as a filter block.
|
||||
# If a block is given, it is treated as the last argument.
|
||||
#
|
||||
# == Around filters
|
||||
#
|
||||
# In addition to the individual before and after filters, it's also possible to specify that a single object should handle
|
||||
# both the before and after call. That's especially useful when you need to keep state active between the before and after,
|
||||
# such as the example of a benchmark filter below:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# around_filter BenchmarkingFilter.new
|
||||
#
|
||||
# # Before this action is performed, BenchmarkingFilter#before(controller) is executed
|
||||
# def index
|
||||
# end
|
||||
# # After this action has been performed, BenchmarkingFilter#after(controller) is executed
|
||||
# end
|
||||
#
|
||||
# class BenchmarkingFilter
|
||||
# def initialize
|
||||
# @runtime
|
||||
# end
|
||||
#
|
||||
# def before
|
||||
# start_timer
|
||||
# end
|
||||
#
|
||||
# def after
|
||||
# stop_timer
|
||||
# report_result
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# == Filter chain skipping
|
||||
#
|
||||
# Some times its convenient to specify a filter chain in a superclass that'll hold true for the majority of the
|
||||
# subclasses, but not necessarily all of them. The subclasses that behave in exception can then specify which filters
|
||||
# they would like to be relieved of. Examples
|
||||
#
|
||||
# class ApplicationController < ActionController::Base
|
||||
# before_filter :authenticate
|
||||
# end
|
||||
#
|
||||
# class WeblogController < ApplicationController
|
||||
# # will run the :authenticate filter
|
||||
# end
|
||||
#
|
||||
# class SignupController < ApplicationController
|
||||
# # will not run the :authenticate filter
|
||||
# skip_before_filter :authenticate
|
||||
# end
|
||||
#
|
||||
# == Filter conditions
|
||||
#
|
||||
# Filters can be limited to run for only specific actions. This can be expressed either by listing the actions to
|
||||
# exclude or the actions to include when executing the filter. Available conditions are +:only+ or +:except+, both
|
||||
# of which accept an arbitrary number of method references. For example:
|
||||
#
|
||||
# class Journal < ActionController::Base
|
||||
# # only require authentication if the current action is edit or delete
|
||||
# before_filter :authorize, :only => [ :edit, :delete ]
|
||||
#
|
||||
# private
|
||||
# def authorize
|
||||
# # redirect to login unless authenticated
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# When setting conditions on inline method (proc) filters the condition must come first and be placed in parentheses.
|
||||
#
|
||||
# class UserPreferences < ActionController::Base
|
||||
# before_filter(:except => :new) { # some proc ... }
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
module ClassMethods
|
||||
# The passed <tt>filters</tt> will be appended to the array of filters that's run _before_ actions
|
||||
# on this controller are performed.
|
||||
def append_before_filter(*filters, &block)
|
||||
conditions = extract_conditions!(filters)
|
||||
filters << block if block_given?
|
||||
add_action_conditions(filters, conditions)
|
||||
append_filter_to_chain('before', filters)
|
||||
end
|
||||
|
||||
# The passed <tt>filters</tt> will be prepended to the array of filters that's run _before_ actions
|
||||
# on this controller are performed.
|
||||
def prepend_before_filter(*filters, &block)
|
||||
conditions = extract_conditions!(filters)
|
||||
filters << block if block_given?
|
||||
add_action_conditions(filters, conditions)
|
||||
prepend_filter_to_chain('before', filters)
|
||||
end
|
||||
|
||||
# Short-hand for append_before_filter since that's the most common of the two.
|
||||
alias :before_filter :append_before_filter
|
||||
|
||||
# The passed <tt>filters</tt> will be appended to the array of filters that's run _after_ actions
|
||||
# on this controller are performed.
|
||||
def append_after_filter(*filters, &block)
|
||||
conditions = extract_conditions!(filters)
|
||||
filters << block if block_given?
|
||||
add_action_conditions(filters, conditions)
|
||||
append_filter_to_chain('after', filters)
|
||||
end
|
||||
|
||||
# The passed <tt>filters</tt> will be prepended to the array of filters that's run _after_ actions
|
||||
# on this controller are performed.
|
||||
def prepend_after_filter(*filters, &block)
|
||||
conditions = extract_conditions!(filters)
|
||||
filters << block if block_given?
|
||||
add_action_conditions(filters, conditions)
|
||||
prepend_filter_to_chain("after", filters)
|
||||
end
|
||||
|
||||
# Short-hand for append_after_filter since that's the most common of the two.
|
||||
alias :after_filter :append_after_filter
|
||||
|
||||
# The passed <tt>filters</tt> will have their +before+ method appended to the array of filters that's run both before actions
|
||||
# on this controller are performed and have their +after+ method prepended to the after actions. The filter objects must all
|
||||
# respond to both +before+ and +after+. So if you do append_around_filter A.new, B.new, the callstack will look like:
|
||||
#
|
||||
# B#before
|
||||
# A#before
|
||||
# A#after
|
||||
# B#after
|
||||
def append_around_filter(*filters)
|
||||
conditions = extract_conditions!(filters)
|
||||
for filter in filters.flatten
|
||||
ensure_filter_responds_to_before_and_after(filter)
|
||||
append_before_filter(conditions || {}) { |c| filter.before(c) }
|
||||
prepend_after_filter(conditions || {}) { |c| filter.after(c) }
|
||||
end
|
||||
end
|
||||
|
||||
# The passed <tt>filters</tt> will have their +before+ method prepended to the array of filters that's run both before actions
|
||||
# on this controller are performed and have their +after+ method appended to the after actions. The filter objects must all
|
||||
# respond to both +before+ and +after+. So if you do prepend_around_filter A.new, B.new, the callstack will look like:
|
||||
#
|
||||
# A#before
|
||||
# B#before
|
||||
# B#after
|
||||
# A#after
|
||||
def prepend_around_filter(*filters)
|
||||
for filter in filters.flatten
|
||||
ensure_filter_responds_to_before_and_after(filter)
|
||||
prepend_before_filter { |c| filter.before(c) }
|
||||
append_after_filter { |c| filter.after(c) }
|
||||
end
|
||||
end
|
||||
|
||||
# Short-hand for append_around_filter since that's the most common of the two.
|
||||
alias :around_filter :append_around_filter
|
||||
|
||||
# Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference
|
||||
# filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
|
||||
# of many sub-controllers need a different hierarchy.
|
||||
#
|
||||
# You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
|
||||
# just like when you apply the filters.
|
||||
def skip_before_filter(*filters)
|
||||
if conditions = extract_conditions!(filters)
|
||||
remove_contradicting_conditions!(filters, conditions)
|
||||
conditions[:only], conditions[:except] = conditions[:except], conditions[:only]
|
||||
add_action_conditions(filters, conditions)
|
||||
else
|
||||
for filter in filters.flatten
|
||||
write_inheritable_attribute("before_filters", read_inheritable_attribute("before_filters") - [ filter ])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference
|
||||
# filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
|
||||
# of many sub-controllers need a different hierarchy.
|
||||
#
|
||||
# You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
|
||||
# just like when you apply the filters.
|
||||
def skip_after_filter(*filters)
|
||||
if conditions = extract_conditions!(filters)
|
||||
remove_contradicting_conditions!(filters, conditions)
|
||||
conditions[:only], conditions[:except] = conditions[:except], conditions[:only]
|
||||
add_action_conditions(filters, conditions)
|
||||
else
|
||||
for filter in filters.flatten
|
||||
write_inheritable_attribute("after_filters", read_inheritable_attribute("after_filters") - [ filter ])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns all the before filters for this class and all its ancestors.
|
||||
def before_filters #:nodoc:
|
||||
@before_filters ||= read_inheritable_attribute("before_filters") || []
|
||||
end
|
||||
|
||||
# Returns all the after filters for this class and all its ancestors.
|
||||
def after_filters #:nodoc:
|
||||
@after_filters ||= read_inheritable_attribute("after_filters") || []
|
||||
end
|
||||
|
||||
# Returns a mapping between filters and the actions that may run them.
|
||||
def included_actions #:nodoc:
|
||||
@included_actions ||= read_inheritable_attribute("included_actions") || {}
|
||||
end
|
||||
|
||||
# Returns a mapping between filters and actions that may not run them.
|
||||
def excluded_actions #:nodoc:
|
||||
@excluded_actions ||= read_inheritable_attribute("excluded_actions") || {}
|
||||
end
|
||||
|
||||
private
|
||||
def append_filter_to_chain(condition, filters)
|
||||
write_inheritable_array("#{condition}_filters", filters)
|
||||
end
|
||||
|
||||
def prepend_filter_to_chain(condition, filters)
|
||||
old_filters = read_inheritable_attribute("#{condition}_filters") || []
|
||||
write_inheritable_attribute("#{condition}_filters", filters + old_filters)
|
||||
end
|
||||
|
||||
def ensure_filter_responds_to_before_and_after(filter)
|
||||
unless filter.respond_to?(:before) && filter.respond_to?(:after)
|
||||
raise ActionControllerError, "Filter object must respond to both before and after"
|
||||
end
|
||||
end
|
||||
|
||||
def extract_conditions!(filters)
|
||||
return nil unless filters.last.is_a? Hash
|
||||
filters.pop
|
||||
end
|
||||
|
||||
def add_action_conditions(filters, conditions)
|
||||
return unless conditions
|
||||
included, excluded = conditions[:only], conditions[:except]
|
||||
write_inheritable_hash('included_actions', condition_hash(filters, included)) && return if included
|
||||
write_inheritable_hash('excluded_actions', condition_hash(filters, excluded)) if excluded
|
||||
end
|
||||
|
||||
def condition_hash(filters, *actions)
|
||||
filters.inject({}) {|hash, filter| hash.merge(filter => actions.flatten.map {|action| action.to_s})}
|
||||
end
|
||||
|
||||
def remove_contradicting_conditions!(filters, conditions)
|
||||
return unless conditions[:only]
|
||||
filters.each do |filter|
|
||||
next unless included_actions_for_filter = (read_inheritable_attribute('included_actions') || {})[filter]
|
||||
[*conditions[:only]].each do |conditional_action|
|
||||
conditional_action = conditional_action.to_s
|
||||
included_actions_for_filter.delete(conditional_action) if included_actions_for_filter.include?(conditional_action)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods # :nodoc:
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
alias_method :perform_action_without_filters, :perform_action
|
||||
alias_method :perform_action, :perform_action_with_filters
|
||||
|
||||
alias_method :process_without_filters, :process
|
||||
alias_method :process, :process_with_filters
|
||||
|
||||
alias_method :process_cleanup_without_filters, :process_cleanup
|
||||
alias_method :process_cleanup, :process_cleanup_with_filters
|
||||
end
|
||||
end
|
||||
|
||||
def perform_action_with_filters
|
||||
before_action_result = before_action
|
||||
|
||||
unless before_action_result == false || performed?
|
||||
perform_action_without_filters
|
||||
after_action
|
||||
end
|
||||
|
||||
@before_filter_chain_aborted = (before_action_result == false)
|
||||
end
|
||||
|
||||
def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
|
||||
@before_filter_chain_aborted = false
|
||||
process_without_filters(request, response, method, *arguments)
|
||||
end
|
||||
|
||||
# Calls all the defined before-filter filters, which are added by using "before_filter :method".
|
||||
# If any of the filters return false, no more filters will be executed and the action is aborted.
|
||||
def before_action #:doc:
|
||||
call_filters(self.class.before_filters)
|
||||
end
|
||||
|
||||
# Calls all the defined after-filter filters, which are added by using "after_filter :method".
|
||||
# If any of the filters return false, no more filters will be executed.
|
||||
def after_action #:doc:
|
||||
call_filters(self.class.after_filters)
|
||||
end
|
||||
|
||||
private
|
||||
def call_filters(filters)
|
||||
filters.each do |filter|
|
||||
next if action_exempted?(filter)
|
||||
|
||||
filter_result = case
|
||||
when filter.is_a?(Symbol)
|
||||
self.send(filter)
|
||||
when filter_block?(filter)
|
||||
filter.call(self)
|
||||
when filter_class?(filter)
|
||||
filter.filter(self)
|
||||
else
|
||||
raise(
|
||||
ActionControllerError,
|
||||
'Filters need to be either a symbol, proc/method, or class implementing a static filter method'
|
||||
)
|
||||
end
|
||||
|
||||
if filter_result == false
|
||||
logger.info "Filter chain halted as [#{filter}] returned false" if logger
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def filter_block?(filter)
|
||||
filter.respond_to?('call') && (filter.arity == 1 || filter.arity == -1)
|
||||
end
|
||||
|
||||
def filter_class?(filter)
|
||||
filter.respond_to?('filter')
|
||||
end
|
||||
|
||||
def action_exempted?(filter)
|
||||
case
|
||||
when ia = self.class.included_actions[filter]
|
||||
!ia.include?(action_name)
|
||||
when ea = self.class.excluded_actions[filter]
|
||||
ea.include?(action_name)
|
||||
end
|
||||
end
|
||||
|
||||
def process_cleanup_with_filters
|
||||
if @before_filter_chain_aborted
|
||||
close_session
|
||||
else
|
||||
process_cleanup_without_filters
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
178
vendor/rails/actionpack/lib/action_controller/flash.rb
vendored
Normal file
178
vendor/rails/actionpack/lib/action_controller/flash.rb
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
module ActionController #:nodoc:
|
||||
# The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
|
||||
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create action
|
||||
# that sets <tt>flash[:notice] = "Successfully created"</tt> before redirecting to a display action that can then expose
|
||||
# the flash to its template. Actually, that exposure is automatically done. Example:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# def create
|
||||
# # save post
|
||||
# flash[:notice] = "Successfully created post"
|
||||
# redirect_to :action => "display", :params => { :id => post.id }
|
||||
# end
|
||||
#
|
||||
# def display
|
||||
# # doesn't need to assign the flash notice to the template, that's done automatically
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# display.rhtml
|
||||
# <% if @flash[:notice] %><div class="notice"><%= @flash[:notice] %></div><% end %>
|
||||
#
|
||||
# This example just places a string in the flash, but you can put any object in there. And of course, you can put as many
|
||||
# as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
|
||||
#
|
||||
# See docs on the FlashHash class for more details about the flash.
|
||||
module Flash
|
||||
def self.included(base)
|
||||
base.send :include, InstanceMethods
|
||||
|
||||
base.class_eval do
|
||||
alias_method :assign_shortcuts_without_flash, :assign_shortcuts
|
||||
alias_method :assign_shortcuts, :assign_shortcuts_with_flash
|
||||
|
||||
alias_method :process_cleanup_without_flash, :process_cleanup
|
||||
alias_method :process_cleanup, :process_cleanup_with_flash
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class FlashNow #:nodoc:
|
||||
def initialize(flash)
|
||||
@flash = flash
|
||||
end
|
||||
|
||||
def []=(k, v)
|
||||
@flash[k] = v
|
||||
@flash.discard(k)
|
||||
v
|
||||
end
|
||||
|
||||
def [](k)
|
||||
@flash[k]
|
||||
end
|
||||
end
|
||||
|
||||
class FlashHash < Hash
|
||||
def initialize #:nodoc:
|
||||
super
|
||||
@used = {}
|
||||
end
|
||||
|
||||
def []=(k, v) #:nodoc:
|
||||
keep(k)
|
||||
super
|
||||
end
|
||||
|
||||
def update(h) #:nodoc:
|
||||
h.keys.each{ |k| discard(k) }
|
||||
super
|
||||
end
|
||||
|
||||
alias :merge! :update
|
||||
|
||||
def replace(h) #:nodoc:
|
||||
@used = {}
|
||||
super
|
||||
end
|
||||
|
||||
# Sets a flash that will not be available to the next action, only to the current.
|
||||
#
|
||||
# flash.now[:message] = "Hello current action"
|
||||
#
|
||||
# This method enables you to use the flash as a central messaging system in your app.
|
||||
# When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
|
||||
# When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
|
||||
# vanish when the current action is done.
|
||||
#
|
||||
# Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
|
||||
def now
|
||||
FlashNow.new self
|
||||
end
|
||||
|
||||
# Keeps either the entire current flash or a specific flash entry available for the next action:
|
||||
#
|
||||
# flash.keep # keeps the entire flash
|
||||
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
|
||||
def keep(k=nil)
|
||||
use(k, false)
|
||||
end
|
||||
|
||||
# Marks the entire flash or a single flash entry to be discarded by the end of the current action
|
||||
#
|
||||
# flash.keep # keep entire flash available for the next action
|
||||
# flash.discard(:warning) # discard the "warning" entry (it'll still be available for the current action)
|
||||
def discard(k=nil)
|
||||
use(k)
|
||||
end
|
||||
|
||||
# Mark for removal entries that were kept, and delete unkept ones.
|
||||
#
|
||||
# This method is called automatically by filters, so you generally don't need to care about it.
|
||||
def sweep #:nodoc:
|
||||
keys.each do |k|
|
||||
unless @used[k]
|
||||
use(k)
|
||||
else
|
||||
delete(k)
|
||||
@used.delete(k)
|
||||
end
|
||||
end
|
||||
(@used.keys - keys).each{|k| @used.delete k } # clean up after keys that could have been left over by calling reject! or shift on the flash
|
||||
end
|
||||
|
||||
private
|
||||
# Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
|
||||
# use() # marks the entire flash as used
|
||||
# use('msg') # marks the "msg" entry as used
|
||||
# use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
|
||||
# use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
|
||||
def use(k=nil, v=true)
|
||||
unless k.nil?
|
||||
@used[k] = v
|
||||
else
|
||||
keys.each{|key| use key, v }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods #:nodoc:
|
||||
def assign_shortcuts_with_flash(request, response) #:nodoc:
|
||||
assign_shortcuts_without_flash(request, response)
|
||||
flash(:refresh)
|
||||
end
|
||||
|
||||
def process_cleanup_with_flash
|
||||
flash.sweep if @session
|
||||
process_cleanup_without_flash
|
||||
end
|
||||
|
||||
protected
|
||||
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to read a notice you put there or
|
||||
# <tt>flash["notice"] = "hello"</tt> to put a new one.
|
||||
# Note that if sessions are disabled only flash.now will work.
|
||||
def flash(refresh = false) #:doc:
|
||||
if @flash.nil? || refresh
|
||||
@flash =
|
||||
if @session.is_a?(Hash)
|
||||
# @session is a Hash, if sessions are disabled
|
||||
# we don't put the flash in the session in this case
|
||||
FlashHash.new
|
||||
else
|
||||
# otherwise, @session is a CGI::Session or a TestSession
|
||||
# so make sure it gets retrieved from/saved to session storage after request processing
|
||||
@session["flash"] ||= FlashHash.new
|
||||
end
|
||||
end
|
||||
|
||||
@flash
|
||||
end
|
||||
|
||||
# deprecated. use <tt>flash.keep</tt> instead
|
||||
def keep_flash #:doc:
|
||||
warn 'keep_flash is deprecated; use flash.keep instead.'
|
||||
flash.keep
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
134
vendor/rails/actionpack/lib/action_controller/helpers.rb
vendored
Normal file
134
vendor/rails/actionpack/lib/action_controller/helpers.rb
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
module ActionController #:nodoc:
|
||||
module Helpers #:nodoc:
|
||||
def self.append_features(base)
|
||||
super
|
||||
|
||||
# Initialize the base module to aggregate its helpers.
|
||||
base.class_inheritable_accessor :master_helper_module
|
||||
base.master_helper_module = Module.new
|
||||
|
||||
# Extend base with class methods to declare helpers.
|
||||
base.extend(ClassMethods)
|
||||
|
||||
base.class_eval do
|
||||
# Wrap inherited to create a new master helper module for subclasses.
|
||||
class << self
|
||||
alias_method :inherited_without_helper, :inherited
|
||||
alias_method :inherited, :inherited_with_helper
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The template helpers serve to relieve the templates from including the same inline code again and again. It's a
|
||||
# set of standardized methods for working with forms (FormHelper), dates (DateHelper), texts (TextHelper), and
|
||||
# Active Records (ActiveRecordHelper) that's available to all templates by default.
|
||||
#
|
||||
# It's also really easy to make your own helpers and it's much encouraged to keep the template files free
|
||||
# from complicated logic. It's even encouraged to bundle common compositions of methods from other helpers
|
||||
# (often the common helpers) as they're used by the specific application.
|
||||
#
|
||||
# module MyHelper
|
||||
# def hello_world() "hello world" end
|
||||
# end
|
||||
#
|
||||
# MyHelper can now be included in a controller, like this:
|
||||
#
|
||||
# class MyController < ActionController::Base
|
||||
# helper :my_helper
|
||||
# end
|
||||
#
|
||||
# ...and, same as above, used in any template rendered from MyController, like this:
|
||||
#
|
||||
# Let's hear what the helper has to say: <tt><%= hello_world %></tt>
|
||||
module ClassMethods
|
||||
# Makes all the (instance) methods in the helper module available to templates rendered through this controller.
|
||||
# See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
|
||||
# available to the templates.
|
||||
def add_template_helper(helper_module) #:nodoc:
|
||||
master_helper_module.send(:include, helper_module)
|
||||
end
|
||||
|
||||
# Declare a helper:
|
||||
# helper :foo
|
||||
# requires 'foo_helper' and includes FooHelper in the template class.
|
||||
# helper FooHelper
|
||||
# includes FooHelper in the template class.
|
||||
# helper { def foo() "#{bar} is the very best" end }
|
||||
# evaluates the block in the template class, adding method #foo.
|
||||
# helper(:three, BlindHelper) { def mice() 'mice' end }
|
||||
# does all three.
|
||||
def helper(*args, &block)
|
||||
args.flatten.each do |arg|
|
||||
case arg
|
||||
when Module
|
||||
add_template_helper(arg)
|
||||
when String, Symbol
|
||||
file_name = arg.to_s.underscore + '_helper'
|
||||
class_name = file_name.camelize
|
||||
|
||||
begin
|
||||
require_dependency(file_name)
|
||||
rescue LoadError => load_error
|
||||
requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
|
||||
msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
|
||||
raise LoadError.new(msg).copy_blame!(load_error)
|
||||
end
|
||||
|
||||
add_template_helper(class_name.constantize)
|
||||
else
|
||||
raise ArgumentError, 'helper expects String, Symbol, or Module argument'
|
||||
end
|
||||
end
|
||||
|
||||
# Evaluate block in template class if given.
|
||||
master_helper_module.module_eval(&block) if block_given?
|
||||
end
|
||||
|
||||
# Declare a controller method as a helper. For example,
|
||||
# helper_method :link_to
|
||||
# def link_to(name, options) ... end
|
||||
# makes the link_to controller method available in the view.
|
||||
def helper_method(*methods)
|
||||
methods.flatten.each do |method|
|
||||
master_helper_module.module_eval <<-end_eval
|
||||
def #{method}(*args, &block)
|
||||
controller.send(%(#{method}), *args, &block)
|
||||
end
|
||||
end_eval
|
||||
end
|
||||
end
|
||||
|
||||
# Declare a controller attribute as a helper. For example,
|
||||
# helper_attr :name
|
||||
# attr_accessor :name
|
||||
# makes the name and name= controller methods available in the view.
|
||||
# The is a convenience wrapper for helper_method.
|
||||
def helper_attr(*attrs)
|
||||
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
|
||||
end
|
||||
|
||||
private
|
||||
def default_helper_module!
|
||||
module_name = name.sub(/Controller$|$/, 'Helper')
|
||||
module_path = module_name.split('::').map { |m| m.underscore }.join('/')
|
||||
require_dependency module_path
|
||||
helper module_name.constantize
|
||||
rescue LoadError
|
||||
logger.debug("#{name}: missing default helper path #{module_path}") if logger
|
||||
rescue NameError
|
||||
logger.debug("#{name}: missing default helper module #{module_name}") if logger
|
||||
end
|
||||
|
||||
def inherited_with_helper(child)
|
||||
inherited_without_helper(child)
|
||||
begin
|
||||
child.master_helper_module = Module.new
|
||||
child.master_helper_module.send :include, master_helper_module
|
||||
child.send :default_helper_module!
|
||||
rescue MissingSourceFile => e
|
||||
raise unless e.is_missing?("helpers/#{child.controller_path}_helper")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
528
vendor/rails/actionpack/lib/action_controller/integration.rb
vendored
Normal file
528
vendor/rails/actionpack/lib/action_controller/integration.rb
vendored
Normal file
|
@ -0,0 +1,528 @@
|
|||
require 'dispatcher'
|
||||
require 'stringio'
|
||||
require 'uri'
|
||||
|
||||
module ActionController
|
||||
module Integration #:nodoc:
|
||||
# An integration Session instance represents a set of requests and responses
|
||||
# performed sequentially by some virtual user. Becase you can instantiate
|
||||
# multiple sessions and run them side-by-side, you can also mimic (to some
|
||||
# limited extent) multiple simultaneous users interacting with your system.
|
||||
#
|
||||
# Typically, you will instantiate a new session using IntegrationTest#open_session,
|
||||
# rather than instantiating Integration::Session directly.
|
||||
class Session
|
||||
include Test::Unit::Assertions
|
||||
include ActionController::TestProcess
|
||||
|
||||
# The integer HTTP status code of the last request.
|
||||
attr_reader :status
|
||||
|
||||
# The status message that accompanied the status code of the last request.
|
||||
attr_reader :status_message
|
||||
|
||||
# The URI of the last request.
|
||||
attr_reader :path
|
||||
|
||||
# The hostname used in the last request.
|
||||
attr_accessor :host
|
||||
|
||||
# The remote_addr used in the last request.
|
||||
attr_accessor :remote_addr
|
||||
|
||||
# The Accept header to send.
|
||||
attr_accessor :accept
|
||||
|
||||
# A map of the cookies returned by the last response, and which will be
|
||||
# sent with the next request.
|
||||
attr_reader :cookies
|
||||
|
||||
# A map of the headers returned by the last response.
|
||||
attr_reader :headers
|
||||
|
||||
# A reference to the controller instance used by the last request.
|
||||
attr_reader :controller
|
||||
|
||||
# A reference to the request instance used by the last request.
|
||||
attr_reader :request
|
||||
|
||||
# A reference to the response instance used by the last request.
|
||||
attr_reader :response
|
||||
|
||||
# Create an initialize a new Session instance.
|
||||
def initialize
|
||||
reset!
|
||||
end
|
||||
|
||||
# Resets the instance. This can be used to reset the state information
|
||||
# in an existing session instance, so it can be used from a clean-slate
|
||||
# condition.
|
||||
#
|
||||
# session.reset!
|
||||
def reset!
|
||||
@status = @path = @headers = nil
|
||||
@result = @status_message = nil
|
||||
@https = false
|
||||
@cookies = {}
|
||||
@controller = @request = @response = nil
|
||||
|
||||
self.host = "www.example.com"
|
||||
self.remote_addr = "127.0.0.1"
|
||||
self.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
|
||||
|
||||
unless @named_routes_configured
|
||||
# install the named routes in this session instance.
|
||||
klass = class<<self; self; end
|
||||
Routing::NamedRoutes.install(klass)
|
||||
|
||||
# the helpers are made protected by default--we make them public for
|
||||
# easier access during testing and troubleshooting.
|
||||
klass.send(:public, *Routing::NamedRoutes::Helpers)
|
||||
@named_routes_configured = true
|
||||
end
|
||||
end
|
||||
|
||||
# Specify whether or not the session should mimic a secure HTTPS request.
|
||||
#
|
||||
# session.https!
|
||||
# session.https!(false)
|
||||
def https!(flag=true)
|
||||
@https = flag
|
||||
end
|
||||
|
||||
# Return +true+ if the session is mimicing a secure HTTPS request.
|
||||
#
|
||||
# if session.https?
|
||||
# ...
|
||||
# end
|
||||
def https?
|
||||
@https
|
||||
end
|
||||
|
||||
# Set the host name to use in the next request.
|
||||
#
|
||||
# session.host! "www.example.com"
|
||||
def host!(name)
|
||||
@host = name
|
||||
end
|
||||
|
||||
# Follow a single redirect response. If the last response was not a
|
||||
# redirect, an exception will be raised. Otherwise, the redirect is
|
||||
# performed on the location header.
|
||||
def follow_redirect!
|
||||
raise "not a redirect! #{@status} #{@status_message}" unless redirect?
|
||||
get(interpret_uri(headers["location"].first))
|
||||
status
|
||||
end
|
||||
|
||||
# Performs a GET request, following any subsequent redirect. Note that
|
||||
# the redirects are followed until the response is not a redirect--this
|
||||
# means you may run into an infinite loop if your redirect loops back to
|
||||
# itself.
|
||||
def get_via_redirect(path, args={})
|
||||
get path, args
|
||||
follow_redirect! while redirect?
|
||||
status
|
||||
end
|
||||
|
||||
# Performs a POST request, following any subsequent redirect. This is
|
||||
# vulnerable to infinite loops, the same as #get_via_redirect.
|
||||
def post_via_redirect(path, args={})
|
||||
post path, args
|
||||
follow_redirect! while redirect?
|
||||
status
|
||||
end
|
||||
|
||||
# Returns +true+ if the last response was a redirect.
|
||||
def redirect?
|
||||
status/100 == 3
|
||||
end
|
||||
|
||||
# Performs a GET request with the given parameters. The parameters may
|
||||
# be +nil+, a Hash, or a string that is appropriately encoded
|
||||
# (application/x-www-form-urlencoded or multipart/form-data). The headers
|
||||
# should be a hash. The keys will automatically be upcased, with the
|
||||
# prefix 'HTTP_' added if needed.
|
||||
def get(path, parameters=nil, headers=nil)
|
||||
process :get, path, parameters, headers
|
||||
end
|
||||
|
||||
# Performs a POST request with the given parameters. The parameters may
|
||||
# be +nil+, a Hash, or a string that is appropriately encoded
|
||||
# (application/x-www-form-urlencoded or multipart/form-data). The headers
|
||||
# should be a hash. The keys will automatically be upcased, with the
|
||||
# prefix 'HTTP_' added if needed.
|
||||
def post(path, parameters=nil, headers=nil)
|
||||
process :post, path, parameters, headers
|
||||
end
|
||||
|
||||
# Performs an XMLHttpRequest request with the given parameters, mimicing
|
||||
# the request environment created by the Prototype library. The parameters
|
||||
# may be +nil+, a Hash, or a string that is appropriately encoded
|
||||
# (application/x-www-form-urlencoded or multipart/form-data). The headers
|
||||
# should be a hash. The keys will automatically be upcased, with the
|
||||
# prefix 'HTTP_' added if needed.
|
||||
def xml_http_request(path, parameters=nil, headers=nil)
|
||||
headers = (headers || {}).merge("X-Requested-With" => "XMLHttpRequest")
|
||||
post(path, parameters, headers)
|
||||
end
|
||||
|
||||
# Returns the URL for the given options, according to the rules specified
|
||||
# in the application's routes.
|
||||
def url_for(options)
|
||||
controller ? controller.url_for(options) : generic_url_rewriter.rewrite(options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
class MockCGI < CGI #:nodoc:
|
||||
attr_accessor :stdinput, :stdoutput, :env_table
|
||||
|
||||
def initialize(env, input=nil)
|
||||
self.env_table = env
|
||||
self.stdinput = StringIO.new(input || "")
|
||||
self.stdoutput = StringIO.new
|
||||
|
||||
super()
|
||||
end
|
||||
end
|
||||
|
||||
# Tailors the session based on the given URI, setting the HTTPS value
|
||||
# and the hostname.
|
||||
def interpret_uri(path)
|
||||
location = URI.parse(path)
|
||||
https! URI::HTTPS === location if location.scheme
|
||||
host! location.host if location.host
|
||||
location.query ? "#{location.path}?#{location.query}" : location.path
|
||||
end
|
||||
|
||||
# Performs the actual request.
|
||||
def process(method, path, parameters=nil, headers=nil)
|
||||
data = requestify(parameters)
|
||||
path = interpret_uri(path) if path =~ %r{://}
|
||||
path = "/#{path}" unless path[0] == ?/
|
||||
@path = path
|
||||
env = {}
|
||||
|
||||
if method == :get
|
||||
env["QUERY_STRING"] = data
|
||||
data = nil
|
||||
end
|
||||
|
||||
env.update(
|
||||
"REQUEST_METHOD" => method.to_s.upcase,
|
||||
"REQUEST_URI" => path,
|
||||
"HTTP_HOST" => host,
|
||||
"REMOTE_ADDR" => remote_addr,
|
||||
"SERVER_PORT" => (https? ? "443" : "80"),
|
||||
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
||||
"CONTENT_LENGTH" => data ? data.length.to_s : nil,
|
||||
"HTTP_COOKIE" => encode_cookies,
|
||||
"HTTPS" => https? ? "on" : "off",
|
||||
"HTTP_ACCEPT" => accept
|
||||
)
|
||||
|
||||
(headers || {}).each do |key, value|
|
||||
key = key.to_s.upcase.gsub(/-/, "_")
|
||||
key = "HTTP_#{key}" unless env.has_key?(key) || env =~ /^X|HTTP/
|
||||
env[key] = value
|
||||
end
|
||||
|
||||
unless ActionController::Base.respond_to?(:clear_last_instantiation!)
|
||||
ActionController::Base.send(:include, ControllerCapture)
|
||||
end
|
||||
|
||||
ActionController::Base.clear_last_instantiation!
|
||||
|
||||
cgi = MockCGI.new(env, data)
|
||||
Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, cgi.stdoutput)
|
||||
@result = cgi.stdoutput.string
|
||||
|
||||
@controller = ActionController::Base.last_instantiation
|
||||
@request = @controller.request
|
||||
@response = @controller.response
|
||||
|
||||
# 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)
|
||||
|
||||
parse_result
|
||||
return status
|
||||
end
|
||||
|
||||
# Parses the result of the response and extracts the various values,
|
||||
# like cookies, status, headers, etc.
|
||||
def parse_result
|
||||
headers, result_body = @result.split(/\r\n\r\n/, 2)
|
||||
|
||||
@headers = Hash.new { |h,k| h[k] = [] }
|
||||
headers.each_line do |line|
|
||||
key, value = line.strip.split(/:\s*/, 2)
|
||||
@headers[key.downcase] << value
|
||||
end
|
||||
|
||||
(@headers['set-cookie'] || [] ).each do |string|
|
||||
name, value = string.match(/^(.*?)=(.*?);/)[1,2]
|
||||
@cookies[name] = value
|
||||
end
|
||||
|
||||
@status, @status_message = @headers["status"].first.split(/ /)
|
||||
@status = @status.to_i
|
||||
end
|
||||
|
||||
# Encode the cookies hash in a format suitable for passing to a
|
||||
# request.
|
||||
def encode_cookies
|
||||
cookies.inject("") do |string, (name, value)|
|
||||
string << "#{name}=#{value}; "
|
||||
end
|
||||
end
|
||||
|
||||
# Get a temporarly URL writer object
|
||||
def generic_url_rewriter
|
||||
cgi = MockCGI.new('REQUEST_METHOD' => "GET",
|
||||
'QUERY_STRING' => "",
|
||||
"REQUEST_URI" => "/",
|
||||
"HTTP_HOST" => host,
|
||||
"SERVER_PORT" => https? ? "443" : "80",
|
||||
"HTTPS" => https? ? "on" : "off")
|
||||
ActionController::UrlRewriter.new(ActionController::CgiRequest.new(cgi), {})
|
||||
end
|
||||
|
||||
def name_with_prefix(prefix, name)
|
||||
prefix ? "#{prefix}[#{name}]" : name.to_s
|
||||
end
|
||||
|
||||
# Convert the given parameters to a request string. The parameters may
|
||||
# be a string, +nil+, or a Hash.
|
||||
def requestify(parameters, prefix=nil)
|
||||
if Hash === parameters
|
||||
return nil if parameters.empty?
|
||||
parameters.map { |k,v| requestify(v, name_with_prefix(prefix, k)) }.join("&")
|
||||
elsif Array === parameters
|
||||
parameters.map { |v| requestify(v, name_with_prefix(prefix, "")) }.join("&")
|
||||
elsif prefix.nil?
|
||||
parameters
|
||||
else
|
||||
"#{CGI.escape(prefix)}=#{CGI.escape(parameters.to_s)}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# 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 :new_without_capture, :new
|
||||
alias_method :new, :new_with_capture
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods #:nodoc:
|
||||
mattr_accessor :last_instantiation
|
||||
|
||||
def clear_last_instantiation!
|
||||
self.last_instantiation = nil
|
||||
end
|
||||
|
||||
def new_with_capture(*args)
|
||||
self.last_instantiation ||= new_without_capture(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# An IntegrationTest is one that spans multiple controllers and actions,
|
||||
# tying them all together to ensure they work together as expected. It tests
|
||||
# more completely than either unit or functional tests do, exercising the
|
||||
# entire stack, from the dispatcher to the database.
|
||||
#
|
||||
# At its simplest, you simply extend IntegrationTest and write your tests
|
||||
# using the get/post methods:
|
||||
#
|
||||
# require "#{File.dirname(__FILE__)}/test_helper"
|
||||
#
|
||||
# class ExampleTest < ActionController::IntegrationTest
|
||||
# fixtures :people
|
||||
#
|
||||
# def test_login
|
||||
# # get the login page
|
||||
# get "/login"
|
||||
# assert_equal 200, status
|
||||
#
|
||||
# # post the login and follow through to the home page
|
||||
# post "/login", :username => people(:jamis).username,
|
||||
# :password => people(:jamis).password
|
||||
# follow_redirect!
|
||||
# assert_equal 200, status
|
||||
# assert_equal "/home", path
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# However, you can also have multiple session instances open per test, and
|
||||
# even extend those instances with assertions and methods to create a very
|
||||
# powerful testing DSL that is specific for your application. You can even
|
||||
# reference any named routes you happen to have defined!
|
||||
#
|
||||
# require "#{File.dirname(__FILE__)}/test_helper"
|
||||
#
|
||||
# class AdvancedTest < ActionController::IntegrationTest
|
||||
# fixtures :people, :rooms
|
||||
#
|
||||
# def test_login_and_speak
|
||||
# jamis, david = login(:jamis), login(:david)
|
||||
# room = rooms(:office)
|
||||
#
|
||||
# jamis.enter(room)
|
||||
# jamis.speak(room, "anybody home?")
|
||||
#
|
||||
# david.enter(room)
|
||||
# david.speak(room, "hello!")
|
||||
# end
|
||||
#
|
||||
# private
|
||||
#
|
||||
# module CustomAssertions
|
||||
# def enter(room)
|
||||
# # reference a named route, for maximum internal consistency!
|
||||
# get(room_url(:id => room.id))
|
||||
# assert(...)
|
||||
# ...
|
||||
# end
|
||||
#
|
||||
# def speak(room, message)
|
||||
# xml_http_request "/say/#{room.id}", :message => message
|
||||
# assert(...)
|
||||
# ...
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def login(who)
|
||||
# open_session do |sess|
|
||||
# sess.extend(CustomAssertions)
|
||||
# who = people(who)
|
||||
# sess.post "/login", :username => who.username,
|
||||
# :password => who.password
|
||||
# assert(...)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
class IntegrationTest < Test::Unit::TestCase
|
||||
# Work around a bug in test/unit caused by the default test being named
|
||||
# as a symbol (:default_test), which causes regex test filters
|
||||
# (like "ruby test.rb -n /foo/") to fail because =~ doesn't work on
|
||||
# symbols.
|
||||
def initialize(name) #:nodoc:
|
||||
super(name.to_s)
|
||||
end
|
||||
|
||||
# Work around test/unit's requirement that every subclass of TestCase have
|
||||
# at least one test method. Note that this implementation extends to all
|
||||
# subclasses, as well, so subclasses of IntegrationTest may also exist
|
||||
# without any test methods.
|
||||
def run(*args) #:nodoc:
|
||||
return if @method_name == "default_test"
|
||||
super
|
||||
end
|
||||
|
||||
# Because of how use_instantiated_fixtures and use_transactional_fixtures
|
||||
# are defined, we need to treat them as special cases. Otherwise, users
|
||||
# would potentially have to set their values for both Test::Unit::TestCase
|
||||
# ActionController::IntegrationTest, since by the time the value is set on
|
||||
# TestCase, IntegrationTest has already been defined and cannot inherit
|
||||
# changes to those variables. So, we make those two attributes copy-on-write.
|
||||
|
||||
class << self
|
||||
def use_transactional_fixtures=(flag) #:nodoc:
|
||||
@_use_transactional_fixtures = true
|
||||
@use_transactional_fixtures = flag
|
||||
end
|
||||
|
||||
def use_instantiated_fixtures=(flag) #:nodoc:
|
||||
@_use_instantiated_fixtures = true
|
||||
@use_instantiated_fixtures = flag
|
||||
end
|
||||
|
||||
def use_transactional_fixtures #:nodoc:
|
||||
@_use_transactional_fixtures ?
|
||||
@use_transactional_fixtures :
|
||||
superclass.use_transactional_fixtures
|
||||
end
|
||||
|
||||
def use_instantiated_fixtures #:nodoc:
|
||||
@_use_instantiated_fixtures ?
|
||||
@use_instantiated_fixtures :
|
||||
superclass.use_instantiated_fixtures
|
||||
end
|
||||
end
|
||||
|
||||
# Reset the current session. This is useful for testing multiple sessions
|
||||
# in a single test case.
|
||||
def reset!
|
||||
@integration_session = open_session
|
||||
end
|
||||
|
||||
%w(get post cookies assigns xml_http_request).each do |method|
|
||||
define_method(method) do |*args|
|
||||
reset! unless @integration_session
|
||||
returning @integration_session.send(method, *args) do
|
||||
copy_session_variables!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Open a new session instance. If a block is given, the new session is
|
||||
# yielded to the block before being returned.
|
||||
#
|
||||
# session = open_session do |sess|
|
||||
# sess.extend(CustomAssertions)
|
||||
# end
|
||||
#
|
||||
# By default, a single session is automatically created for you, but you
|
||||
# can use this method to open multiple sessions that ought to be tested
|
||||
# simultaneously.
|
||||
def open_session
|
||||
session = Integration::Session.new
|
||||
|
||||
# delegate the fixture accessors back to the test instance
|
||||
extras = Module.new { attr_accessor :delegate, :test_result }
|
||||
self.class.fixture_table_names.each do |table_name|
|
||||
name = table_name.tr(".", "_")
|
||||
next unless respond_to?(name)
|
||||
extras.send(:define_method, name) { |*args| delegate.send(name, *args) }
|
||||
end
|
||||
|
||||
# delegate add_assertion to the test case
|
||||
extras.send(:define_method, :add_assertion) { test_result.add_assertion }
|
||||
session.extend(extras)
|
||||
session.delegate = self
|
||||
session.test_result = @_result
|
||||
|
||||
yield session if block_given?
|
||||
session
|
||||
end
|
||||
|
||||
# Copy the instance variables from the current session instance into the
|
||||
# test instance.
|
||||
def copy_session_variables! #:nodoc:
|
||||
return unless @integration_session
|
||||
%w(controller response request).each do |var|
|
||||
instance_variable_set("@#{var}", @integration_session.send(var))
|
||||
end
|
||||
end
|
||||
|
||||
# Delegate unhandled messages to the current session instance.
|
||||
def method_missing(sym, *args, &block)
|
||||
reset! unless @integration_session
|
||||
returning @integration_session.send(sym, *args, &block) do
|
||||
copy_session_variables!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
310
vendor/rails/actionpack/lib/action_controller/layout.rb
vendored
Normal file
310
vendor/rails/actionpack/lib/action_controller/layout.rb
vendored
Normal file
|
@ -0,0 +1,310 @@
|
|||
module ActionController #:nodoc:
|
||||
module Layout #:nodoc:
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
base.class_eval do
|
||||
alias_method :render_with_no_layout, :render
|
||||
alias_method :render, :render_with_a_layout
|
||||
|
||||
class << self
|
||||
alias_method :inherited_without_layout, :inherited
|
||||
alias_method :inherited, :inherited_with_layout
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
|
||||
# repeated setups. The inclusion pattern has pages that look like this:
|
||||
#
|
||||
# <%= render "shared/header" %>
|
||||
# Hello World
|
||||
# <%= render "shared/footer" %>
|
||||
#
|
||||
# This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
|
||||
# and if you ever want to change the structure of these two includes, you'll have to change all the templates.
|
||||
#
|
||||
# With layouts, you can flip it around and have the common structure know where to insert changing content. This means
|
||||
# that the header and footer are only mentioned in one place, like this:
|
||||
#
|
||||
# <!-- The header part of this layout -->
|
||||
# <%= yield %>
|
||||
# <!-- The footer part of this layout -->
|
||||
#
|
||||
# And then you have content pages that look like this:
|
||||
#
|
||||
# hello world
|
||||
#
|
||||
# Not a word about common structures. At rendering time, the content page is computed and then inserted in the layout,
|
||||
# like this:
|
||||
#
|
||||
# <!-- The header part of this layout -->
|
||||
# hello world
|
||||
# <!-- The footer part of this layout -->
|
||||
#
|
||||
# == Accessing shared variables
|
||||
#
|
||||
# Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
|
||||
# references that won't materialize before rendering time:
|
||||
#
|
||||
# <h1><%= @page_title %></h1>
|
||||
# <%= yield %>
|
||||
#
|
||||
# ...and content pages that fulfill these references _at_ rendering time:
|
||||
#
|
||||
# <% @page_title = "Welcome" %>
|
||||
# Off-world colonies offers you a chance to start a new life
|
||||
#
|
||||
# The result after rendering is:
|
||||
#
|
||||
# <h1>Welcome</h1>
|
||||
# Off-world colonies offers you a chance to start a new life
|
||||
#
|
||||
# == Automatic layout assignment
|
||||
#
|
||||
# If there is a template in <tt>app/views/layouts/</tt> with the same name as the current controller then it will be automatically
|
||||
# set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
|
||||
# <tt>app/views/layouts/weblog.rhtml</tt> or <tt>app/views/layouts/weblog.rxml</tt> exists then it will be automatically set as
|
||||
# the layout for your WeblogController. You can create a layout with the name <tt>application.rhtml</tt> or <tt>application.rxml</tt>
|
||||
# and this will be set as the default controller if there is no layout with the same name as the current controller and there is
|
||||
# no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout.
|
||||
# assignment. So an Admin::WeblogController will look for a template named <tt>app/views/layouts/admin/weblog.rhtml</tt>.
|
||||
# Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set.
|
||||
# Explicitly setting the layout in a parent class, though, will not override the child class's layout assignement if the child
|
||||
# class has a layout with the same name.
|
||||
#
|
||||
# == Inheritance for layouts
|
||||
#
|
||||
# Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples:
|
||||
#
|
||||
# class BankController < ActionController::Base
|
||||
# layout "bank_standard"
|
||||
#
|
||||
# class InformationController < BankController
|
||||
#
|
||||
# class VaultController < BankController
|
||||
# layout :access_level_layout
|
||||
#
|
||||
# class EmployeeController < BankController
|
||||
# layout nil
|
||||
#
|
||||
# The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
|
||||
# and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
|
||||
#
|
||||
# == Types of layouts
|
||||
#
|
||||
# Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
|
||||
# you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
|
||||
# be done either by specifying a method reference as a symbol or using an inline method (as a proc).
|
||||
#
|
||||
# The method reference is the preferred approach to variable layouts and is used like this:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# layout :writers_and_readers
|
||||
#
|
||||
# def index
|
||||
# # fetching posts
|
||||
# end
|
||||
#
|
||||
# private
|
||||
# def writers_and_readers
|
||||
# logged_in? ? "writer_layout" : "reader_layout"
|
||||
# end
|
||||
#
|
||||
# Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
|
||||
# is logged in or not.
|
||||
#
|
||||
# If you want to use an inline method, such as a proc, do something like this:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
|
||||
#
|
||||
# Of course, the most common way of specifying a layout is still just as a plain template name:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# layout "weblog_standard"
|
||||
#
|
||||
# If no directory is specified for the template name, the template will by default by looked for in +app/views/layouts/+.
|
||||
#
|
||||
# == Conditional layouts
|
||||
#
|
||||
# If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
|
||||
# a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
|
||||
# <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# layout "weblog_standard", :except => :rss
|
||||
#
|
||||
# # ...
|
||||
#
|
||||
# end
|
||||
#
|
||||
# This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
|
||||
# around the rendered view.
|
||||
#
|
||||
# Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
|
||||
# #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
|
||||
#
|
||||
# == Using a different layout in the action render call
|
||||
#
|
||||
# If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
|
||||
# Some times you'll have exceptions, though, where one action wants to use a different layout than the rest of the controller.
|
||||
# This is possible using the <tt>render</tt> method. It's just a bit more manual work as you'll have to supply fully
|
||||
# qualified template and layout names as this example shows:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# def help
|
||||
# render :action => "help/index", :layout => "help"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# As you can see, you pass the template as the first parameter, the status code as the second ("200" is OK), and the layout
|
||||
# as the third.
|
||||
#
|
||||
# NOTE: The old notation for rendering the view from a layout was to expose the magic <tt>@content_for_layout</tt> instance
|
||||
# variable. The preferred notation now is to use <tt>yield</tt>, as documented above.
|
||||
module ClassMethods
|
||||
# If a layout is specified, all rendered actions will have their result rendered
|
||||
# when the layout<tt>yield</tt>'s. This layout can itself depend on instance variables assigned during action
|
||||
# performance and have access to them as any normal template would.
|
||||
def layout(template_name, conditions = {})
|
||||
add_layout_conditions(conditions)
|
||||
write_inheritable_attribute "layout", template_name
|
||||
end
|
||||
|
||||
def layout_conditions #:nodoc:
|
||||
@layout_conditions ||= read_inheritable_attribute("layout_conditions")
|
||||
end
|
||||
|
||||
def default_layout #:nodoc:
|
||||
@default_layout ||= read_inheritable_attribute("layout")
|
||||
end
|
||||
|
||||
private
|
||||
def inherited_with_layout(child)
|
||||
inherited_without_layout(child)
|
||||
child.send :include, Reloadable
|
||||
layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
|
||||
child.layout(layout_match) unless layout_list.grep(%r{layouts/#{layout_match}\.[a-z][0-9a-z]*$}).empty?
|
||||
end
|
||||
|
||||
def layout_list
|
||||
Dir.glob("#{template_root}/layouts/**/*")
|
||||
end
|
||||
|
||||
def add_layout_conditions(conditions)
|
||||
write_inheritable_hash "layout_conditions", normalize_conditions(conditions)
|
||||
end
|
||||
|
||||
def normalize_conditions(conditions)
|
||||
conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
|
||||
end
|
||||
|
||||
def layout_directory_exists_cache
|
||||
@@layout_directory_exists_cache ||= Hash.new do |h, dirname|
|
||||
h[dirname] = File.directory? dirname
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
|
||||
# is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
|
||||
# object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
|
||||
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
|
||||
def active_layout(passed_layout = nil)
|
||||
layout = passed_layout || self.class.default_layout
|
||||
|
||||
active_layout = case layout
|
||||
when String then layout
|
||||
when Symbol then send(layout)
|
||||
when Proc then layout.call(self)
|
||||
end
|
||||
|
||||
# Explicitly passed layout names with slashes are looked up relative to the template root,
|
||||
# but auto-discovered layouts derived from a nested controller will contain a slash, though be relative
|
||||
# to the 'layouts' directory so we have to check the file system to infer which case the layout name came from.
|
||||
if active_layout
|
||||
if active_layout.include?('/') && ! layout_directory?(active_layout)
|
||||
active_layout
|
||||
else
|
||||
"layouts/#{active_layout}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def render_with_a_layout(options = nil, deprecated_status = nil, deprecated_layout = nil, &block) #:nodoc:
|
||||
template_with_options = options.is_a?(Hash)
|
||||
|
||||
if apply_layout?(template_with_options, options) && (layout = pick_layout(template_with_options, options, deprecated_layout))
|
||||
options = options.merge :layout => false if template_with_options
|
||||
logger.info("Rendering #{options} within #{layout}") if logger
|
||||
|
||||
if template_with_options
|
||||
content_for_layout = render_with_no_layout(options, &block)
|
||||
deprecated_status = options[:status] || deprecated_status
|
||||
else
|
||||
content_for_layout = render_with_no_layout(options, deprecated_status, &block)
|
||||
end
|
||||
|
||||
erase_render_results
|
||||
add_variables_to_assigns
|
||||
@template.instance_variable_set("@content_for_layout", content_for_layout)
|
||||
render_text(@template.render_file(layout, true), deprecated_status)
|
||||
else
|
||||
render_with_no_layout(options, deprecated_status, &block)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def apply_layout?(template_with_options, options)
|
||||
return false if options == :update
|
||||
template_with_options ? candidate_for_layout?(options) : !template_exempt_from_layout?
|
||||
end
|
||||
|
||||
def candidate_for_layout?(options)
|
||||
(options.has_key?(:layout) && options[:layout] != false) ||
|
||||
options.values_at(:text, :xml, :file, :inline, :partial, :nothing).compact.empty? &&
|
||||
!template_exempt_from_layout?(default_template_name(options[:action] || options[:template]))
|
||||
end
|
||||
|
||||
def pick_layout(template_with_options, options, deprecated_layout)
|
||||
if deprecated_layout
|
||||
deprecated_layout
|
||||
elsif template_with_options
|
||||
case layout = options[:layout]
|
||||
when FalseClass
|
||||
nil
|
||||
when NilClass, TrueClass
|
||||
active_layout if action_has_layout?
|
||||
else
|
||||
active_layout(layout)
|
||||
end
|
||||
else
|
||||
active_layout if action_has_layout?
|
||||
end
|
||||
end
|
||||
|
||||
def action_has_layout?
|
||||
if conditions = self.class.layout_conditions
|
||||
case
|
||||
when only = conditions[:only]
|
||||
only.include?(action_name)
|
||||
when except = conditions[:except]
|
||||
!except.include?(action_name)
|
||||
else
|
||||
true
|
||||
end
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Does a layout directory for this class exist?
|
||||
# we cache this info in a class level hash
|
||||
def layout_directory?(layout_name)
|
||||
template_path = File.join(self.class.view_root, 'layouts', layout_name)
|
||||
dirname = File.dirname(template_path)
|
||||
self.class.send(:layout_directory_exists_cache)[dirname]
|
||||
end
|
||||
end
|
||||
end
|
52
vendor/rails/actionpack/lib/action_controller/macros/auto_complete.rb
vendored
Normal file
52
vendor/rails/actionpack/lib/action_controller/macros/auto_complete.rb
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
module ActionController
|
||||
# Macros are class-level calls that add pre-defined actions to the controller based on the parameters passed in.
|
||||
# Currently, they're used to bridge the JavaScript macros, like autocompletion and in-place editing, with the controller
|
||||
# backing.
|
||||
module Macros
|
||||
module AutoComplete #:nodoc:
|
||||
def self.append_features(base) #:nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Example:
|
||||
#
|
||||
# # Controller
|
||||
# class BlogController < ApplicationController
|
||||
# auto_complete_for :post, :title
|
||||
# end
|
||||
#
|
||||
# # View
|
||||
# <%= text_field_with_auto_complete :post, title %>
|
||||
#
|
||||
# By default, auto_complete_for limits the results to 10 entries,
|
||||
# and sorts by the given field.
|
||||
#
|
||||
# auto_complete_for takes a third parameter, an options hash to
|
||||
# the find method used to search for the records:
|
||||
#
|
||||
# auto_complete_for :post, :title, :limit => 15, :order => 'created_at DESC'
|
||||
#
|
||||
# For help on defining text input fields with autocompletion,
|
||||
# see ActionView::Helpers::JavaScriptHelper.
|
||||
#
|
||||
# For more examples, see script.aculo.us:
|
||||
# * http://script.aculo.us/demos/ajax/autocompleter
|
||||
# * http://script.aculo.us/demos/ajax/autocompleter_customized
|
||||
module ClassMethods
|
||||
def auto_complete_for(object, method, options = {})
|
||||
define_method("auto_complete_for_#{object}_#{method}") do
|
||||
find_options = {
|
||||
:conditions => [ "LOWER(#{method}) LIKE ?", '%' + params[object][method].downcase + '%' ],
|
||||
:order => "#{method} ASC",
|
||||
:limit => 10 }.merge!(options)
|
||||
|
||||
@items = object.to_s.camelize.constantize.find(:all, find_options)
|
||||
|
||||
render :inline => "<%= auto_complete_result @items, '#{method}' %>"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
32
vendor/rails/actionpack/lib/action_controller/macros/in_place_editing.rb
vendored
Normal file
32
vendor/rails/actionpack/lib/action_controller/macros/in_place_editing.rb
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
module ActionController
|
||||
module Macros
|
||||
module InPlaceEditing #:nodoc:
|
||||
def self.append_features(base) #:nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Example:
|
||||
#
|
||||
# # Controller
|
||||
# class BlogController < ApplicationController
|
||||
# in_place_edit_for :post, :title
|
||||
# end
|
||||
#
|
||||
# # View
|
||||
# <%= in_place_editor_field :post, 'title' %>
|
||||
#
|
||||
# For help on defining an in place editor in the browser,
|
||||
# see ActionView::Helpers::JavaScriptHelper.
|
||||
module ClassMethods
|
||||
def in_place_edit_for(object, attribute, options = {})
|
||||
define_method("set_#{object}_#{attribute}") do
|
||||
@item = object.to_s.camelize.constantize.find(params[:id])
|
||||
@item.update_attribute(attribute, params[:value])
|
||||
render :text => @item.send(attribute)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
169
vendor/rails/actionpack/lib/action_controller/mime_responds.rb
vendored
Normal file
169
vendor/rails/actionpack/lib/action_controller/mime_responds.rb
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
module ActionController #:nodoc:
|
||||
module MimeResponds #:nodoc:
|
||||
def self.included(base)
|
||||
base.send(:include, ActionController::MimeResponds::InstanceMethods)
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
# Without web-service support, an action which collects the data for displaying a list of people
|
||||
# might look something like this:
|
||||
#
|
||||
# def list
|
||||
# @people = Person.find(:all)
|
||||
# end
|
||||
#
|
||||
# Here's the same action, with web-service support baked in:
|
||||
#
|
||||
# def list
|
||||
# @people = Person.find(:all)
|
||||
#
|
||||
# respond_to do |wants|
|
||||
# wants.html
|
||||
# wants.xml { render :xml => @people.to_xml }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# What that says is, "if the client wants HTML in response to this action, just respond as we
|
||||
# would have before, but if the client wants XML, return them the list of people in XML format."
|
||||
# (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
|
||||
#
|
||||
# Supposing you have an action that adds a new person, optionally creating their company
|
||||
# (by name) if it does not already exist, without web-services, it might look like this:
|
||||
#
|
||||
# def add
|
||||
# @company = Company.find_or_create_by_name(params[:company][:name])
|
||||
# @person = @company.people.create(params[:person])
|
||||
#
|
||||
# redirect_to(person_list_url)
|
||||
# end
|
||||
#
|
||||
# Here's the same action, with web-service support baked in:
|
||||
#
|
||||
# def add
|
||||
# company = params[:person].delete(:company)
|
||||
# @company = Company.find_or_create_by_name(company[:name])
|
||||
# @person = @company.people.create(params[:person])
|
||||
#
|
||||
# respond_to do |wants|
|
||||
# wants.html { redirect_to(person_list_url) }
|
||||
# wants.js
|
||||
# wants.xml { render :xml => @person.to_xml(:include => @company) }
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# If the client wants HTML, we just redirect them back to the person list. If they want Javascript
|
||||
# (wants.js), then it is an RJS request and we render the RJS template associated with this action.
|
||||
# Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
|
||||
# include the person’s company in the rendered XML, so you get something like this:
|
||||
#
|
||||
# <person>
|
||||
# <id>...</id>
|
||||
# ...
|
||||
# <company>
|
||||
# <id>...</id>
|
||||
# <name>...</name>
|
||||
# ...
|
||||
# </company>
|
||||
# </person>
|
||||
#
|
||||
# Note, however, the extra bit at the top of that action:
|
||||
#
|
||||
# company = params[:person].delete(:company)
|
||||
# @company = Company.find_or_create_by_name(company[:name])
|
||||
#
|
||||
# This is because the incoming XML document (if a web-service request is in process) can only contain a
|
||||
# single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
|
||||
#
|
||||
# person[name]=...&person[company][name]=...&...
|
||||
#
|
||||
# And, like this (xml-encoded):
|
||||
#
|
||||
# <person>
|
||||
# <name>...</name>
|
||||
# <company>
|
||||
# <name>...</name>
|
||||
# </company>
|
||||
# </person>
|
||||
#
|
||||
# In other words, we make the request so that it operates on a single entity—a person. Then, in the action,
|
||||
# we extract the company data from the request, find or create the company, and then create the new person
|
||||
# with the remaining data.
|
||||
#
|
||||
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities
|
||||
# in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow
|
||||
# and accept Rails' defaults, life will be much easier.
|
||||
#
|
||||
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
|
||||
# environment.rb as follows.
|
||||
#
|
||||
# Mime::Type.register "image/jpg", :jpg
|
||||
#
|
||||
def respond_to(*types, &block)
|
||||
raise ArgumentError, "respond_to takes either types or a block, never bot" unless types.any? ^ block
|
||||
block ||= lambda { |responder| types.each { |type| responder.send(type) } }
|
||||
responder = Responder.new(block.binding)
|
||||
block.call(responder)
|
||||
responder.respond
|
||||
end
|
||||
end
|
||||
|
||||
class Responder #:nodoc:
|
||||
DEFAULT_BLOCKS = {
|
||||
:html => 'Proc.new { render }',
|
||||
:js => 'Proc.new { render :action => "#{action_name}.rjs" }',
|
||||
:xml => 'Proc.new { render :action => "#{action_name}.rxml" }'
|
||||
}
|
||||
|
||||
def initialize(block_binding)
|
||||
@block_binding = block_binding
|
||||
@mime_type_priority = eval("request.accepts", block_binding)
|
||||
@order = []
|
||||
@responses = {}
|
||||
end
|
||||
|
||||
def custom(mime_type, &block)
|
||||
mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
|
||||
|
||||
@order << mime_type
|
||||
|
||||
if block_given?
|
||||
@responses[mime_type] = block
|
||||
else
|
||||
@responses[mime_type] = eval(DEFAULT_BLOCKS[mime_type.to_sym], @block_binding)
|
||||
end
|
||||
end
|
||||
|
||||
for mime_type in %w( all html js xml rss atom yaml )
|
||||
eval <<-EOT
|
||||
def #{mime_type}(&block)
|
||||
custom(Mime::#{mime_type.upcase}, &block)
|
||||
end
|
||||
EOT
|
||||
end
|
||||
|
||||
def any(*args, &block)
|
||||
args.each { |type| send(type, &block) }
|
||||
end
|
||||
|
||||
def respond
|
||||
for priority in @mime_type_priority
|
||||
if priority == Mime::ALL
|
||||
@responses[@order.first].call
|
||||
return
|
||||
else
|
||||
if priority === @order
|
||||
@responses[priority].call
|
||||
return # mime type match found, be happy and return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if @order.include?(Mime::ALL)
|
||||
@responses[Mime::ALL].call
|
||||
else
|
||||
eval 'render(:nothing => true, :status => "406 Not Acceptable")', @block_binding
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
142
vendor/rails/actionpack/lib/action_controller/mime_type.rb
vendored
Normal file
142
vendor/rails/actionpack/lib/action_controller/mime_type.rb
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
module Mime
|
||||
class Type #:nodoc:
|
||||
# A simple helper class used in parsing the accept header
|
||||
class AcceptItem #:nodoc:
|
||||
attr_accessor :order, :name, :q
|
||||
|
||||
def initialize(order, name, q=nil)
|
||||
@order = order
|
||||
@name = name.strip
|
||||
q ||= 0.0 if @name == "*/*" # default "*/*" to end of list
|
||||
@q = ((q || 1.0).to_f * 100).to_i
|
||||
end
|
||||
|
||||
def to_s
|
||||
@name
|
||||
end
|
||||
|
||||
def <=>(item)
|
||||
result = item.q <=> q
|
||||
result = order <=> item.order if result == 0
|
||||
result
|
||||
end
|
||||
|
||||
def ==(item)
|
||||
name == (item.respond_to?(:name) ? item.name : item)
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
def lookup(string)
|
||||
LOOKUP[string]
|
||||
end
|
||||
|
||||
def parse(accept_header)
|
||||
# keep track of creation order to keep the subsequent sort stable
|
||||
index = 0
|
||||
list = accept_header.split(/,/).
|
||||
map! { |i| AcceptItem.new(index += 1, *i.split(/;\s*q=/)) }.sort!
|
||||
|
||||
# Take care of the broken text/xml entry by renaming or deleting it
|
||||
|
||||
text_xml = list.index("text/xml")
|
||||
app_xml = list.index("application/xml")
|
||||
|
||||
if text_xml && app_xml
|
||||
# set the q value to the max of the two
|
||||
list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
|
||||
|
||||
# make sure app_xml is ahead of text_xml in the list
|
||||
if app_xml > text_xml
|
||||
list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
|
||||
app_xml, text_xml = text_xml, app_xml
|
||||
end
|
||||
|
||||
# delete text_xml from the list
|
||||
list.delete_at(text_xml)
|
||||
|
||||
elsif text_xml
|
||||
list[text_xml].name = "application/xml"
|
||||
end
|
||||
|
||||
# Look for more specific xml-based types and sort them ahead of app/xml
|
||||
|
||||
if app_xml
|
||||
idx = app_xml
|
||||
app_xml_type = list[app_xml]
|
||||
|
||||
while(idx < list.length)
|
||||
type = list[idx]
|
||||
break if type.q < app_xml_type.q
|
||||
if type.name =~ /\+xml$/
|
||||
list[app_xml], list[idx] = list[idx], list[app_xml]
|
||||
app_xml = idx
|
||||
end
|
||||
idx += 1
|
||||
end
|
||||
end
|
||||
|
||||
list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
|
||||
list
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(string, symbol = nil, synonyms = [])
|
||||
@symbol, @synonyms = symbol, synonyms
|
||||
@string = string
|
||||
end
|
||||
|
||||
def to_s
|
||||
@string
|
||||
end
|
||||
|
||||
def to_str
|
||||
to_s
|
||||
end
|
||||
|
||||
def to_sym
|
||||
@symbol || @string.to_sym
|
||||
end
|
||||
|
||||
def ===(list)
|
||||
if list.is_a?(Array)
|
||||
(@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def ==(mime_type)
|
||||
(@synonyms + [ self ]).any? { |synonym| synonym.to_s == mime_type.to_s } if mime_type
|
||||
end
|
||||
end
|
||||
|
||||
ALL = Type.new "*/*", :all
|
||||
HTML = Type.new "text/html", :html, %w( application/xhtml+xml )
|
||||
JS = Type.new "text/javascript", :js, %w( application/javascript application/x-javascript )
|
||||
XML = Type.new "application/xml", :xml, %w( text/xml application/x-xml )
|
||||
RSS = Type.new "application/rss+xml", :rss
|
||||
ATOM = Type.new "application/atom+xml", :atom
|
||||
YAML = Type.new "application/x-yaml", :yaml, %w( text/yaml )
|
||||
|
||||
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) }
|
||||
|
||||
LOOKUP["*/*"] = ALL
|
||||
|
||||
LOOKUP["text/html"] = HTML
|
||||
LOOKUP["application/xhtml+xml"] = HTML
|
||||
|
||||
LOOKUP["application/xml"] = XML
|
||||
LOOKUP["text/xml"] = XML
|
||||
LOOKUP["application/x-xml"] = XML
|
||||
|
||||
LOOKUP["text/javascript"] = JS
|
||||
LOOKUP["application/javascript"] = JS
|
||||
LOOKUP["application/x-javascript"] = JS
|
||||
|
||||
LOOKUP["text/yaml"] = YAML
|
||||
LOOKUP["application/x-yaml"] = YAML
|
||||
|
||||
LOOKUP["application/rss+xml"] = RSS
|
||||
LOOKUP["application/atom+xml"] = ATOM
|
||||
end
|
401
vendor/rails/actionpack/lib/action_controller/pagination.rb
vendored
Normal file
401
vendor/rails/actionpack/lib/action_controller/pagination.rb
vendored
Normal file
|
@ -0,0 +1,401 @@
|
|||
module ActionController
|
||||
# === Action Pack pagination for Active Record collections
|
||||
#
|
||||
# The Pagination module aids in the process of paging large collections of
|
||||
# Active Record objects. It offers macro-style automatic fetching of your
|
||||
# model for multiple views, or explicit fetching for single actions. And if
|
||||
# the magic isn't flexible enough for your needs, you can create your own
|
||||
# paginators with a minimal amount of code.
|
||||
#
|
||||
# The Pagination module can handle as much or as little as you wish. In the
|
||||
# controller, have it automatically query your model for pagination; or,
|
||||
# if you prefer, create Paginator objects yourself.
|
||||
#
|
||||
# Pagination is included automatically for all controllers.
|
||||
#
|
||||
# For help rendering pagination links, see
|
||||
# ActionView::Helpers::PaginationHelper.
|
||||
#
|
||||
# ==== Automatic pagination for every action in a controller
|
||||
#
|
||||
# class PersonController < ApplicationController
|
||||
# model :person
|
||||
#
|
||||
# paginate :people, :order => 'last_name, first_name',
|
||||
# :per_page => 20
|
||||
#
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
# Each action in this controller now has access to a <tt>@people</tt>
|
||||
# instance variable, which is an ordered collection of model objects for the
|
||||
# current page (at most 20, sorted by last name and first name), and a
|
||||
# <tt>@person_pages</tt> Paginator instance. The current page is determined
|
||||
# by the <tt>params[:page]</tt> variable.
|
||||
#
|
||||
# ==== Pagination for a single action
|
||||
#
|
||||
# def list
|
||||
# @person_pages, @people =
|
||||
# paginate :people, :order => 'last_name, first_name'
|
||||
# end
|
||||
#
|
||||
# Like the previous example, but explicitly creates <tt>@person_pages</tt>
|
||||
# and <tt>@people</tt> for a single action, and uses the default of 10 items
|
||||
# per page.
|
||||
#
|
||||
# ==== Custom/"classic" pagination
|
||||
#
|
||||
# def list
|
||||
# @person_pages = Paginator.new self, Person.count, 10, params[:page]
|
||||
# @people = Person.find :all, :order => 'last_name, first_name',
|
||||
# :limit => @person_pages.items_per_page,
|
||||
# :offset => @person_pages.current.offset
|
||||
# end
|
||||
#
|
||||
# Explicitly creates the paginator from the previous example and uses
|
||||
# Paginator#to_sql to retrieve <tt>@people</tt> from the model.
|
||||
#
|
||||
module Pagination
|
||||
unless const_defined?(:OPTIONS)
|
||||
# A hash holding options for controllers using macro-style pagination
|
||||
OPTIONS = Hash.new
|
||||
|
||||
# The default options for pagination
|
||||
DEFAULT_OPTIONS = {
|
||||
:class_name => nil,
|
||||
:singular_name => nil,
|
||||
:per_page => 10,
|
||||
:conditions => nil,
|
||||
:order_by => nil,
|
||||
:order => nil,
|
||||
:join => nil,
|
||||
:joins => nil,
|
||||
:count => nil,
|
||||
:include => nil,
|
||||
:select => nil,
|
||||
:parameter => 'page'
|
||||
}
|
||||
end
|
||||
|
||||
def self.included(base) #:nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
def self.validate_options!(collection_id, options, in_action) #:nodoc:
|
||||
options.merge!(DEFAULT_OPTIONS) {|key, old, new| old}
|
||||
|
||||
valid_options = DEFAULT_OPTIONS.keys
|
||||
valid_options << :actions unless in_action
|
||||
|
||||
unknown_option_keys = options.keys - valid_options
|
||||
raise ActionController::ActionControllerError,
|
||||
"Unknown options: #{unknown_option_keys.join(', ')}" unless
|
||||
unknown_option_keys.empty?
|
||||
|
||||
options[:singular_name] ||= Inflector.singularize(collection_id.to_s)
|
||||
options[:class_name] ||= Inflector.camelize(options[:singular_name])
|
||||
end
|
||||
|
||||
# Returns a paginator and a collection of Active Record model instances
|
||||
# for the paginator's current page. This is designed to be used in a
|
||||
# single action; to automatically paginate multiple actions, consider
|
||||
# ClassMethods#paginate.
|
||||
#
|
||||
# +options+ are:
|
||||
# <tt>:singular_name</tt>:: the singular name to use, if it can't be inferred by
|
||||
# singularizing the collection name
|
||||
# <tt>:class_name</tt>:: the class name to use, if it can't be inferred by
|
||||
# camelizing the singular name
|
||||
# <tt>:per_page</tt>:: the maximum number of items to include in a
|
||||
# single page. Defaults to 10
|
||||
# <tt>:conditions</tt>:: optional conditions passed to Model.find(:all, *params) and
|
||||
# Model.count
|
||||
# <tt>:order</tt>:: optional order parameter passed to Model.find(:all, *params)
|
||||
# <tt>:order_by</tt>:: (deprecated, used :order) optional order parameter passed to Model.find(:all, *params)
|
||||
# <tt>:joins</tt>:: optional joins parameter passed to Model.find(:all, *params)
|
||||
# and Model.count
|
||||
# <tt>:join</tt>:: (deprecated, used :joins or :include) optional join parameter passed to Model.find(:all, *params)
|
||||
# and Model.count
|
||||
# <tt>:include</tt>:: optional eager loading parameter passed to Model.find(:all, *params)
|
||||
# and Model.count
|
||||
# <tt>:select</tt>:: :select parameter passed to Model.find(:all, *params)
|
||||
#
|
||||
# <tt>:count</tt>:: parameter passed as :select option to Model.count(*params)
|
||||
#
|
||||
def paginate(collection_id, options={})
|
||||
Pagination.validate_options!(collection_id, options, true)
|
||||
paginator_and_collection_for(collection_id, options)
|
||||
end
|
||||
|
||||
# These methods become class methods on any controller
|
||||
module ClassMethods
|
||||
# Creates a +before_filter+ which automatically paginates an Active
|
||||
# Record model for all actions in a controller (or certain actions if
|
||||
# specified with the <tt>:actions</tt> option).
|
||||
#
|
||||
# +options+ are the same as PaginationHelper#paginate, with the addition
|
||||
# of:
|
||||
# <tt>:actions</tt>:: an array of actions for which the pagination is
|
||||
# active. Defaults to +nil+ (i.e., every action)
|
||||
def paginate(collection_id, options={})
|
||||
Pagination.validate_options!(collection_id, options, false)
|
||||
module_eval do
|
||||
before_filter :create_paginators_and_retrieve_collections
|
||||
OPTIONS[self] ||= Hash.new
|
||||
OPTIONS[self][collection_id] = options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_paginators_and_retrieve_collections #:nodoc:
|
||||
Pagination::OPTIONS[self.class].each do |collection_id, options|
|
||||
next unless options[:actions].include? action_name if
|
||||
options[:actions]
|
||||
|
||||
paginator, collection =
|
||||
paginator_and_collection_for(collection_id, options)
|
||||
|
||||
paginator_name = "@#{options[:singular_name]}_pages"
|
||||
self.instance_variable_set(paginator_name, paginator)
|
||||
|
||||
collection_name = "@#{collection_id.to_s}"
|
||||
self.instance_variable_set(collection_name, collection)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the total number of items in the collection to be paginated for
|
||||
# the +model+ and given +conditions+. Override this method to implement a
|
||||
# custom counter.
|
||||
def count_collection_for_pagination(model, options)
|
||||
model.count(:conditions => options[:conditions],
|
||||
:joins => options[:join] || options[:joins],
|
||||
:include => options[:include],
|
||||
:select => options[:count])
|
||||
end
|
||||
|
||||
# Returns a collection of items for the given +model+ and +options[conditions]+,
|
||||
# ordered by +options[order]+, for the current page in the given +paginator+.
|
||||
# Override this method to implement a custom finder.
|
||||
def find_collection_for_pagination(model, options, paginator)
|
||||
model.find(:all, :conditions => options[:conditions],
|
||||
:order => options[:order_by] || options[:order],
|
||||
:joins => options[:join] || options[:joins], :include => options[:include],
|
||||
:select => options[:select], :limit => options[:per_page],
|
||||
:offset => paginator.current.offset)
|
||||
end
|
||||
|
||||
protected :create_paginators_and_retrieve_collections,
|
||||
:count_collection_for_pagination,
|
||||
:find_collection_for_pagination
|
||||
|
||||
def paginator_and_collection_for(collection_id, options) #:nodoc:
|
||||
klass = options[:class_name].constantize
|
||||
page = @params[options[:parameter]]
|
||||
count = count_collection_for_pagination(klass, options)
|
||||
paginator = Paginator.new(self, count, options[:per_page], page)
|
||||
collection = find_collection_for_pagination(klass, options, paginator)
|
||||
|
||||
return paginator, collection
|
||||
end
|
||||
|
||||
private :paginator_and_collection_for
|
||||
|
||||
# A class representing a paginator for an Active Record collection.
|
||||
class Paginator
|
||||
include Enumerable
|
||||
|
||||
# Creates a new Paginator on the given +controller+ for a set of items
|
||||
# of size +item_count+ and having +items_per_page+ items per page.
|
||||
# Raises ArgumentError if items_per_page is out of bounds (i.e., less
|
||||
# than or equal to zero). The page CGI parameter for links defaults to
|
||||
# "page" and can be overridden with +page_parameter+.
|
||||
def initialize(controller, item_count, items_per_page, current_page=1)
|
||||
raise ArgumentError, 'must have at least one item per page' if
|
||||
items_per_page <= 0
|
||||
|
||||
@controller = controller
|
||||
@item_count = item_count || 0
|
||||
@items_per_page = items_per_page
|
||||
@pages = {}
|
||||
|
||||
self.current_page = current_page
|
||||
end
|
||||
attr_reader :controller, :item_count, :items_per_page
|
||||
|
||||
# Sets the current page number of this paginator. If +page+ is a Page
|
||||
# object, its +number+ attribute is used as the value; if the page does
|
||||
# not belong to this Paginator, an ArgumentError is raised.
|
||||
def current_page=(page)
|
||||
if page.is_a? Page
|
||||
raise ArgumentError, 'Page/Paginator mismatch' unless
|
||||
page.paginator == self
|
||||
end
|
||||
page = page.to_i
|
||||
@current_page_number = has_page_number?(page) ? page : 1
|
||||
end
|
||||
|
||||
# Returns a Page object representing this paginator's current page.
|
||||
def current_page
|
||||
@current_page ||= self[@current_page_number]
|
||||
end
|
||||
alias current :current_page
|
||||
|
||||
# Returns a new Page representing the first page in this paginator.
|
||||
def first_page
|
||||
@first_page ||= self[1]
|
||||
end
|
||||
alias first :first_page
|
||||
|
||||
# Returns a new Page representing the last page in this paginator.
|
||||
def last_page
|
||||
@last_page ||= self[page_count]
|
||||
end
|
||||
alias last :last_page
|
||||
|
||||
# Returns the number of pages in this paginator.
|
||||
def page_count
|
||||
@page_count ||= @item_count.zero? ? 1 :
|
||||
(q,r=@item_count.divmod(@items_per_page); r==0? q : q+1)
|
||||
end
|
||||
|
||||
alias length :page_count
|
||||
|
||||
# Returns true if this paginator contains the page of index +number+.
|
||||
def has_page_number?(number)
|
||||
number >= 1 and number <= page_count
|
||||
end
|
||||
|
||||
# Returns a new Page representing the page with the given index
|
||||
# +number+.
|
||||
def [](number)
|
||||
@pages[number] ||= Page.new(self, number)
|
||||
end
|
||||
|
||||
# Successively yields all the paginator's pages to the given block.
|
||||
def each(&block)
|
||||
page_count.times do |n|
|
||||
yield self[n+1]
|
||||
end
|
||||
end
|
||||
|
||||
# A class representing a single page in a paginator.
|
||||
class Page
|
||||
include Comparable
|
||||
|
||||
# Creates a new Page for the given +paginator+ with the index
|
||||
# +number+. If +number+ is not in the range of valid page numbers or
|
||||
# is not a number at all, it defaults to 1.
|
||||
def initialize(paginator, number)
|
||||
@paginator = paginator
|
||||
@number = number.to_i
|
||||
@number = 1 unless @paginator.has_page_number? @number
|
||||
end
|
||||
attr_reader :paginator, :number
|
||||
alias to_i :number
|
||||
|
||||
# Compares two Page objects and returns true when they represent the
|
||||
# same page (i.e., their paginators are the same and they have the
|
||||
# same page number).
|
||||
def ==(page)
|
||||
return false if page.nil?
|
||||
@paginator == page.paginator and
|
||||
@number == page.number
|
||||
end
|
||||
|
||||
# Compares two Page objects and returns -1 if the left-hand page comes
|
||||
# before the right-hand page, 0 if the pages are equal, and 1 if the
|
||||
# left-hand page comes after the right-hand page. Raises ArgumentError
|
||||
# if the pages do not belong to the same Paginator object.
|
||||
def <=>(page)
|
||||
raise ArgumentError unless @paginator == page.paginator
|
||||
@number <=> page.number
|
||||
end
|
||||
|
||||
# Returns the item offset for the first item in this page.
|
||||
def offset
|
||||
@paginator.items_per_page * (@number - 1)
|
||||
end
|
||||
|
||||
# Returns the number of the first item displayed.
|
||||
def first_item
|
||||
offset + 1
|
||||
end
|
||||
|
||||
# Returns the number of the last item displayed.
|
||||
def last_item
|
||||
[@paginator.items_per_page * @number, @paginator.item_count].min
|
||||
end
|
||||
|
||||
# Returns true if this page is the first page in the paginator.
|
||||
def first?
|
||||
self == @paginator.first
|
||||
end
|
||||
|
||||
# Returns true if this page is the last page in the paginator.
|
||||
def last?
|
||||
self == @paginator.last
|
||||
end
|
||||
|
||||
# Returns a new Page object representing the page just before this
|
||||
# page, or nil if this is the first page.
|
||||
def previous
|
||||
if first? then nil else @paginator[@number - 1] end
|
||||
end
|
||||
|
||||
# Returns a new Page object representing the page just after this
|
||||
# page, or nil if this is the last page.
|
||||
def next
|
||||
if last? then nil else @paginator[@number + 1] end
|
||||
end
|
||||
|
||||
# Returns a new Window object for this page with the specified
|
||||
# +padding+.
|
||||
def window(padding=2)
|
||||
Window.new(self, padding)
|
||||
end
|
||||
|
||||
# Returns the limit/offset array for this page.
|
||||
def to_sql
|
||||
[@paginator.items_per_page, offset]
|
||||
end
|
||||
|
||||
def to_param #:nodoc:
|
||||
@number.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# A class for representing ranges around a given page.
|
||||
class Window
|
||||
# Creates a new Window object for the given +page+ with the specified
|
||||
# +padding+.
|
||||
def initialize(page, padding=2)
|
||||
@paginator = page.paginator
|
||||
@page = page
|
||||
self.padding = padding
|
||||
end
|
||||
attr_reader :paginator, :page
|
||||
|
||||
# Sets the window's padding (the number of pages on either side of the
|
||||
# window page).
|
||||
def padding=(padding)
|
||||
@padding = padding < 0 ? 0 : padding
|
||||
# Find the beginning and end pages of the window
|
||||
@first = @paginator.has_page_number?(@page.number - @padding) ?
|
||||
@paginator[@page.number - @padding] : @paginator.first
|
||||
@last = @paginator.has_page_number?(@page.number + @padding) ?
|
||||
@paginator[@page.number + @padding] : @paginator.last
|
||||
end
|
||||
attr_reader :padding, :first, :last
|
||||
|
||||
# Returns an array of Page objects in the current window.
|
||||
def pages
|
||||
(@first.number..@last.number).to_a.collect! {|n| @paginator[n]}
|
||||
end
|
||||
alias to_a :pages
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
257
vendor/rails/actionpack/lib/action_controller/request.rb
vendored
Executable file
257
vendor/rails/actionpack/lib/action_controller/request.rb
vendored
Executable file
|
@ -0,0 +1,257 @@
|
|||
module ActionController
|
||||
# Subclassing AbstractRequest makes these methods available to the request objects used in production and testing,
|
||||
# CgiRequest and TestRequest
|
||||
class AbstractRequest
|
||||
cattr_accessor :relative_url_root
|
||||
|
||||
# Returns the hash of environment variables for this request,
|
||||
# such as { 'RAILS_ENV' => 'production' }.
|
||||
attr_reader :env
|
||||
|
||||
# Returns both GET and POST parameters in a single hash.
|
||||
def parameters
|
||||
@parameters ||= request_parameters.update(query_parameters).update(path_parameters).with_indifferent_access
|
||||
end
|
||||
|
||||
# Returns the HTTP request method as a lowercase symbol (:get, for example)
|
||||
def method
|
||||
@request_method ||= @env['REQUEST_METHOD'].downcase.to_sym
|
||||
end
|
||||
|
||||
# Is this a GET request? Equivalent to request.method == :get
|
||||
def get?
|
||||
method == :get
|
||||
end
|
||||
|
||||
# Is this a POST request? Equivalent to request.method == :post
|
||||
def post?
|
||||
method == :post
|
||||
end
|
||||
|
||||
# Is this a PUT request? Equivalent to request.method == :put
|
||||
def put?
|
||||
method == :put
|
||||
end
|
||||
|
||||
# Is this a DELETE request? Equivalent to request.method == :delete
|
||||
def delete?
|
||||
method == :delete
|
||||
end
|
||||
|
||||
# Is this a HEAD request? Equivalent to request.method == :head
|
||||
def head?
|
||||
method == :head
|
||||
end
|
||||
|
||||
# Determine whether the body of a HTTP call is URL-encoded (default)
|
||||
# or matches one of the registered param_parsers.
|
||||
#
|
||||
# For backward compatibility, the post format is extracted from the
|
||||
# X-Post-Data-Format HTTP header if present.
|
||||
def content_type
|
||||
@content_type ||=
|
||||
begin
|
||||
content_type = @env['CONTENT_TYPE'].to_s.downcase
|
||||
|
||||
if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
|
||||
case x_post_format.to_s.downcase
|
||||
when 'yaml'
|
||||
content_type = 'application/x-yaml'
|
||||
when 'xml'
|
||||
content_type = 'application/xml'
|
||||
end
|
||||
end
|
||||
|
||||
Mime::Type.lookup(content_type)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the accepted MIME type for the request
|
||||
def accepts
|
||||
@accepts ||=
|
||||
if @env['HTTP_ACCEPT'].to_s.strip.empty?
|
||||
[ content_type, Mime::ALL ]
|
||||
else
|
||||
Mime::Type.parse(@env['HTTP_ACCEPT'])
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if the request's "X-Requested-With" header contains
|
||||
# "XMLHttpRequest". (The Prototype Javascript library sends this header with
|
||||
# every Ajax request.)
|
||||
def xml_http_request?
|
||||
not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
|
||||
end
|
||||
alias xhr? :xml_http_request?
|
||||
|
||||
# Determine originating IP address. REMOTE_ADDR is the standard
|
||||
# but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
|
||||
# HTTP_X_FORWARDED_FOR are set by proxies so check for these before
|
||||
# falling back to REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma-
|
||||
# delimited list in the case of multiple chained proxies; the first is
|
||||
# the originating IP.
|
||||
def remote_ip
|
||||
return @env['HTTP_CLIENT_IP'] if @env.include? 'HTTP_CLIENT_IP'
|
||||
|
||||
if @env.include? 'HTTP_X_FORWARDED_FOR' then
|
||||
remote_ips = @env['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
|
||||
ip =~ /^unknown$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
|
||||
end
|
||||
|
||||
return remote_ips.first.strip unless remote_ips.empty?
|
||||
end
|
||||
|
||||
@env['REMOTE_ADDR']
|
||||
end
|
||||
|
||||
# Returns the domain part of a host, such as rubyonrails.org in "www.rubyonrails.org". You can specify
|
||||
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
|
||||
def domain(tld_length = 1)
|
||||
return nil if !/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.match(host).nil? or host.nil?
|
||||
|
||||
host.split('.').last(1 + tld_length).join('.')
|
||||
end
|
||||
|
||||
# Returns all the subdomains as an array, so ["dev", "www"] would be returned for "dev.www.rubyonrails.org".
|
||||
# You can specify a different <tt>tld_length</tt>, such as 2 to catch ["www"] instead of ["www", "rubyonrails"]
|
||||
# in "www.rubyonrails.co.uk".
|
||||
def subdomains(tld_length = 1)
|
||||
return [] unless host
|
||||
parts = host.split('.')
|
||||
parts[0..-(tld_length+2)]
|
||||
end
|
||||
|
||||
# Receive the raw post data.
|
||||
# This is useful for services such as REST, XMLRPC and SOAP
|
||||
# which communicate over HTTP POST but don't use the traditional parameter format.
|
||||
def raw_post
|
||||
@env['RAW_POST_DATA']
|
||||
end
|
||||
|
||||
# Returns the request URI correctly, taking into account the idiosyncracies
|
||||
# of the various servers.
|
||||
def request_uri
|
||||
if uri = @env['REQUEST_URI']
|
||||
(%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri # Remove domain, which webrick puts into the request_uri.
|
||||
else # REQUEST_URI is blank under IIS - get this from PATH_INFO and SCRIPT_NAME
|
||||
script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
|
||||
uri = @env['PATH_INFO']
|
||||
uri = uri.sub(/#{script_filename}\//, '') unless script_filename.nil?
|
||||
unless (env_qs = @env['QUERY_STRING']).nil? || env_qs.empty?
|
||||
uri << '?' << env_qs
|
||||
end
|
||||
uri
|
||||
end
|
||||
end
|
||||
|
||||
# Return 'https://' if this is an SSL request and 'http://' otherwise.
|
||||
def protocol
|
||||
ssl? ? 'https://' : 'http://'
|
||||
end
|
||||
|
||||
# Is this an SSL request?
|
||||
def ssl?
|
||||
@env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
||||
end
|
||||
|
||||
# Returns the interpreted path to requested resource after all the installation directory of this application was taken into account
|
||||
def path
|
||||
path = (uri = request_uri) ? uri.split('?').first : ''
|
||||
|
||||
# Cut off the path to the installation directory if given
|
||||
root = relative_url_root
|
||||
path[0, root.length] = '' if root
|
||||
path || ''
|
||||
end
|
||||
|
||||
# Returns the path minus the web server relative installation directory.
|
||||
# This can be set with the environment variable RAILS_RELATIVE_URL_ROOT.
|
||||
# It can be automatically extracted for Apache setups. If the server is not
|
||||
# Apache, this method returns an empty string.
|
||||
def relative_url_root
|
||||
@@relative_url_root ||= case
|
||||
when @env["RAILS_RELATIVE_URL_ROOT"]
|
||||
@env["RAILS_RELATIVE_URL_ROOT"]
|
||||
when server_software == 'apache'
|
||||
@env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '')
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the port number of this request as an integer.
|
||||
def port
|
||||
@port_as_int ||= @env['SERVER_PORT'].to_i
|
||||
end
|
||||
|
||||
# Returns the standard port number for this request's protocol
|
||||
def standard_port
|
||||
case protocol
|
||||
when 'https://' then 443
|
||||
else 80
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a port suffix like ":8080" if the port number of this request
|
||||
# is not the default HTTP port 80 or HTTPS port 443.
|
||||
def port_string
|
||||
(port == standard_port) ? '' : ":#{port}"
|
||||
end
|
||||
|
||||
# Returns a host:port string for this request, such as example.com or
|
||||
# example.com:8080.
|
||||
def host_with_port
|
||||
host + port_string
|
||||
end
|
||||
|
||||
def path_parameters=(parameters) #:nodoc:
|
||||
@path_parameters = parameters
|
||||
@symbolized_path_parameters = @parameters = nil
|
||||
end
|
||||
|
||||
# The same as <tt>path_parameters</tt> with explicitly symbolized keys
|
||||
def symbolized_path_parameters
|
||||
@symbolized_path_parameters ||= path_parameters.symbolize_keys
|
||||
end
|
||||
|
||||
# Returns a hash with the parameters used to form the path of the request
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# {:action => 'my_action', :controller => 'my_controller'}
|
||||
def path_parameters
|
||||
@path_parameters ||= {}
|
||||
end
|
||||
|
||||
# Returns the lowercase name of the HTTP server software.
|
||||
def server_software
|
||||
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
|
||||
end
|
||||
|
||||
#--
|
||||
# Must be implemented in the concrete request
|
||||
#++
|
||||
def query_parameters #:nodoc:
|
||||
end
|
||||
|
||||
def request_parameters #:nodoc:
|
||||
end
|
||||
|
||||
# Returns the host for this request, such as example.com.
|
||||
def host
|
||||
end
|
||||
|
||||
def cookies #:nodoc:
|
||||
end
|
||||
|
||||
def session #:nodoc:
|
||||
end
|
||||
|
||||
def session=(session) #:nodoc:
|
||||
@session = session
|
||||
end
|
||||
|
||||
def reset_session #:nodoc:
|
||||
end
|
||||
end
|
||||
end
|
139
vendor/rails/actionpack/lib/action_controller/rescue.rb
vendored
Normal file
139
vendor/rails/actionpack/lib/action_controller/rescue.rb
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
module ActionController #:nodoc:
|
||||
# Actions that fail to perform as expected throw exceptions. These exceptions can either be rescued for the public view
|
||||
# (with a nice user-friendly explanation) or for the developers view (with tons of debugging information). The developers view
|
||||
# is already implemented by the Action Controller, but the public view should be tailored to your specific application. So too
|
||||
# could the decision on whether something is a public or a developer request.
|
||||
#
|
||||
# You can tailor the rescuing behavior and appearance by overwriting the following two stub methods.
|
||||
module Rescue
|
||||
def self.append_features(base) #:nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
base.class_eval do
|
||||
alias_method :perform_action_without_rescue, :perform_action
|
||||
alias_method :perform_action, :perform_action_with_rescue
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods #:nodoc:
|
||||
def process_with_exception(request, response, exception)
|
||||
new.process(request, response, :rescue_action, exception)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
# Exception handler called when the performance of an action raises an exception.
|
||||
def rescue_action(exception)
|
||||
log_error(exception) if logger
|
||||
erase_results if performed?
|
||||
|
||||
if consider_all_requests_local || local_request?
|
||||
rescue_action_locally(exception)
|
||||
else
|
||||
rescue_action_in_public(exception)
|
||||
end
|
||||
end
|
||||
|
||||
# Overwrite to implement custom logging of errors. By default logs as fatal.
|
||||
def log_error(exception) #:doc:
|
||||
if ActionView::TemplateError === exception
|
||||
logger.fatal(exception.to_s)
|
||||
else
|
||||
logger.fatal(
|
||||
"\n\n#{exception.class} (#{exception.message}):\n " +
|
||||
clean_backtrace(exception).join("\n ") +
|
||||
"\n\n"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>).
|
||||
def rescue_action_in_public(exception) #:doc:
|
||||
case exception
|
||||
when RoutingError, UnknownAction then
|
||||
render_text(IO.read(File.join(RAILS_ROOT, 'public', '404.html')), "404 Not Found")
|
||||
else render_text "<html><body><h1>Application error (Rails)</h1></body></html>"
|
||||
end
|
||||
end
|
||||
|
||||
# Overwrite to expand the meaning of a local request in order to show local rescues on other occurrences than
|
||||
# the remote IP being 127.0.0.1. For example, this could include the IP of the developer machine when debugging
|
||||
# remotely.
|
||||
def local_request? #:doc:
|
||||
[@request.remote_addr, @request.remote_ip] == ["127.0.0.1"] * 2
|
||||
end
|
||||
|
||||
# Renders a detailed diagnostics screen on action exceptions.
|
||||
def rescue_action_locally(exception)
|
||||
add_variables_to_assigns
|
||||
@template.instance_variable_set("@exception", exception)
|
||||
@template.instance_variable_set("@rescues_path", File.dirname(__FILE__) + "/templates/rescues/")
|
||||
@template.send(:assign_variables_from_controller)
|
||||
|
||||
@template.instance_variable_set("@contents", @template.render_file(template_path_for_local_rescue(exception), false))
|
||||
|
||||
@headers["Content-Type"] = "text/html"
|
||||
render_file(rescues_path("layout"), response_code_for_rescue(exception))
|
||||
end
|
||||
|
||||
private
|
||||
def perform_action_with_rescue #:nodoc:
|
||||
begin
|
||||
perform_action_without_rescue
|
||||
rescue Object => exception
|
||||
if defined?(Breakpoint) && @params["BP-RETRY"]
|
||||
msg = exception.backtrace.first
|
||||
if md = /^(.+?):(\d+)(?::in `(.+)')?$/.match(msg) then
|
||||
origin_file, origin_line = md[1], md[2].to_i
|
||||
|
||||
set_trace_func(lambda do |type, file, line, method, context, klass|
|
||||
if file == origin_file and line == origin_line then
|
||||
set_trace_func(nil)
|
||||
@params["BP-RETRY"] = false
|
||||
|
||||
callstack = caller
|
||||
callstack.slice!(0) if callstack.first["rescue.rb"]
|
||||
file, line, method = *callstack.first.match(/^(.+?):(\d+)(?::in `(.*?)')?/).captures
|
||||
|
||||
message = "Exception at #{file}:#{line}#{" in `#{method}'" if method}." # `´ ( for ruby-mode)
|
||||
|
||||
Breakpoint.handle_breakpoint(context, message, file, line)
|
||||
end
|
||||
end)
|
||||
|
||||
retry
|
||||
end
|
||||
end
|
||||
|
||||
rescue_action(exception)
|
||||
end
|
||||
end
|
||||
|
||||
def rescues_path(template_name)
|
||||
File.dirname(__FILE__) + "/templates/rescues/#{template_name}.rhtml"
|
||||
end
|
||||
|
||||
def template_path_for_local_rescue(exception)
|
||||
rescues_path(
|
||||
case exception
|
||||
when MissingTemplate then "missing_template"
|
||||
when RoutingError then "routing_error"
|
||||
when UnknownAction then "unknown_action"
|
||||
when ActionView::TemplateError then "template_error"
|
||||
else "diagnostics"
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
def response_code_for_rescue(exception)
|
||||
case exception
|
||||
when UnknownAction, RoutingError then "404 Page Not Found"
|
||||
else "500 Internal Error"
|
||||
end
|
||||
end
|
||||
|
||||
def clean_backtrace(exception)
|
||||
exception.backtrace.collect { |line| Object.const_defined?(:RAILS_ROOT) ? line.gsub(RAILS_ROOT, "") : line }
|
||||
end
|
||||
end
|
||||
end
|
17
vendor/rails/actionpack/lib/action_controller/response.rb
vendored
Executable file
17
vendor/rails/actionpack/lib/action_controller/response.rb
vendored
Executable file
|
@ -0,0 +1,17 @@
|
|||
module ActionController
|
||||
class AbstractResponse #:nodoc:
|
||||
DEFAULT_HEADERS = { "Cache-Control" => "no-cache" }
|
||||
attr_accessor :body, :headers, :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params
|
||||
|
||||
def initialize
|
||||
@body, @headers, @session, @assigns = "", DEFAULT_HEADERS.merge("cookie" => []), [], []
|
||||
end
|
||||
|
||||
def redirect(to_url, permanently = false)
|
||||
@headers["Status"] = "302 Found" unless @headers["Status"] == "301 Moved Permanently"
|
||||
@headers["location"] = to_url
|
||||
|
||||
@body = "<html><body>You are being <a href=\"#{to_url}\">redirected</a>.</body></html>"
|
||||
end
|
||||
end
|
||||
end
|
716
vendor/rails/actionpack/lib/action_controller/routing.rb
vendored
Normal file
716
vendor/rails/actionpack/lib/action_controller/routing.rb
vendored
Normal file
|
@ -0,0 +1,716 @@
|
|||
module ActionController
|
||||
module Routing #:nodoc:
|
||||
class << self
|
||||
def expiry_hash(options, recall)
|
||||
k = v = nil
|
||||
expire_on = {}
|
||||
options.each {|k, v| expire_on[k] = ((rcv = recall[k]) && (rcv != v))}
|
||||
expire_on
|
||||
end
|
||||
|
||||
def extract_parameter_value(parameter) #:nodoc:
|
||||
CGI.escape((parameter.respond_to?(:to_param) ? parameter.to_param : parameter).to_s)
|
||||
end
|
||||
def controller_relative_to(controller, previous)
|
||||
if controller.nil? then previous
|
||||
elsif controller[0] == ?/ then controller[1..-1]
|
||||
elsif %r{^(.*)/} =~ previous then "#{$1}/#{controller}"
|
||||
else controller
|
||||
end
|
||||
end
|
||||
|
||||
def treat_hash(hash, keys_to_delete = [])
|
||||
k = v = nil
|
||||
hash.each do |k, v|
|
||||
if v then hash[k] = (v.respond_to? :to_param) ? v.to_param.to_s : v.to_s
|
||||
else
|
||||
hash.delete k
|
||||
keys_to_delete << k
|
||||
end
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
||||
def test_condition(expression, condition)
|
||||
case condition
|
||||
when String then "(#{expression} == #{condition.inspect})"
|
||||
when Regexp then
|
||||
condition = Regexp.new("^#{condition.source}$") unless /^\^.*\$$/ =~ condition.source
|
||||
"(#{condition.inspect} =~ #{expression})"
|
||||
when Array then
|
||||
conds = condition.collect do |condition|
|
||||
cond = test_condition(expression, condition)
|
||||
(cond[0, 1] == '(' && cond[-1, 1] == ')') ? cond : "(#{cond})"
|
||||
end
|
||||
"(#{conds.join(' || ')})"
|
||||
when true then expression
|
||||
when nil then "! #{expression}"
|
||||
else
|
||||
raise ArgumentError, "Valid criteria are strings, regular expressions, true, or nil"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Component #:nodoc:
|
||||
def dynamic?() false end
|
||||
def optional?() false end
|
||||
|
||||
def key() nil end
|
||||
|
||||
def self.new(string, *args)
|
||||
return super(string, *args) unless self == Component
|
||||
case string
|
||||
when ':controller' then ControllerComponent.new(:controller, *args)
|
||||
when /^:(\w+)$/ then DynamicComponent.new($1, *args)
|
||||
when /^\*(\w+)$/ then PathComponent.new($1, *args)
|
||||
else StaticComponent.new(string, *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class StaticComponent < Component #:nodoc:
|
||||
attr_reader :value
|
||||
|
||||
def initialize(value)
|
||||
@value = value
|
||||
end
|
||||
|
||||
def write_recognition(g)
|
||||
g.if_next_matches(value) do |gp|
|
||||
gp.move_forward {|gpp| gpp.continue}
|
||||
end
|
||||
end
|
||||
|
||||
def write_generation(g)
|
||||
g.add_segment(value) {|gp| gp.continue }
|
||||
end
|
||||
end
|
||||
|
||||
class DynamicComponent < Component #:nodoc:
|
||||
attr_reader :key, :default
|
||||
attr_accessor :condition
|
||||
|
||||
def dynamic?() true end
|
||||
def optional?() @optional end
|
||||
|
||||
def default=(default)
|
||||
@optional = true
|
||||
@default = default
|
||||
end
|
||||
|
||||
def initialize(key, options = {})
|
||||
@key = key.to_sym
|
||||
@optional = false
|
||||
default, @condition = options[:default], options[:condition]
|
||||
self.default = default if options.key?(:default)
|
||||
end
|
||||
|
||||
def default_check(g)
|
||||
presence = "#{g.hash_value(key, !! default)}"
|
||||
if default
|
||||
"!(#{presence} && #{g.hash_value(key, false)} != #{default.to_s.inspect})"
|
||||
else
|
||||
"! #{presence}"
|
||||
end
|
||||
end
|
||||
|
||||
def write_generation(g)
|
||||
wrote_dropout = write_dropout_generation(g)
|
||||
write_continue_generation(g, wrote_dropout)
|
||||
end
|
||||
|
||||
def write_dropout_generation(g)
|
||||
return false unless optional? && g.after.all? {|c| c.optional?}
|
||||
|
||||
check = [default_check(g)]
|
||||
gp = g.dup # Use another generator to write the conditions after the first &&
|
||||
# We do this to ensure that the generator will not assume x_value is set. It will
|
||||
# not be set if it follows a false condition -- for example, false && (x = 2)
|
||||
|
||||
check += gp.after.map {|c| c.default_check gp}
|
||||
gp.if(check.join(' && ')) { gp.finish } # If this condition is met, we stop here
|
||||
true
|
||||
end
|
||||
|
||||
def write_continue_generation(g, use_else)
|
||||
test = Routing.test_condition(g.hash_value(key, true, default), condition || true)
|
||||
check = (use_else && condition.nil? && default) ? [:else] : [use_else ? :elsif : :if, test]
|
||||
|
||||
g.send(*check) do |gp|
|
||||
gp.expire_for_keys(key) unless gp.after.empty?
|
||||
add_segments_to(gp) {|gpp| gpp.continue}
|
||||
end
|
||||
end
|
||||
|
||||
def add_segments_to(g)
|
||||
g.add_segment(%(\#{CGI.escape(#{g.hash_value(key, true, default)})})) {|gp| yield gp}
|
||||
end
|
||||
|
||||
def recognition_check(g)
|
||||
test_type = [true, nil].include?(condition) ? :presence : :constraint
|
||||
|
||||
prefix = condition.is_a?(Regexp) ? "#{g.next_segment(true)} && " : ''
|
||||
check = prefix + Routing.test_condition(g.next_segment(true), condition || true)
|
||||
|
||||
g.if(check) {|gp| yield gp, test_type}
|
||||
end
|
||||
|
||||
def write_recognition(g)
|
||||
test_type = nil
|
||||
recognition_check(g) do |gp, test_type|
|
||||
assign_result(gp) {|gpp| gpp.continue}
|
||||
end
|
||||
|
||||
if optional? && g.after.all? {|c| c.optional?}
|
||||
call = (test_type == :presence) ? [:else] : [:elsif, "! #{g.next_segment(true)}"]
|
||||
|
||||
g.send(*call) do |gp|
|
||||
assign_default(gp)
|
||||
gp.after.each {|c| c.assign_default(gp)}
|
||||
gp.finish(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def assign_result(g, with_default = false)
|
||||
g.result key, "CGI.unescape(#{g.next_segment(true, with_default ? default : nil)})"
|
||||
g.move_forward {|gp| yield gp}
|
||||
end
|
||||
|
||||
def assign_default(g)
|
||||
g.constant_result key, default unless default.nil?
|
||||
end
|
||||
end
|
||||
|
||||
class ControllerComponent < DynamicComponent #:nodoc:
|
||||
def key() :controller end
|
||||
|
||||
def add_segments_to(g)
|
||||
g.add_segment(%(\#{#{g.hash_value(key, true, default)}})) {|gp| yield gp}
|
||||
end
|
||||
|
||||
def recognition_check(g)
|
||||
g << "controller_result = ::ActionController::Routing::ControllerComponent.traverse_to_controller(#{g.path_name}, #{g.index_name})"
|
||||
g.if('controller_result') do |gp|
|
||||
gp << 'controller_value, segments_to_controller = controller_result'
|
||||
if condition
|
||||
gp << "controller_path = #{gp.path_name}[#{gp.index_name},segments_to_controller].join('/')"
|
||||
gp.if(Routing.test_condition("controller_path", condition)) do |gpp|
|
||||
gpp.move_forward('segments_to_controller') {|gppp| yield gppp, :constraint}
|
||||
end
|
||||
else
|
||||
gp.move_forward('segments_to_controller') {|gpp| yield gpp, :constraint}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def assign_result(g)
|
||||
g.result key, 'controller_value'
|
||||
yield g
|
||||
end
|
||||
|
||||
def assign_default(g)
|
||||
ControllerComponent.assign_controller(g, default)
|
||||
end
|
||||
|
||||
class << self
|
||||
def assign_controller(g, controller)
|
||||
expr = "::#{controller.split('/').collect {|c| c.camelize}.join('::')}Controller"
|
||||
g.result :controller, expr, true
|
||||
end
|
||||
|
||||
def traverse_to_controller(segments, start_at = 0)
|
||||
mod = ::Object
|
||||
length = segments.length
|
||||
index = start_at
|
||||
mod_name = controller_name = segment = nil
|
||||
while index < length
|
||||
return nil unless /\A[A-Za-z][A-Za-z\d_]*\Z/ =~ (segment = segments[index])
|
||||
index += 1
|
||||
|
||||
mod_name = segment.camelize
|
||||
controller_name = "#{mod_name}Controller"
|
||||
path_suffix = File.join(segments[start_at..(index - 1)])
|
||||
next_mod = nil
|
||||
|
||||
# If the controller is already present, or if we load it, return it.
|
||||
if mod.const_defined?(controller_name) || attempt_load(mod, controller_name, path_suffix + "_controller") == :defined
|
||||
controller = mod.const_get(controller_name)
|
||||
return nil unless controller.is_a?(Class) && controller.ancestors.include?(ActionController::Base) # it's not really a controller?
|
||||
return [controller, (index - start_at)]
|
||||
end
|
||||
|
||||
# No controller? Look for the module
|
||||
if mod.const_defined? mod_name
|
||||
next_mod = mod.send(:const_get, mod_name)
|
||||
next_mod = nil unless next_mod.is_a?(Module)
|
||||
else
|
||||
# Try to load a file that defines the module we want.
|
||||
case attempt_load(mod, mod_name, path_suffix)
|
||||
when :defined then next_mod = mod.const_get mod_name
|
||||
when :dir then # We didn't find a file, but there's a dir.
|
||||
next_mod = Module.new # So create a module for the directory
|
||||
mod.send :const_set, mod_name, next_mod
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
mod = next_mod
|
||||
|
||||
return nil unless mod && mod.is_a?(Module)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
protected
|
||||
def safe_load_paths #:nodoc:
|
||||
if defined?(RAILS_ROOT)
|
||||
$LOAD_PATH.select do |base|
|
||||
base = File.expand_path(base)
|
||||
extended_root = File.expand_path(RAILS_ROOT)
|
||||
# Exclude all paths that are not nested within app, lib, or components.
|
||||
base.match(/\A#{Regexp.escape(extended_root)}\/*(app|lib|components)\/[a-z]/) || base =~ %r{rails-[\d.]+/builtin}
|
||||
end
|
||||
else
|
||||
$LOAD_PATH
|
||||
end
|
||||
end
|
||||
|
||||
def attempt_load(mod, const_name, path)
|
||||
has_dir = false
|
||||
safe_load_paths.each do |load_path|
|
||||
full_path = File.join(load_path, path)
|
||||
file_path = full_path + '.rb'
|
||||
if File.file?(file_path) # Found a .rb file? Load it up
|
||||
require_dependency(file_path)
|
||||
return :defined if mod.const_defined? const_name
|
||||
else
|
||||
has_dir ||= File.directory?(full_path)
|
||||
end
|
||||
end
|
||||
return (has_dir ? :dir : nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class PathComponent < DynamicComponent #:nodoc:
|
||||
def optional?() true end
|
||||
def default() [] end
|
||||
def condition() nil end
|
||||
|
||||
def default=(value)
|
||||
raise RoutingError, "All path components have an implicit default of []" unless value == []
|
||||
end
|
||||
|
||||
def write_generation(g)
|
||||
raise RoutingError, 'Path components must occur last' unless g.after.empty?
|
||||
g.if("#{g.hash_value(key, true)} && ! #{g.hash_value(key, true)}.empty?") do
|
||||
g << "#{g.hash_value(key, true)} = #{g.hash_value(key, true)}.join('/') unless #{g.hash_value(key, true)}.is_a?(String)"
|
||||
g.add_segment("\#{CGI.escape_skipping_slashes(#{g.hash_value(key, true)})}") {|gp| gp.finish }
|
||||
end
|
||||
g.else { g.finish }
|
||||
end
|
||||
|
||||
def write_recognition(g)
|
||||
raise RoutingError, "Path components must occur last" unless g.after.empty?
|
||||
|
||||
start = g.index_name
|
||||
start = "(#{start})" unless /^\w+$/ =~ start
|
||||
|
||||
value_expr = "#{g.path_name}[#{start}..-1] || []"
|
||||
g.result key, "ActionController::Routing::PathComponent::Result.new_escaped(#{value_expr})"
|
||||
g.finish(false)
|
||||
end
|
||||
|
||||
class Result < ::Array #:nodoc:
|
||||
def to_s() join '/' end
|
||||
def self.new_escaped(strings)
|
||||
new strings.collect {|str| CGI.unescape str}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Route #:nodoc:
|
||||
attr_accessor :components, :known
|
||||
attr_reader :path, :options, :keys, :defaults
|
||||
|
||||
def initialize(path, options = {})
|
||||
@path, @options = path, options
|
||||
|
||||
initialize_components path
|
||||
defaults, conditions = initialize_hashes options.dup
|
||||
@defaults = defaults.dup
|
||||
configure_components(defaults, conditions)
|
||||
add_default_requirements
|
||||
initialize_keys
|
||||
end
|
||||
|
||||
def inspect
|
||||
"<#{self.class} #{path.inspect}, #{options.inspect[1..-1]}>"
|
||||
end
|
||||
|
||||
def write_generation(generator = CodeGeneration::GenerationGenerator.new)
|
||||
generator.before, generator.current, generator.after = [], components.first, (components[1..-1] || [])
|
||||
|
||||
if known.empty? then generator.go
|
||||
else
|
||||
# Alter the conditions to allow :action => 'index' to also catch :action => nil
|
||||
altered_known = known.collect do |k, v|
|
||||
if k == :action && v== 'index' then [k, [nil, 'index']]
|
||||
else [k, v]
|
||||
end
|
||||
end
|
||||
generator.if(generator.check_conditions(altered_known)) {|gp| gp.go }
|
||||
end
|
||||
|
||||
generator
|
||||
end
|
||||
|
||||
def write_recognition(generator = CodeGeneration::RecognitionGenerator.new)
|
||||
g = generator.dup
|
||||
g.share_locals_with generator
|
||||
g.before, g.current, g.after = [], components.first, (components[1..-1] || [])
|
||||
|
||||
known.each do |key, value|
|
||||
if key == :controller then ControllerComponent.assign_controller(g, value)
|
||||
else g.constant_result(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
g.go
|
||||
|
||||
generator
|
||||
end
|
||||
|
||||
def initialize_keys
|
||||
@keys = (components.collect {|c| c.key} + known.keys).compact
|
||||
@keys.freeze
|
||||
end
|
||||
|
||||
def extra_keys(options)
|
||||
options.keys - @keys
|
||||
end
|
||||
|
||||
def matches_controller?(controller)
|
||||
if known[:controller] then known[:controller] == controller
|
||||
else
|
||||
c = components.find {|c| c.key == :controller}
|
||||
return false unless c
|
||||
return c.condition.nil? || eval(Routing.test_condition('controller', c.condition))
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def initialize_components(path)
|
||||
path = path.split('/') if path.is_a? String
|
||||
path.shift if path.first.blank?
|
||||
self.components = path.collect {|str| Component.new str}
|
||||
end
|
||||
|
||||
def initialize_hashes(options)
|
||||
path_keys = components.collect {|c| c.key }.compact
|
||||
self.known = {}
|
||||
defaults = options.delete(:defaults) || {}
|
||||
conditions = options.delete(:require) || {}
|
||||
conditions.update(options.delete(:requirements) || {})
|
||||
|
||||
options.each do |k, v|
|
||||
if path_keys.include?(k) then (v.is_a?(Regexp) ? conditions : defaults)[k] = v
|
||||
else known[k] = v
|
||||
end
|
||||
end
|
||||
[defaults, conditions]
|
||||
end
|
||||
|
||||
def configure_components(defaults, conditions)
|
||||
components.each do |component|
|
||||
if defaults.key?(component.key) then component.default = defaults[component.key]
|
||||
elsif component.key == :action then component.default = 'index'
|
||||
elsif component.key == :id then component.default = nil
|
||||
end
|
||||
|
||||
component.condition = conditions[component.key] if conditions.key?(component.key)
|
||||
end
|
||||
end
|
||||
|
||||
def add_default_requirements
|
||||
component_keys = components.collect {|c| c.key}
|
||||
known[:action] ||= 'index' unless component_keys.include? :action
|
||||
end
|
||||
end
|
||||
|
||||
class RouteSet #:nodoc:
|
||||
attr_reader :routes, :categories, :controller_to_selector
|
||||
def initialize
|
||||
@routes = []
|
||||
@generation_methods = Hash.new(:generate_default_path)
|
||||
end
|
||||
|
||||
def generate(options, request_or_recall_hash = {})
|
||||
recall = request_or_recall_hash.is_a?(Hash) ? request_or_recall_hash : request_or_recall_hash.symbolized_path_parameters
|
||||
use_recall = true
|
||||
|
||||
controller = options[:controller]
|
||||
options[:action] ||= 'index' if controller
|
||||
recall_controller = recall[:controller]
|
||||
if (recall_controller && recall_controller.include?(?/)) || (controller && controller.include?(?/))
|
||||
recall = {} if controller && controller[0] == ?/
|
||||
options[:controller] = Routing.controller_relative_to(controller, recall_controller)
|
||||
end
|
||||
options = recall.dup if options.empty? # XXX move to url_rewriter?
|
||||
|
||||
keys_to_delete = []
|
||||
Routing.treat_hash(options, keys_to_delete)
|
||||
|
||||
merged = recall.merge(options)
|
||||
keys_to_delete.each {|key| merged.delete key}
|
||||
expire_on = Routing.expiry_hash(options, recall)
|
||||
|
||||
generate_path(merged, options, expire_on)
|
||||
end
|
||||
|
||||
def generate_path(merged, options, expire_on)
|
||||
send @generation_methods[merged[:controller]], merged, options, expire_on
|
||||
end
|
||||
def generate_default_path(*args)
|
||||
write_generation
|
||||
generate_default_path(*args)
|
||||
end
|
||||
|
||||
def write_generation
|
||||
method_sources = []
|
||||
@generation_methods = Hash.new(:generate_default_path)
|
||||
categorize_routes.each do |controller, routes|
|
||||
next unless routes.length < @routes.length
|
||||
|
||||
ivar = controller.gsub('/', '__')
|
||||
method_name = "generate_path_for_#{ivar}".to_sym
|
||||
instance_variable_set "@#{ivar}", routes
|
||||
code = generation_code_for(ivar, method_name).to_s
|
||||
method_sources << code
|
||||
|
||||
filename = "generated_code/routing/generation_for_controller_#{controller}.rb"
|
||||
eval(code, nil, filename)
|
||||
|
||||
@generation_methods[controller.to_s] = method_name
|
||||
@generation_methods[controller.to_sym] = method_name
|
||||
end
|
||||
|
||||
code = generation_code_for('routes', 'generate_default_path').to_s
|
||||
eval(code, nil, 'generated_code/routing/generation.rb')
|
||||
|
||||
return (method_sources << code)
|
||||
end
|
||||
|
||||
def recognize(request)
|
||||
string_path = request.path
|
||||
string_path.chomp! if string_path[0] == ?/
|
||||
path = string_path.split '/'
|
||||
path.shift
|
||||
|
||||
hash = recognize_path(path)
|
||||
return recognition_failed(request) unless hash && hash['controller']
|
||||
|
||||
controller = hash['controller']
|
||||
hash['controller'] = controller.controller_path
|
||||
request.path_parameters = hash
|
||||
controller.new
|
||||
end
|
||||
alias :recognize! :recognize
|
||||
|
||||
def recognition_failed(request)
|
||||
raise ActionController::RoutingError, "Recognition failed for #{request.path.inspect}"
|
||||
end
|
||||
|
||||
def write_recognition
|
||||
g = generator = CodeGeneration::RecognitionGenerator.new
|
||||
g.finish_statement = Proc.new {|hash_expr| "return #{hash_expr}"}
|
||||
|
||||
g.def "self.recognize_path(path)" do
|
||||
each do |route|
|
||||
g << 'index = 0'
|
||||
route.write_recognition(g)
|
||||
end
|
||||
end
|
||||
|
||||
eval g.to_s, nil, 'generated/routing/recognition.rb'
|
||||
return g.to_s
|
||||
end
|
||||
|
||||
def generation_code_for(ivar = 'routes', method_name = nil)
|
||||
routes = instance_variable_get('@' + ivar)
|
||||
key_ivar = "@keys_for_#{ivar}"
|
||||
instance_variable_set(key_ivar, routes.collect {|route| route.keys})
|
||||
|
||||
g = generator = CodeGeneration::GenerationGenerator.new
|
||||
g.def "self.#{method_name}(merged, options, expire_on)" do
|
||||
g << 'unused_count = options.length + 1'
|
||||
g << "unused_keys = keys = options.keys"
|
||||
g << 'path = nil'
|
||||
|
||||
routes.each_with_index do |route, index|
|
||||
g << "new_unused_keys = keys - #{key_ivar}[#{index}]"
|
||||
g << 'new_path = ('
|
||||
g.source.indent do
|
||||
if index.zero?
|
||||
g << "new_unused_count = new_unused_keys.length"
|
||||
g << "hash = merged; not_expired = true"
|
||||
route.write_generation(g.dup)
|
||||
else
|
||||
g.if "(new_unused_count = new_unused_keys.length) < unused_count" do |gp|
|
||||
gp << "hash = merged; not_expired = true"
|
||||
route.write_generation(gp)
|
||||
end
|
||||
end
|
||||
end
|
||||
g.source.lines.last << ' )' # Add the closing brace to the end line
|
||||
g.if 'new_path' do
|
||||
g << 'return new_path, [] if new_unused_count.zero?'
|
||||
g << 'path = new_path; unused_keys = new_unused_keys; unused_count = new_unused_count'
|
||||
end
|
||||
end
|
||||
|
||||
g << "raise RoutingError, \"No url can be generated for the hash \#{options.inspect}\" unless path"
|
||||
g << "return path, unused_keys"
|
||||
end
|
||||
|
||||
return g
|
||||
end
|
||||
|
||||
def categorize_routes
|
||||
@categorized_routes = by_controller = Hash.new(self)
|
||||
|
||||
known_controllers.each do |name|
|
||||
set = by_controller[name] = []
|
||||
each do |route|
|
||||
set << route if route.matches_controller? name
|
||||
end
|
||||
end
|
||||
|
||||
@categorized_routes
|
||||
end
|
||||
|
||||
def known_controllers
|
||||
@routes.inject([]) do |known, route|
|
||||
if (controller = route.known[:controller])
|
||||
if controller.is_a?(Regexp)
|
||||
known << controller.source.scan(%r{[\w\d/]+}).select {|word| controller =~ word}
|
||||
else known << controller
|
||||
end
|
||||
end
|
||||
known
|
||||
end.uniq
|
||||
end
|
||||
|
||||
def reload
|
||||
NamedRoutes.clear
|
||||
|
||||
if defined?(RAILS_ROOT) then load(File.join(RAILS_ROOT, 'config', 'routes.rb'))
|
||||
else connect(':controller/:action/:id', :action => 'index', :id => nil)
|
||||
end
|
||||
|
||||
NamedRoutes.install
|
||||
end
|
||||
|
||||
def connect(*args)
|
||||
new_route = Route.new(*args)
|
||||
@routes << new_route
|
||||
return new_route
|
||||
end
|
||||
|
||||
def draw
|
||||
old_routes = @routes
|
||||
@routes = []
|
||||
|
||||
begin yield self
|
||||
rescue
|
||||
@routes = old_routes
|
||||
raise
|
||||
end
|
||||
write_generation
|
||||
write_recognition
|
||||
end
|
||||
|
||||
def empty?() @routes.empty? end
|
||||
|
||||
def each(&block) @routes.each(&block) end
|
||||
|
||||
# Defines a new named route with the provided name and arguments.
|
||||
# This method need only be used when you wish to use a name that a RouteSet instance
|
||||
# method exists for, such as categories.
|
||||
#
|
||||
# For example, map.categories '/categories', :controller => 'categories' will not work
|
||||
# due to RouteSet#categories.
|
||||
def named_route(name, path, hash = {})
|
||||
route = connect(path, hash)
|
||||
NamedRoutes.name_route(route, name)
|
||||
route
|
||||
end
|
||||
|
||||
def method_missing(name, *args)
|
||||
(1..2).include?(args.length) ? named_route(name, *args) : super(name, *args)
|
||||
end
|
||||
|
||||
def extra_keys(options, recall = {})
|
||||
generate(options.dup, recall).last
|
||||
end
|
||||
end
|
||||
|
||||
module NamedRoutes #:nodoc:
|
||||
Helpers = []
|
||||
class << self
|
||||
def clear() Helpers.clear end
|
||||
|
||||
def hash_access_name(name)
|
||||
"hash_for_#{name}_url"
|
||||
end
|
||||
|
||||
def url_helper_name(name)
|
||||
"#{name}_url"
|
||||
end
|
||||
|
||||
def known_hash_for_route(route)
|
||||
hash = route.known.symbolize_keys
|
||||
route.defaults.each do |key, value|
|
||||
hash[key.to_sym] ||= value if value
|
||||
end
|
||||
hash[:controller] = "/#{hash[:controller]}"
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def define_hash_access_method(route, name)
|
||||
hash = known_hash_for_route(route)
|
||||
define_method(hash_access_name(name)) do |*args|
|
||||
args.first ? hash.merge(args.first) : hash
|
||||
end
|
||||
end
|
||||
|
||||
def name_route(route, name)
|
||||
define_hash_access_method(route, name)
|
||||
|
||||
module_eval(%{def #{url_helper_name name}(options = {})
|
||||
url_for(#{hash_access_name(name)}.merge(options))
|
||||
end}, "generated/routing/named_routes/#{name}.rb")
|
||||
|
||||
protected url_helper_name(name), hash_access_name(name)
|
||||
|
||||
Helpers << url_helper_name(name).to_sym
|
||||
Helpers << hash_access_name(name).to_sym
|
||||
Helpers.uniq!
|
||||
end
|
||||
|
||||
def install(cls = ActionController::Base)
|
||||
cls.send :include, self
|
||||
if cls.respond_to? :helper_method
|
||||
Helpers.each do |helper_name|
|
||||
cls.send :helper_method, helper_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Routes = RouteSet.new
|
||||
end
|
||||
end
|
190
vendor/rails/actionpack/lib/action_controller/scaffolding.rb
vendored
Normal file
190
vendor/rails/actionpack/lib/action_controller/scaffolding.rb
vendored
Normal file
|
@ -0,0 +1,190 @@
|
|||
module ActionController
|
||||
module Scaffolding # :nodoc:
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Scaffolding is a way to quickly put an Active Record class online by providing a series of standardized actions
|
||||
# for listing, showing, creating, updating, and destroying objects of the class. These standardized actions come
|
||||
# with both controller logic and default templates that through introspection already know which fields to display
|
||||
# and which input types to use. Example:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# scaffold :entry
|
||||
# end
|
||||
#
|
||||
# This tiny piece of code will add all of the following methods to the controller:
|
||||
#
|
||||
# class WeblogController < ActionController::Base
|
||||
# verify :method => :post, :only => [ :destroy, :create, :update ],
|
||||
# :redirect_to => { :action => :list }
|
||||
#
|
||||
# def index
|
||||
# list
|
||||
# end
|
||||
#
|
||||
# def list
|
||||
# @entries = Entry.find_all
|
||||
# render_scaffold "list"
|
||||
# end
|
||||
#
|
||||
# def show
|
||||
# @entry = Entry.find(params[:id])
|
||||
# render_scaffold
|
||||
# end
|
||||
#
|
||||
# def destroy
|
||||
# Entry.find(params[:id]).destroy
|
||||
# redirect_to :action => "list"
|
||||
# end
|
||||
#
|
||||
# def new
|
||||
# @entry = Entry.new
|
||||
# render_scaffold
|
||||
# end
|
||||
#
|
||||
# def create
|
||||
# @entry = Entry.new(params[:entry])
|
||||
# if @entry.save
|
||||
# flash[:notice] = "Entry was successfully created"
|
||||
# redirect_to :action => "list"
|
||||
# else
|
||||
# render_scaffold('new')
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def edit
|
||||
# @entry = Entry.find(params[:id])
|
||||
# render_scaffold
|
||||
# end
|
||||
#
|
||||
# def update
|
||||
# @entry = Entry.find(params[:id])
|
||||
# @entry.attributes = params[:entry]
|
||||
#
|
||||
# if @entry.save
|
||||
# flash[:notice] = "Entry was successfully updated"
|
||||
# redirect_to :action => "show", :id => @entry
|
||||
# else
|
||||
# render_scaffold('edit')
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# The <tt>render_scaffold</tt> method will first check to see if you've made your own template (like "weblog/show.rhtml" for
|
||||
# the show action) and if not, then render the generic template for that action. This gives you the possibility of using the
|
||||
# scaffold while you're building your specific application. Start out with a totally generic setup, then replace one template
|
||||
# and one action at a time while relying on the rest of the scaffolded templates and actions.
|
||||
module ClassMethods
|
||||
# Adds a swath of generic CRUD actions to the controller. The +model_id+ is automatically converted into a class name unless
|
||||
# one is specifically provide through <tt>options[:class_name]</tt>. So <tt>scaffold :post</tt> would use Post as the class
|
||||
# and @post/@posts for the instance variables.
|
||||
#
|
||||
# It's possible to use more than one scaffold in a single controller by specifying <tt>options[:suffix] = true</tt>. This will
|
||||
# make <tt>scaffold :post, :suffix => true</tt> use method names like list_post, show_post, and create_post
|
||||
# instead of just list, show, and post. If suffix is used, then no index method is added.
|
||||
def scaffold(model_id, options = {})
|
||||
options.assert_valid_keys(:class_name, :suffix)
|
||||
|
||||
singular_name = model_id.to_s
|
||||
class_name = options[:class_name] || singular_name.camelize
|
||||
plural_name = singular_name.pluralize
|
||||
suffix = options[:suffix] ? "_#{singular_name}" : ""
|
||||
|
||||
unless options[:suffix]
|
||||
module_eval <<-"end_eval", __FILE__, __LINE__
|
||||
def index
|
||||
list
|
||||
end
|
||||
end_eval
|
||||
end
|
||||
|
||||
module_eval <<-"end_eval", __FILE__, __LINE__
|
||||
|
||||
verify :method => :post, :only => [ :destroy#{suffix}, :create#{suffix}, :update#{suffix} ],
|
||||
:redirect_to => { :action => :list#{suffix} }
|
||||
|
||||
|
||||
def list#{suffix}
|
||||
@#{singular_name}_pages, @#{plural_name} = paginate :#{plural_name}, :per_page => 10
|
||||
render#{suffix}_scaffold "list#{suffix}"
|
||||
end
|
||||
|
||||
def show#{suffix}
|
||||
@#{singular_name} = #{class_name}.find(params[:id])
|
||||
render#{suffix}_scaffold
|
||||
end
|
||||
|
||||
def destroy#{suffix}
|
||||
#{class_name}.find(params[:id]).destroy
|
||||
redirect_to :action => "list#{suffix}"
|
||||
end
|
||||
|
||||
def new#{suffix}
|
||||
@#{singular_name} = #{class_name}.new
|
||||
render#{suffix}_scaffold
|
||||
end
|
||||
|
||||
def create#{suffix}
|
||||
@#{singular_name} = #{class_name}.new(params[:#{singular_name}])
|
||||
if @#{singular_name}.save
|
||||
flash[:notice] = "#{class_name} was successfully created"
|
||||
redirect_to :action => "list#{suffix}"
|
||||
else
|
||||
render#{suffix}_scaffold('new')
|
||||
end
|
||||
end
|
||||
|
||||
def edit#{suffix}
|
||||
@#{singular_name} = #{class_name}.find(params[:id])
|
||||
render#{suffix}_scaffold
|
||||
end
|
||||
|
||||
def update#{suffix}
|
||||
@#{singular_name} = #{class_name}.find(params[:id])
|
||||
@#{singular_name}.attributes = params[:#{singular_name}]
|
||||
|
||||
if @#{singular_name}.save
|
||||
flash[:notice] = "#{class_name} was successfully updated"
|
||||
redirect_to :action => "show#{suffix}", :id => @#{singular_name}
|
||||
else
|
||||
render#{suffix}_scaffold('edit')
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def render#{suffix}_scaffold(action=nil)
|
||||
action ||= caller_method_name(caller)
|
||||
# logger.info ("testing template:" + "\#{self.class.controller_path}/\#{action}") if logger
|
||||
|
||||
if template_exists?("\#{self.class.controller_path}/\#{action}")
|
||||
render_action(action)
|
||||
else
|
||||
@scaffold_class = #{class_name}
|
||||
@scaffold_singular_name, @scaffold_plural_name = "#{singular_name}", "#{plural_name}"
|
||||
@scaffold_suffix = "#{suffix}"
|
||||
add_instance_variables_to_assigns
|
||||
|
||||
@template.instance_variable_set("@content_for_layout", @template.render_file(scaffold_path(action.sub(/#{suffix}$/, "")), false))
|
||||
|
||||
if !active_layout.nil?
|
||||
render_file(active_layout, nil, true)
|
||||
else
|
||||
render_file(scaffold_path("layout"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def scaffold_path(template_name)
|
||||
File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".rhtml"
|
||||
end
|
||||
|
||||
def caller_method_name(caller)
|
||||
caller.first.scan(/`(.*)'/).first.first # ' ruby-mode
|
||||
end
|
||||
end_eval
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
333
vendor/rails/actionpack/lib/action_controller/session/active_record_store.rb
vendored
Normal file
333
vendor/rails/actionpack/lib/action_controller/session/active_record_store.rb
vendored
Normal file
|
@ -0,0 +1,333 @@
|
|||
require 'cgi'
|
||||
require 'cgi/session'
|
||||
require 'digest/md5'
|
||||
require 'base64'
|
||||
|
||||
class CGI
|
||||
class Session
|
||||
# Return this session's underlying Session instance. Useful for the DB-backed session stores.
|
||||
def model
|
||||
@dbman.model if @dbman
|
||||
end
|
||||
|
||||
|
||||
# A session store backed by an Active Record class. A default class is
|
||||
# provided, but any object duck-typing to an Active Record +Session+ class
|
||||
# with text +session_id+ and +data+ attributes is sufficient.
|
||||
#
|
||||
# The default assumes a +sessions+ tables with columns:
|
||||
# +id+ (numeric primary key),
|
||||
# +session_id+ (text, or longtext if your session data exceeds 65K), and
|
||||
# +data+ (text or longtext; careful if your session data exceeds 65KB).
|
||||
# The +session_id+ column should always be indexed for speedy lookups.
|
||||
# Session data is marshaled to the +data+ column in Base64 format.
|
||||
# If the data you write is larger than the column's size limit,
|
||||
# ActionController::SessionOverflowError will be raised.
|
||||
#
|
||||
# You may configure the table name, primary key, and data column.
|
||||
# For example, at the end of config/environment.rb:
|
||||
# CGI::Session::ActiveRecordStore::Session.table_name = 'legacy_session_table'
|
||||
# CGI::Session::ActiveRecordStore::Session.primary_key = 'session_id'
|
||||
# CGI::Session::ActiveRecordStore::Session.data_column_name = 'legacy_session_data'
|
||||
# Note that setting the primary key to the session_id frees you from
|
||||
# having a separate id column if you don't want it. However, you must
|
||||
# set session.model.id = session.session_id by hand! A before_filter
|
||||
# on ApplicationController is a good place.
|
||||
#
|
||||
# Since the default class is a simple Active Record, you get timestamps
|
||||
# for free if you add +created_at+ and +updated_at+ datetime columns to
|
||||
# the +sessions+ table, making periodic session expiration a snap.
|
||||
#
|
||||
# You may provide your own session class implementation, whether a
|
||||
# feature-packed Active Record or a bare-metal high-performance SQL
|
||||
# store, by setting
|
||||
# +CGI::Session::ActiveRecordStore.session_class = MySessionClass+
|
||||
# You must implement these methods:
|
||||
# self.find_by_session_id(session_id)
|
||||
# initialize(hash_of_session_id_and_data)
|
||||
# attr_reader :session_id
|
||||
# attr_accessor :data
|
||||
# save
|
||||
# destroy
|
||||
#
|
||||
# The example SqlBypass class is a generic SQL session store. You may
|
||||
# use it as a basis for high-performance database-specific stores.
|
||||
class ActiveRecordStore
|
||||
# The default Active Record class.
|
||||
class Session < ActiveRecord::Base
|
||||
# Customizable data column name. Defaults to 'data'.
|
||||
cattr_accessor :data_column_name
|
||||
self.data_column_name = 'data'
|
||||
|
||||
before_save :marshal_data!
|
||||
before_save :raise_on_session_data_overflow!
|
||||
|
||||
class << self
|
||||
# Don't try to reload ARStore::Session in dev mode.
|
||||
def reloadable? #:nodoc:
|
||||
false
|
||||
end
|
||||
|
||||
def data_column_size_limit
|
||||
@data_column_size_limit ||= columns_hash[@@data_column_name].limit
|
||||
end
|
||||
|
||||
# Hook to set up sessid compatibility.
|
||||
def find_by_session_id(session_id)
|
||||
setup_sessid_compatibility!
|
||||
find_by_session_id(session_id)
|
||||
end
|
||||
|
||||
def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
|
||||
def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
|
||||
|
||||
def create_table!
|
||||
connection.execute <<-end_sql
|
||||
CREATE TABLE #{table_name} (
|
||||
id INTEGER PRIMARY KEY,
|
||||
#{connection.quote_column_name('session_id')} TEXT UNIQUE,
|
||||
#{connection.quote_column_name(@@data_column_name)} TEXT(255)
|
||||
)
|
||||
end_sql
|
||||
end
|
||||
|
||||
def drop_table!
|
||||
connection.execute "DROP TABLE #{table_name}"
|
||||
end
|
||||
|
||||
private
|
||||
# Compatibility with tables using sessid instead of session_id.
|
||||
def setup_sessid_compatibility!
|
||||
# Reset column info since it may be stale.
|
||||
reset_column_information
|
||||
if columns_hash['sessid']
|
||||
def self.find_by_session_id(*args)
|
||||
find_by_sessid(*args)
|
||||
end
|
||||
|
||||
define_method(:session_id) { sessid }
|
||||
define_method(:session_id=) { |session_id| self.sessid = session_id }
|
||||
else
|
||||
def self.find_by_session_id(session_id)
|
||||
find :first, :conditions => ["session_id #{attribute_condition(session_id)}", session_id]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Lazy-unmarshal session state.
|
||||
def data
|
||||
@data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
|
||||
end
|
||||
|
||||
# Has the session been loaded yet?
|
||||
def loaded?
|
||||
!! @data
|
||||
end
|
||||
|
||||
private
|
||||
attr_writer :data
|
||||
|
||||
def marshal_data!
|
||||
return false if !loaded?
|
||||
write_attribute(@@data_column_name, self.class.marshal(self.data))
|
||||
end
|
||||
|
||||
# Ensures that the data about to be stored in the database is not
|
||||
# larger than the data storage column. Raises
|
||||
# ActionController::SessionOverflowError.
|
||||
def raise_on_session_data_overflow!
|
||||
return false if !loaded?
|
||||
limit = self.class.data_column_size_limit
|
||||
if loaded? and limit and read_attribute(@@data_column_name).size > limit
|
||||
raise ActionController::SessionOverflowError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# A barebones session store which duck-types with the default session
|
||||
# store but bypasses Active Record and issues SQL directly. This is
|
||||
# an example session model class meant as a basis for your own classes.
|
||||
#
|
||||
# The database connection, table name, and session id and data columns
|
||||
# are configurable class attributes. Marshaling and unmarshaling
|
||||
# are implemented as class methods that you may override. By default,
|
||||
# marshaling data is +Base64.encode64(Marshal.dump(data))+ and
|
||||
# unmarshaling data is +Marshal.load(Base64.decode64(data))+.
|
||||
#
|
||||
# This marshaling behavior is intended to store the widest range of
|
||||
# binary session data in a +text+ column. For higher performance,
|
||||
# store in a +blob+ column instead and forgo the Base64 encoding.
|
||||
class SqlBypass
|
||||
# Use the ActiveRecord::Base.connection by default.
|
||||
cattr_accessor :connection
|
||||
|
||||
# The table name defaults to 'sessions'.
|
||||
cattr_accessor :table_name
|
||||
@@table_name = 'sessions'
|
||||
|
||||
# The session id field defaults to 'session_id'.
|
||||
cattr_accessor :session_id_column
|
||||
@@session_id_column = 'session_id'
|
||||
|
||||
# The data field defaults to 'data'.
|
||||
cattr_accessor :data_column
|
||||
@@data_column = 'data'
|
||||
|
||||
class << self
|
||||
|
||||
def connection
|
||||
@@connection ||= ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
# Look up a session by id and unmarshal its data if found.
|
||||
def find_by_session_id(session_id)
|
||||
if record = @@connection.select_one("SELECT * FROM #{@@table_name} WHERE #{@@session_id_column}=#{@@connection.quote(session_id)}")
|
||||
new(:session_id => session_id, :marshaled_data => record['data'])
|
||||
end
|
||||
end
|
||||
|
||||
def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
|
||||
def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
|
||||
|
||||
def create_table!
|
||||
@@connection.execute <<-end_sql
|
||||
CREATE TABLE #{table_name} (
|
||||
id INTEGER PRIMARY KEY,
|
||||
#{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE,
|
||||
#{@@connection.quote_column_name(data_column)} TEXT
|
||||
)
|
||||
end_sql
|
||||
end
|
||||
|
||||
def drop_table!
|
||||
@@connection.execute "DROP TABLE #{table_name}"
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :session_id
|
||||
attr_writer :data
|
||||
|
||||
# Look for normal and marshaled data, self.find_by_session_id's way of
|
||||
# telling us to postpone unmarshaling until the data is requested.
|
||||
# We need to handle a normal data attribute in case of a new record.
|
||||
def initialize(attributes)
|
||||
@session_id, @data, @marshaled_data = attributes[:session_id], attributes[:data], attributes[:marshaled_data]
|
||||
@new_record = @marshaled_data.nil?
|
||||
end
|
||||
|
||||
def new_record?
|
||||
@new_record
|
||||
end
|
||||
|
||||
# Lazy-unmarshal session state.
|
||||
def data
|
||||
unless @data
|
||||
if @marshaled_data
|
||||
@data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil
|
||||
else
|
||||
@data = {}
|
||||
end
|
||||
end
|
||||
@data
|
||||
end
|
||||
|
||||
def loaded?
|
||||
!! @data
|
||||
end
|
||||
|
||||
def save
|
||||
return false if !loaded?
|
||||
marshaled_data = self.class.marshal(data)
|
||||
|
||||
if @new_record
|
||||
@new_record = false
|
||||
@@connection.update <<-end_sql, 'Create session'
|
||||
INSERT INTO #{@@table_name} (
|
||||
#{@@connection.quote_column_name(@@session_id_column)},
|
||||
#{@@connection.quote_column_name(@@data_column)} )
|
||||
VALUES (
|
||||
#{@@connection.quote(session_id)},
|
||||
#{@@connection.quote(marshaled_data)} )
|
||||
end_sql
|
||||
else
|
||||
@@connection.update <<-end_sql, 'Update session'
|
||||
UPDATE #{@@table_name}
|
||||
SET #{@@connection.quote_column_name(@@data_column)}=#{@@connection.quote(marshaled_data)}
|
||||
WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
|
||||
end_sql
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
unless @new_record
|
||||
@@connection.delete <<-end_sql, 'Destroy session'
|
||||
DELETE FROM #{@@table_name}
|
||||
WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)}
|
||||
end_sql
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# The class used for session storage. Defaults to
|
||||
# CGI::Session::ActiveRecordStore::Session.
|
||||
cattr_accessor :session_class
|
||||
self.session_class = Session
|
||||
|
||||
# Find or instantiate a session given a CGI::Session.
|
||||
def initialize(session, option = nil)
|
||||
session_id = session.session_id
|
||||
unless @session = ActiveRecord::Base.silence { @@session_class.find_by_session_id(session_id) }
|
||||
unless session.new_session
|
||||
raise CGI::Session::NoSession, 'uninitialized session'
|
||||
end
|
||||
@session = @@session_class.new(:session_id => session_id, :data => {})
|
||||
# session saving can be lazy again, because of improved component implementation
|
||||
# therefore next line gets commented out:
|
||||
# @session.save
|
||||
end
|
||||
end
|
||||
|
||||
# Access the underlying session model.
|
||||
def model
|
||||
@session
|
||||
end
|
||||
|
||||
# Restore session state. The session model handles unmarshaling.
|
||||
def restore
|
||||
if @session
|
||||
@session.data
|
||||
end
|
||||
end
|
||||
|
||||
# Save session store.
|
||||
def update
|
||||
if @session
|
||||
ActiveRecord::Base.silence { @session.save }
|
||||
end
|
||||
end
|
||||
|
||||
# Save and close the session store.
|
||||
def close
|
||||
if @session
|
||||
update
|
||||
@session = nil
|
||||
end
|
||||
end
|
||||
|
||||
# Delete and close the session store.
|
||||
def delete
|
||||
if @session
|
||||
ActiveRecord::Base.silence { @session.destroy }
|
||||
@session = nil
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def logger
|
||||
ActionController::Base.logger rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
32
vendor/rails/actionpack/lib/action_controller/session/drb_server.rb
vendored
Normal file
32
vendor/rails/actionpack/lib/action_controller/session/drb_server.rb
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
#!/usr/local/bin/ruby -w
|
||||
|
||||
# This is a really simple session storage daemon, basically just a hash,
|
||||
# which is enabled for DRb access.
|
||||
|
||||
require 'drb'
|
||||
|
||||
session_hash = Hash.new
|
||||
session_hash.instance_eval { @mutex = Mutex.new }
|
||||
|
||||
class <<session_hash
|
||||
def []=(key, value)
|
||||
@mutex.synchronize do
|
||||
super(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@mutex.synchronize do
|
||||
super(key)
|
||||
end
|
||||
end
|
||||
|
||||
def delete(key)
|
||||
@mutex.synchronize do
|
||||
super(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DRb.start_service('druby://127.0.0.1:9192', session_hash)
|
||||
DRb.thread.join
|
31
vendor/rails/actionpack/lib/action_controller/session/drb_store.rb
vendored
Normal file
31
vendor/rails/actionpack/lib/action_controller/session/drb_store.rb
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
require 'cgi'
|
||||
require 'cgi/session'
|
||||
require 'drb'
|
||||
|
||||
class CGI #:nodoc:all
|
||||
class Session
|
||||
class DRbStore
|
||||
@@session_data = DRbObject.new(nil, 'druby://localhost:9192')
|
||||
|
||||
def initialize(session, option=nil)
|
||||
@session_id = session.session_id
|
||||
end
|
||||
|
||||
def restore
|
||||
@h = @@session_data[@session_id] || {}
|
||||
end
|
||||
|
||||
def update
|
||||
@@session_data[@session_id] = @h
|
||||
end
|
||||
|
||||
def close
|
||||
update
|
||||
end
|
||||
|
||||
def delete
|
||||
@@session_data.delete(@session_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
101
vendor/rails/actionpack/lib/action_controller/session/mem_cache_store.rb
vendored
Normal file
101
vendor/rails/actionpack/lib/action_controller/session/mem_cache_store.rb
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
# cgi/session/memcached.rb - persistent storage of marshalled session data
|
||||
#
|
||||
# == Overview
|
||||
#
|
||||
# This file provides the CGI::Session::MemCache class, which builds
|
||||
# persistence of storage data on top of the MemCache library. See
|
||||
# cgi/session.rb for more details on session storage managers.
|
||||
#
|
||||
|
||||
begin
|
||||
require 'cgi/session'
|
||||
require 'memcache'
|
||||
|
||||
class CGI
|
||||
class Session
|
||||
# MemCache-based session storage class.
|
||||
#
|
||||
# This builds upon the top-level MemCache class provided by the
|
||||
# library file memcache.rb. Session data is marshalled and stored
|
||||
# in a memcached cache.
|
||||
class MemCacheStore
|
||||
def check_id(id) #:nodoc:#
|
||||
/[^0-9a-zA-Z]+/ =~ id.to_s ? false : true
|
||||
end
|
||||
|
||||
# Create a new CGI::Session::MemCache instance
|
||||
#
|
||||
# This constructor is used internally by CGI::Session. The
|
||||
# user does not generally need to call it directly.
|
||||
#
|
||||
# +session+ is the session for which this instance is being
|
||||
# created. The session id must only contain alphanumeric
|
||||
# characters; automatically generated session ids observe
|
||||
# this requirement.
|
||||
#
|
||||
# +options+ is a hash of options for the initializer. The
|
||||
# following options are recognized:
|
||||
#
|
||||
# cache:: an instance of a MemCache client to use as the
|
||||
# session cache.
|
||||
#
|
||||
# expires:: an expiry time value to use for session entries in
|
||||
# the session cache. +expires+ is interpreted in seconds
|
||||
# relative to the current time if it’s less than 60*60*24*30
|
||||
# (30 days), or as an absolute Unix time (e.g., Time#to_i) if
|
||||
# greater. If +expires+ is +0+, or not passed on +options+,
|
||||
# the entry will never expire.
|
||||
#
|
||||
# This session's memcache entry will be created if it does
|
||||
# not exist, or retrieved if it does.
|
||||
def initialize(session, options = {})
|
||||
id = session.session_id
|
||||
unless check_id(id)
|
||||
raise ArgumentError, "session_id '%s' is invalid" % id
|
||||
end
|
||||
@cache = options['cache'] || MemCache.new('localhost')
|
||||
@expires = options['expires'] || 0
|
||||
@session_key = "session:#{id}"
|
||||
@session_data = {}
|
||||
end
|
||||
|
||||
# Restore session state from the session's memcache entry.
|
||||
#
|
||||
# Returns the session state as a hash.
|
||||
def restore
|
||||
begin
|
||||
@session_data = @cache[@session_key] || {}
|
||||
rescue
|
||||
@session_data = {}
|
||||
end
|
||||
end
|
||||
|
||||
# Save session state to the session's memcache entry.
|
||||
def update
|
||||
begin
|
||||
@cache.set(@session_key, @session_data, @expires)
|
||||
rescue
|
||||
# Ignore session update failures.
|
||||
end
|
||||
end
|
||||
|
||||
# Update and close the session's memcache entry.
|
||||
def close
|
||||
update
|
||||
end
|
||||
|
||||
# Delete the session's memcache entry.
|
||||
def delete
|
||||
begin
|
||||
@cache.delete(@session_key)
|
||||
rescue
|
||||
# Ignore session delete failures.
|
||||
end
|
||||
@session_data = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
# MemCache wasn't available so neither can the store be
|
||||
end
|
145
vendor/rails/actionpack/lib/action_controller/session_management.rb
vendored
Normal file
145
vendor/rails/actionpack/lib/action_controller/session_management.rb
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
require 'action_controller/session/drb_store'
|
||||
require 'action_controller/session/mem_cache_store'
|
||||
if Object.const_defined?(:ActiveRecord)
|
||||
require 'action_controller/session/active_record_store'
|
||||
end
|
||||
|
||||
module ActionController #:nodoc:
|
||||
module SessionManagement #:nodoc:
|
||||
def self.included(base)
|
||||
base.extend(ClassMethods)
|
||||
|
||||
base.send :alias_method, :process_without_session_management_support, :process
|
||||
base.send :alias_method, :process, :process_with_session_management_support
|
||||
|
||||
base.send :alias_method, :process_cleanup_without_session_management_support, :process_cleanup
|
||||
base.send :alias_method, :process_cleanup, :process_cleanup_with_session_management_support
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Set the session store to be used for keeping the session data between requests. The default is using the
|
||||
# file system, but you can also specify one of the other included stores (:active_record_store, :drb_store,
|
||||
# :mem_cache_store, or :memory_store) or use your own class.
|
||||
def session_store=(store)
|
||||
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager] =
|
||||
store.is_a?(Symbol) ? CGI::Session.const_get(store == :drb_store ? "DRbStore" : store.to_s.camelize) : store
|
||||
end
|
||||
|
||||
# Returns the session store class currently used.
|
||||
def session_store
|
||||
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager]
|
||||
end
|
||||
|
||||
# Returns the hash used to configure the session. Example use:
|
||||
#
|
||||
# ActionController::Base.session_options[:session_secure] = true # session only available over HTTPS
|
||||
def session_options
|
||||
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
|
||||
end
|
||||
|
||||
# Specify how sessions ought to be managed for a subset of the actions on
|
||||
# the controller. Like filters, you can specify <tt>:only</tt> and
|
||||
# <tt>:except</tt> clauses to restrict the subset, otherwise options
|
||||
# apply to all actions on this controller.
|
||||
#
|
||||
# The session options are inheritable, as well, so if you specify them in
|
||||
# a parent controller, they apply to controllers that extend the parent.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # turn off session management for all actions.
|
||||
# session :off
|
||||
#
|
||||
# # turn off session management for all actions _except_ foo and bar.
|
||||
# session :off, :except => %w(foo bar)
|
||||
#
|
||||
# # turn off session management for only the foo and bar actions.
|
||||
# session :off, :only => %w(foo bar)
|
||||
#
|
||||
# # the session will only work over HTTPS, but only for the foo action
|
||||
# session :only => :foo, :session_secure => true
|
||||
#
|
||||
# # the session will only be disabled for 'foo', and only if it is
|
||||
# # requested as a web service
|
||||
# session :off, :only => :foo,
|
||||
# :if => Proc.new { |req| req.parameters[:ws] }
|
||||
#
|
||||
# All session options described for ActionController::Base.process_cgi
|
||||
# are valid arguments.
|
||||
def session(*args)
|
||||
options = Hash === args.last ? args.pop : {}
|
||||
|
||||
options[:disabled] = true if !args.empty?
|
||||
options[:only] = [*options[:only]].map { |o| o.to_s } if options[:only]
|
||||
options[:except] = [*options[:except]].map { |o| o.to_s } if options[:except]
|
||||
if options[:only] && options[:except]
|
||||
raise ArgumentError, "only one of either :only or :except are allowed"
|
||||
end
|
||||
|
||||
write_inheritable_array("session_options", [options])
|
||||
end
|
||||
|
||||
def cached_session_options #:nodoc:
|
||||
@session_options ||= read_inheritable_attribute("session_options") || []
|
||||
end
|
||||
|
||||
def session_options_for(request, action) #:nodoc:
|
||||
if (session_options = cached_session_options).empty?
|
||||
{}
|
||||
else
|
||||
options = {}
|
||||
|
||||
action = action.to_s
|
||||
session_options.each do |opts|
|
||||
next if opts[:if] && !opts[:if].call(request)
|
||||
if opts[:only] && opts[:only].include?(action)
|
||||
options.merge!(opts)
|
||||
elsif opts[:except] && !opts[:except].include?(action)
|
||||
options.merge!(opts)
|
||||
elsif !opts[:only] && !opts[:except]
|
||||
options.merge!(opts)
|
||||
end
|
||||
end
|
||||
|
||||
if options.empty? then options
|
||||
else
|
||||
options.delete :only
|
||||
options.delete :except
|
||||
options.delete :if
|
||||
options[:disabled] ? false : options
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process_with_session_management_support(request, response, method = :perform_action, *arguments) #:nodoc:
|
||||
set_session_options(request)
|
||||
process_without_session_management_support(request, response, method, *arguments)
|
||||
end
|
||||
|
||||
private
|
||||
def set_session_options(request)
|
||||
request.session_options = self.class.session_options_for(request, request.parameters["action"] || "index")
|
||||
end
|
||||
|
||||
def process_cleanup_with_session_management_support
|
||||
process_cleanup_without_session_management_support
|
||||
clear_persistent_model_associations
|
||||
end
|
||||
|
||||
# Clear cached associations in session data so they don't overflow
|
||||
# the database field. Only applies to ActiveRecordStore since there
|
||||
# is not a standard way to iterate over session data.
|
||||
def clear_persistent_model_associations #:doc:
|
||||
if defined?(@session) && @session.instance_variables.include?('@data')
|
||||
session_data = @session.instance_variable_get('@data')
|
||||
|
||||
if session_data && session_data.respond_to?(:each_value)
|
||||
session_data.each_value do |obj|
|
||||
obj.clear_association_cache if obj.respond_to?(:clear_association_cache)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
147
vendor/rails/actionpack/lib/action_controller/streaming.rb
vendored
Normal file
147
vendor/rails/actionpack/lib/action_controller/streaming.rb
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
module ActionController #:nodoc:
|
||||
# Methods for sending files and streams to the browser instead of rendering.
|
||||
module Streaming
|
||||
DEFAULT_SEND_FILE_OPTIONS = {
|
||||
:type => 'application/octet-stream'.freeze,
|
||||
:disposition => 'attachment'.freeze,
|
||||
:stream => true,
|
||||
:buffer_size => 4096
|
||||
}.freeze
|
||||
|
||||
protected
|
||||
# Sends the file by streaming it 4096 bytes at a time. This way the
|
||||
# whole file doesn't need to be read into memory at once. This makes
|
||||
# it feasible to send even large files.
|
||||
#
|
||||
# Be careful to sanitize the path parameter if it coming from a web
|
||||
# page. send_file(params[:path]) allows a malicious user to
|
||||
# download any file on your server.
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
||||
# Defaults to File.basename(path).
|
||||
# * <tt>:type</tt> - specifies an HTTP content type.
|
||||
# Defaults to 'application/octet-stream'.
|
||||
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
||||
# Valid values are 'inline' and 'attachment' (default).
|
||||
# * <tt>:stream</tt> - whether to send the file to the user agent as it is read (true)
|
||||
# or to read the entire file before sending (false). Defaults to true.
|
||||
# * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
|
||||
# Defaults to 4096.
|
||||
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
|
||||
#
|
||||
# The default Content-Type and Content-Disposition headers are
|
||||
# set to download arbitrary binary files in as many browsers as
|
||||
# possible. IE versions 4, 5, 5.5, and 6 are all known to have
|
||||
# a variety of quirks (especially when downloading over SSL).
|
||||
#
|
||||
# Simple download:
|
||||
# send_file '/path/to.zip'
|
||||
#
|
||||
# Show a JPEG in the browser:
|
||||
# send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
|
||||
#
|
||||
# Show a 404 page in the browser:
|
||||
# send_file '/path/to/404.html, :type => 'text/html; charset=utf-8', :status => 404
|
||||
#
|
||||
# Read about the other Content-* HTTP headers if you'd like to
|
||||
# provide the user with more information (such as Content-Description).
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
|
||||
#
|
||||
# Also be aware that the document may be cached by proxies and browsers.
|
||||
# The Pragma and Cache-Control headers declare how the file may be cached
|
||||
# by intermediaries. They default to require clients to validate with
|
||||
# the server before releasing cached responses. See
|
||||
# http://www.mnot.net/cache_docs/ for an overview of web caching and
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
||||
# for the Cache-Control header spec.
|
||||
def send_file(path, options = {}) #:doc:
|
||||
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
|
||||
|
||||
options[:length] ||= File.size(path)
|
||||
options[:filename] ||= File.basename(path)
|
||||
send_file_headers! options
|
||||
|
||||
@performed_render = false
|
||||
|
||||
if options[:stream]
|
||||
render :status => options[:status], :text => Proc.new { |response, output|
|
||||
logger.info "Streaming file #{path}" unless logger.nil?
|
||||
len = options[:buffer_size] || 4096
|
||||
File.open(path, 'rb') do |file|
|
||||
if output.respond_to?(:syswrite)
|
||||
begin
|
||||
while true
|
||||
output.syswrite(file.sysread(len))
|
||||
end
|
||||
rescue EOFError
|
||||
end
|
||||
else
|
||||
while buf = file.read(len)
|
||||
output.write(buf)
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
else
|
||||
logger.info "Sending file #{path}" unless logger.nil?
|
||||
File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
|
||||
end
|
||||
end
|
||||
|
||||
# Send binary data to the user as a file download. May set content type, apparent file name,
|
||||
# and specify whether to show data inline or download as an attachment.
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:filename</tt> - Suggests a filename for the browser to use.
|
||||
# * <tt>:type</tt> - specifies an HTTP content type.
|
||||
# Defaults to 'application/octet-stream'.
|
||||
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
||||
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
|
||||
# Valid values are 'inline' and 'attachment' (default).
|
||||
#
|
||||
# Generic data download:
|
||||
# send_data buffer
|
||||
#
|
||||
# Download a dynamically-generated tarball:
|
||||
# send_data generate_tgz('dir'), :filename => 'dir.tgz'
|
||||
#
|
||||
# Display an image Active Record in the browser:
|
||||
# send_data image.data, :type => image.content_type, :disposition => 'inline'
|
||||
#
|
||||
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
||||
def send_data(data, options = {}) #:doc:
|
||||
logger.info "Sending data #{options[:filename]}" unless logger.nil?
|
||||
send_file_headers! options.merge(:length => data.size)
|
||||
@performed_render = false
|
||||
render :status => options[:status], :text => data
|
||||
end
|
||||
|
||||
private
|
||||
def send_file_headers!(options)
|
||||
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
|
||||
[:length, :type, :disposition].each do |arg|
|
||||
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
|
||||
end
|
||||
|
||||
disposition = options[:disposition].dup || 'attachment'
|
||||
|
||||
disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
|
||||
|
||||
@headers.update(
|
||||
'Content-Length' => options[:length],
|
||||
'Content-Type' => options[:type].strip, # fixes a problem with extra '\r' with some browsers
|
||||
'Content-Disposition' => disposition,
|
||||
'Content-Transfer-Encoding' => 'binary'
|
||||
)
|
||||
|
||||
# Fix a problem with IE 6.0 on opening downloaded files:
|
||||
# If Cache-Control: no-cache is set (which Rails does by default),
|
||||
# IE removes the file it just downloaded from its cache immediately
|
||||
# after it displays the "open/save" dialog, which means that if you
|
||||
# hit "open" the file isn't there anymore when the application that
|
||||
# is called for handling the download is run, so let's workaround that
|
||||
@headers['Cache-Control'] = 'private' if @headers['Cache-Control'] == 'no-cache'
|
||||
end
|
||||
end
|
||||
end
|
44
vendor/rails/actionpack/lib/action_controller/templates/rescues/_request_and_response.rhtml
vendored
Normal file
44
vendor/rails/actionpack/lib/action_controller/templates/rescues/_request_and_response.rhtml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
<% unless @exception.blamed_files.blank? %>
|
||||
<% if (hide = @exception.blamed_files.length > 8) %>
|
||||
<a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
|
||||
<% end %>
|
||||
<pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>
|
||||
<% end %>
|
||||
|
||||
<% if false %>
|
||||
<br /><br />
|
||||
<% begin %>
|
||||
<%= form_tag(@request.request_uri, "method" => @request.method) %>
|
||||
<input type="hidden" name="BP-RETRY" value="1" />
|
||||
|
||||
<% for key, values in @params %>
|
||||
<% next if key == "BP-RETRY" %>
|
||||
<% for value in Array(values) %>
|
||||
<input type="hidden" name="<%= key %>" value="<%= value %>" />
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<input type="submit" value="Retry with Breakpoint" />
|
||||
</form>
|
||||
<% rescue Exception => e %>
|
||||
<%=h "Couldn't render breakpoint link due to #{e.class} #{e.message}" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%
|
||||
request_parameters_without_action = @request.parameters.clone
|
||||
request_parameters_without_action.delete("action")
|
||||
request_parameters_without_action.delete("controller")
|
||||
|
||||
request_dump = request_parameters_without_action.inspect.gsub(/,/, ",\n")
|
||||
%>
|
||||
|
||||
<h2 style="margin-top: 30px">Request</h2>
|
||||
<p><b>Parameters</b>: <%=h request_dump == "{}" ? "None" : request_dump %></p>
|
||||
|
||||
<p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
|
||||
<div id="session_dump" style="display:none"><%= debug(@request.session.instance_variable_get("@data")) %></div>
|
||||
|
||||
|
||||
<h2 style="margin-top: 30px">Response</h2>
|
||||
<b>Headers</b>: <%=h @response.headers.inspect.gsub(/,/, ",\n") %><br/>
|
26
vendor/rails/actionpack/lib/action_controller/templates/rescues/_trace.rhtml
vendored
Normal file
26
vendor/rails/actionpack/lib/action_controller/templates/rescues/_trace.rhtml
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
<%
|
||||
traces = [
|
||||
["Application Trace", @exception.application_backtrace],
|
||||
["Framework Trace", @exception.framework_backtrace],
|
||||
["Full Trace", @exception.clean_backtrace]
|
||||
]
|
||||
names = traces.collect {|name, trace| name}
|
||||
%>
|
||||
|
||||
<p><code>RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %></code></p>
|
||||
|
||||
<div id="traces">
|
||||
<% names.each do |name| -%>
|
||||
<%
|
||||
show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
|
||||
hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
|
||||
%>
|
||||
<a href="#" onclick="<%= hide %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
|
||||
<% end -%>
|
||||
|
||||
<% traces.each do |name, trace| -%>
|
||||
<div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
|
||||
<pre><code><%= trace.join "\n" %></code></pre>
|
||||
</div>
|
||||
<% end -%>
|
||||
</div>
|
11
vendor/rails/actionpack/lib/action_controller/templates/rescues/diagnostics.rhtml
vendored
Normal file
11
vendor/rails/actionpack/lib/action_controller/templates/rescues/diagnostics.rhtml
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<h1>
|
||||
<%=h @exception.class.to_s %>
|
||||
<% if @request.parameters['controller'] %>
|
||||
in <%=h @request.parameters['controller'].humanize %>Controller<% if @request.parameters['action'] %>#<%=h @request.parameters['action'] %><% end %>
|
||||
<% end %>
|
||||
</h1>
|
||||
<pre><%=h @exception.clean_message %></pre>
|
||||
|
||||
<%= render_file(@rescues_path + "/_trace.rhtml", false) %>
|
||||
|
||||
<%= render_file(@rescues_path + "/_request_and_response.rhtml", false) %>
|
29
vendor/rails/actionpack/lib/action_controller/templates/rescues/layout.rhtml
vendored
Normal file
29
vendor/rails/actionpack/lib/action_controller/templates/rescues/layout.rhtml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Action Controller: Exception caught</title>
|
||||
<style>
|
||||
body { background-color: #fff; color: #333; }
|
||||
|
||||
body, p, ol, ul, td {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
a { color: #000; }
|
||||
a:visited { color: #666; }
|
||||
a:hover { color: #fff; background-color:#000; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<%= @contents %>
|
||||
|
||||
</body>
|
||||
</html>
|
2
vendor/rails/actionpack/lib/action_controller/templates/rescues/missing_template.rhtml
vendored
Normal file
2
vendor/rails/actionpack/lib/action_controller/templates/rescues/missing_template.rhtml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
<h1>Template is missing</h1>
|
||||
<p><%=h @exception.message %></p>
|
10
vendor/rails/actionpack/lib/action_controller/templates/rescues/routing_error.rhtml
vendored
Normal file
10
vendor/rails/actionpack/lib/action_controller/templates/rescues/routing_error.rhtml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<h1>Routing Error</h1>
|
||||
<p><pre><%=h @exception.message %></pre></p>
|
||||
<% unless @exception.failures.empty? %><p>
|
||||
<h2>Failure reasons:</h2>
|
||||
<ol>
|
||||
<% @exception.failures.each do |route, reason| %>
|
||||
<li><code><%=h route.inspect.gsub('\\', '') %></code> failed because <%=h reason.downcase %></li>
|
||||
<% end %>
|
||||
</ol>
|
||||
</p><% end %>
|
21
vendor/rails/actionpack/lib/action_controller/templates/rescues/template_error.rhtml
vendored
Normal file
21
vendor/rails/actionpack/lib/action_controller/templates/rescues/template_error.rhtml
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
<h1>
|
||||
<%=h @exception.original_exception.class.to_s %> in
|
||||
<%=h @request.parameters["controller"].capitalize if @request.parameters["controller"]%>#<%=h @request.parameters["action"] %>
|
||||
</h1>
|
||||
|
||||
<p>
|
||||
Showing <i><%=h @exception.file_name %></i> where line <b>#<%=h @exception.line_number %></b> raised:
|
||||
<pre><code><%=h @exception.message %></code></pre>
|
||||
</p>
|
||||
|
||||
<p>Extracted source (around line <b>#<%=h @exception.line_number %></b>):
|
||||
<pre><code><%=h @exception.source_extract %></code></pre></p>
|
||||
|
||||
<p><%=h @exception.sub_template_message %></p>
|
||||
|
||||
<% @real_exception = @exception
|
||||
@exception = @exception.original_exception || @exception %>
|
||||
<%= render_file(@rescues_path + "/_trace.rhtml", false) %>
|
||||
<% @exception = @real_exception %>
|
||||
|
||||
<%= render_file(@rescues_path + "/_request_and_response.rhtml", false) %>
|
2
vendor/rails/actionpack/lib/action_controller/templates/rescues/unknown_action.rhtml
vendored
Normal file
2
vendor/rails/actionpack/lib/action_controller/templates/rescues/unknown_action.rhtml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
<h1>Unknown action</h1>
|
||||
<p><%=h @exception.message %></p>
|
7
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/edit.rhtml
vendored
Normal file
7
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/edit.rhtml
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<h1>Editing <%= @scaffold_singular_name %></h1>
|
||||
|
||||
<%= error_messages_for(@scaffold_singular_name) %>
|
||||
<%= form(@scaffold_singular_name, :action => "update#{@scaffold_suffix}") %>
|
||||
|
||||
<%= link_to "Show", :action => "show#{@scaffold_suffix}", :id => instance_variable_get("@#{@scaffold_singular_name}") %> |
|
||||
<%= link_to "Back", :action => "list#{@scaffold_suffix}" %>
|
69
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/layout.rhtml
vendored
Normal file
69
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/layout.rhtml
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Scaffolding</title>
|
||||
<style>
|
||||
body { background-color: #fff; color: #333; }
|
||||
|
||||
body, p, ol, ul, td {
|
||||
font-family: verdana, arial, helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #eee;
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
a { color: #000; }
|
||||
a:visited { color: #666; }
|
||||
a:hover { color: #fff; background-color:#000; }
|
||||
|
||||
.fieldWithErrors {
|
||||
padding: 2px;
|
||||
background-color: red;
|
||||
display: table;
|
||||
}
|
||||
|
||||
#errorExplanation {
|
||||
width: 400px;
|
||||
border: 2px solid red;
|
||||
padding: 7px;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 20px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#errorExplanation h2 {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
padding: 5px 5px 5px 15px;
|
||||
font-size: 12px;
|
||||
margin: -7px;
|
||||
background-color: #c00;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#errorExplanation p {
|
||||
color: #333;
|
||||
margin-bottom: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#errorExplanation ul li {
|
||||
font-size: 12px;
|
||||
list-style: square;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p style="color: green"><%= flash[:notice] %></p>
|
||||
|
||||
<%= yield %>
|
||||
|
||||
</body>
|
||||
</html>
|
27
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/list.rhtml
vendored
Normal file
27
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/list.rhtml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<h1>Listing <%= @scaffold_plural_name %></h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<% for column in @scaffold_class.content_columns %>
|
||||
<th><%= column.human_name %></th>
|
||||
<% end %>
|
||||
</tr>
|
||||
|
||||
<% for entry in instance_variable_get("@#{@scaffold_plural_name}") %>
|
||||
<tr>
|
||||
<% for column in @scaffold_class.content_columns %>
|
||||
<td><%= entry.send(column.name) %></td>
|
||||
<% end %>
|
||||
<td><%= link_to "Show", :action => "show#{@scaffold_suffix}", :id => entry %></td>
|
||||
<td><%= link_to "Edit", :action => "edit#{@scaffold_suffix}", :id => entry %></td>
|
||||
<td><%= link_to "Destroy", {:action => "destroy#{@scaffold_suffix}", :id => entry}, { :confirm => "Are you sure?", :post => true} %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
<%= link_to "Previous page", { :page => instance_variable_get("@#{@scaffold_singular_name}_pages").current.previous } if instance_variable_get("@#{@scaffold_singular_name}_pages").current.previous %>
|
||||
<%= link_to "Next page", { :page => instance_variable_get("@#{@scaffold_singular_name}_pages").current.next } if instance_variable_get("@#{@scaffold_singular_name}_pages").current.next %>
|
||||
|
||||
<br />
|
||||
|
||||
<%= link_to "New #{@scaffold_singular_name}", :action => "new#{@scaffold_suffix}" %>
|
6
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/new.rhtml
vendored
Normal file
6
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/new.rhtml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
<h1>New <%= @scaffold_singular_name %></h1>
|
||||
|
||||
<%= error_messages_for(@scaffold_singular_name) %>
|
||||
<%= form(@scaffold_singular_name, :action => "create#{@scaffold_suffix}") %>
|
||||
|
||||
<%= link_to "Back", :action => "list#{@scaffold_suffix}" %>
|
9
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/show.rhtml
vendored
Normal file
9
vendor/rails/actionpack/lib/action_controller/templates/scaffolds/show.rhtml
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
<% for column in @scaffold_class.content_columns %>
|
||||
<p>
|
||||
<b><%= column.human_name %>:</b>
|
||||
<%= instance_variable_get("@#{@scaffold_singular_name}").send(column.name) %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
<%= link_to "Edit", :action => "edit#{@scaffold_suffix}", :id => instance_variable_get("@#{@scaffold_singular_name}") %> |
|
||||
<%= link_to "Back", :action => "list#{@scaffold_suffix}" %>
|
482
vendor/rails/actionpack/lib/action_controller/test_process.rb
vendored
Normal file
482
vendor/rails/actionpack/lib/action_controller/test_process.rb
vendored
Normal file
|
@ -0,0 +1,482 @@
|
|||
require File.dirname(__FILE__) + '/assertions'
|
||||
require File.dirname(__FILE__) + '/deprecated_assertions'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
class Base
|
||||
# Process a test request called with a +TestRequest+ object.
|
||||
def self.process_test(request)
|
||||
new.process_test(request)
|
||||
end
|
||||
|
||||
def process_test(request) #:nodoc:
|
||||
process(request, TestResponse.new)
|
||||
end
|
||||
|
||||
def process_with_test(*args)
|
||||
returning process_without_test(*args) do
|
||||
add_variables_to_assigns
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :process_without_test, :process
|
||||
alias_method :process, :process_with_test
|
||||
end
|
||||
|
||||
class TestRequest < AbstractRequest #:nodoc:
|
||||
attr_accessor :cookies, :session_options
|
||||
attr_accessor :query_parameters, :request_parameters, :path, :session, :env
|
||||
attr_accessor :host
|
||||
|
||||
def initialize(query_parameters = nil, request_parameters = nil, session = nil)
|
||||
@query_parameters = query_parameters || {}
|
||||
@request_parameters = request_parameters || {}
|
||||
@session = session || TestSession.new
|
||||
|
||||
initialize_containers
|
||||
initialize_default_values
|
||||
|
||||
super()
|
||||
end
|
||||
|
||||
def reset_session
|
||||
@session = {}
|
||||
end
|
||||
|
||||
def raw_post
|
||||
if raw_post = env['RAW_POST_DATA']
|
||||
raw_post
|
||||
else
|
||||
params = self.request_parameters.dup
|
||||
%w(controller action only_path).each do |k|
|
||||
params.delete(k)
|
||||
params.delete(k.to_sym)
|
||||
end
|
||||
|
||||
params.map { |k,v| [ CGI.escape(k.to_s), CGI.escape(v.to_s) ].join('=') }.sort.join('&')
|
||||
end
|
||||
end
|
||||
|
||||
def port=(number)
|
||||
@env["SERVER_PORT"] = number.to_i
|
||||
@port_as_int = nil
|
||||
end
|
||||
|
||||
def action=(action_name)
|
||||
@query_parameters.update({ "action" => action_name })
|
||||
@parameters = nil
|
||||
end
|
||||
|
||||
# Used to check AbstractRequest's request_uri functionality.
|
||||
# Disables the use of @path and @request_uri so superclass can handle those.
|
||||
def set_REQUEST_URI(value)
|
||||
@env["REQUEST_URI"] = value
|
||||
@request_uri = nil
|
||||
@path = nil
|
||||
end
|
||||
|
||||
def request_uri=(uri)
|
||||
@request_uri = uri
|
||||
@path = uri.split("?").first
|
||||
end
|
||||
|
||||
def remote_addr=(addr)
|
||||
@env['REMOTE_ADDR'] = addr
|
||||
end
|
||||
|
||||
def remote_addr
|
||||
@env['REMOTE_ADDR']
|
||||
end
|
||||
|
||||
def request_uri
|
||||
@request_uri || super()
|
||||
end
|
||||
|
||||
def path
|
||||
@path || super()
|
||||
end
|
||||
|
||||
def assign_parameters(controller_path, action, parameters)
|
||||
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
|
||||
extra_keys = ActionController::Routing::Routes.extra_keys(parameters)
|
||||
non_path_parameters = get? ? query_parameters : request_parameters
|
||||
parameters.each do |key, value|
|
||||
if value.is_a? Fixnum
|
||||
value = value.to_s
|
||||
elsif value.is_a? Array
|
||||
value = ActionController::Routing::PathComponent::Result.new(value)
|
||||
end
|
||||
|
||||
if extra_keys.include?(key.to_sym)
|
||||
non_path_parameters[key] = value
|
||||
else
|
||||
path_parameters[key.to_s] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def recycle!
|
||||
self.request_parameters = {}
|
||||
self.query_parameters = {}
|
||||
self.path_parameters = {}
|
||||
@request_method, @accepts, @content_type = nil, nil, nil
|
||||
end
|
||||
|
||||
private
|
||||
def initialize_containers
|
||||
@env, @cookies = {}, {}
|
||||
end
|
||||
|
||||
def initialize_default_values
|
||||
@host = "test.host"
|
||||
@request_uri = "/"
|
||||
self.remote_addr = "0.0.0.0"
|
||||
@env["SERVER_PORT"] = 80
|
||||
@env['REQUEST_METHOD'] = "GET"
|
||||
end
|
||||
end
|
||||
|
||||
# A refactoring of TestResponse to allow the same behavior to be applied
|
||||
# to the "real" CgiResponse class in integration tests.
|
||||
module TestResponseBehavior #:nodoc:
|
||||
# the response code of the request
|
||||
def response_code
|
||||
headers['Status'][0,3].to_i rescue 0
|
||||
end
|
||||
|
||||
# returns a String to ensure compatibility with Net::HTTPResponse
|
||||
def code
|
||||
headers['Status'].to_s.split(' ')[0]
|
||||
end
|
||||
|
||||
def message
|
||||
headers['Status'].to_s.split(' ',2)[1]
|
||||
end
|
||||
|
||||
# was the response successful?
|
||||
def success?
|
||||
response_code == 200
|
||||
end
|
||||
|
||||
# was the URL not found?
|
||||
def missing?
|
||||
response_code == 404
|
||||
end
|
||||
|
||||
# were we redirected?
|
||||
def redirect?
|
||||
(300..399).include?(response_code)
|
||||
end
|
||||
|
||||
# was there a server-side error?
|
||||
def error?
|
||||
(500..599).include?(response_code)
|
||||
end
|
||||
|
||||
alias_method :server_error?, :error?
|
||||
|
||||
# returns the redirection location or nil
|
||||
def redirect_url
|
||||
redirect? ? headers['location'] : nil
|
||||
end
|
||||
|
||||
# does the redirect location match this regexp pattern?
|
||||
def redirect_url_match?( pattern )
|
||||
return false if redirect_url.nil?
|
||||
p = Regexp.new(pattern) if pattern.class == String
|
||||
p = pattern if pattern.class == Regexp
|
||||
return false if p.nil?
|
||||
p.match(redirect_url) != nil
|
||||
end
|
||||
|
||||
# returns the template path of the file which was used to
|
||||
# render this response (or nil)
|
||||
def rendered_file(with_controller=false)
|
||||
unless template.first_render.nil?
|
||||
unless with_controller
|
||||
template.first_render
|
||||
else
|
||||
template.first_render.split('/').last || template.first_render
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# was this template rendered by a file?
|
||||
def rendered_with_file?
|
||||
!rendered_file.nil?
|
||||
end
|
||||
|
||||
# a shortcut to the flash (or an empty hash if no flash.. hey! that rhymes!)
|
||||
def flash
|
||||
session['flash'] || {}
|
||||
end
|
||||
|
||||
# do we have a flash?
|
||||
def has_flash?
|
||||
!session['flash'].empty?
|
||||
end
|
||||
|
||||
# do we have a flash that has contents?
|
||||
def has_flash_with_contents?
|
||||
!flash.empty?
|
||||
end
|
||||
|
||||
# does the specified flash object exist?
|
||||
def has_flash_object?(name=nil)
|
||||
!flash[name].nil?
|
||||
end
|
||||
|
||||
# does the specified object exist in the session?
|
||||
def has_session_object?(name=nil)
|
||||
!session[name].nil?
|
||||
end
|
||||
|
||||
# a shortcut to the template.assigns
|
||||
def template_objects
|
||||
template.assigns || {}
|
||||
end
|
||||
|
||||
# does the specified template object exist?
|
||||
def has_template_object?(name=nil)
|
||||
!template_objects[name].nil?
|
||||
end
|
||||
|
||||
# Returns the response cookies, converted to a Hash of (name => CGI::Cookie) pairs
|
||||
# Example:
|
||||
#
|
||||
# assert_equal ['AuthorOfNewPage'], r.cookies['author'].value
|
||||
def cookies
|
||||
headers['cookie'].inject({}) { |hash, cookie| hash[cookie.name] = cookie; hash }
|
||||
end
|
||||
|
||||
# Returns binary content (downloadable file), converted to a String
|
||||
def binary_content
|
||||
raise "Response body is not a Proc: #{body.inspect}" unless body.kind_of?(Proc)
|
||||
require 'stringio'
|
||||
|
||||
sio = StringIO.new
|
||||
|
||||
begin
|
||||
$stdout = sio
|
||||
body.call
|
||||
ensure
|
||||
$stdout = STDOUT
|
||||
end
|
||||
|
||||
sio.rewind
|
||||
sio.read
|
||||
end
|
||||
end
|
||||
|
||||
class TestResponse < AbstractResponse #:nodoc:
|
||||
include TestResponseBehavior
|
||||
end
|
||||
|
||||
class TestSession #:nodoc:
|
||||
def initialize(attributes = {})
|
||||
@attributes = attributes
|
||||
end
|
||||
|
||||
def [](key)
|
||||
@attributes[key]
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
@attributes[key] = value
|
||||
end
|
||||
|
||||
def session_id
|
||||
""
|
||||
end
|
||||
|
||||
def update() end
|
||||
def close() end
|
||||
def delete() @attributes = {} end
|
||||
end
|
||||
|
||||
# Essentially generates a modified Tempfile object similar to the object
|
||||
# you'd get from the standard library CGI module in a multipart
|
||||
# request. This means you can use an ActionController::TestUploadedFile
|
||||
# object in the params of a test request in order to simulate
|
||||
# a file upload.
|
||||
#
|
||||
# Usage example, within a functional test:
|
||||
# post :change_avatar, :avatar => ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + '/files/spongebob.png', 'image/png')
|
||||
class TestUploadedFile
|
||||
# The filename, *not* including the path, of the "uploaded" file
|
||||
attr_reader :original_filename
|
||||
|
||||
# The content type of the "uploaded" file
|
||||
attr_reader :content_type
|
||||
|
||||
def initialize(path, content_type = 'text/plain')
|
||||
raise "file does not exist" unless File.exist?(path)
|
||||
@content_type = content_type
|
||||
@original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 }
|
||||
@tempfile = Tempfile.new(@original_filename)
|
||||
FileUtils.copy_file(path, @tempfile.path)
|
||||
end
|
||||
|
||||
def path #:nodoc:
|
||||
@tempfile.path
|
||||
end
|
||||
|
||||
alias local_path path
|
||||
|
||||
def method_missing(method_name, *args, &block) #:nodoc:
|
||||
@tempfile.send(method_name, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
module TestProcess
|
||||
def self.included(base)
|
||||
# execute the request simulating a specific http method and set/volley the response
|
||||
%w( get post put delete head ).each do |method|
|
||||
base.class_eval <<-EOV, __FILE__, __LINE__
|
||||
def #{method}(action, parameters = nil, session = nil, flash = nil)
|
||||
@request.env['REQUEST_METHOD'] = "#{method.upcase}" if @request
|
||||
process(action, parameters, session, flash)
|
||||
end
|
||||
EOV
|
||||
end
|
||||
end
|
||||
|
||||
# execute the request and set/volley the response
|
||||
def process(action, parameters = nil, session = nil, flash = nil)
|
||||
# Sanity check for required instance variables so we can give an
|
||||
# understandable error message.
|
||||
%w(controller request response).each do |iv_name|
|
||||
raise "@#{iv_name} is nil: make sure you set it in your test's setup method." if instance_variable_get("@#{iv_name}").nil?
|
||||
end
|
||||
|
||||
@request.recycle!
|
||||
|
||||
@html_document = nil
|
||||
@request.env['REQUEST_METHOD'] ||= "GET"
|
||||
@request.action = action.to_s
|
||||
|
||||
parameters ||= {}
|
||||
@request.assign_parameters(@controller.class.controller_path, action.to_s, parameters)
|
||||
|
||||
@request.session = ActionController::TestSession.new(session) unless session.nil?
|
||||
@request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
|
||||
build_request_uri(action, parameters)
|
||||
@controller.process(@request, @response)
|
||||
end
|
||||
|
||||
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
|
||||
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
||||
@request.env['HTTP_ACCEPT'] = 'text/javascript, text/html, application/xml, text/xml, */*'
|
||||
returning self.send(request_method, action, parameters, session, flash) do
|
||||
@request.env.delete 'HTTP_X_REQUESTED_WITH'
|
||||
@request.env.delete 'HTTP_ACCEPT'
|
||||
end
|
||||
end
|
||||
alias xhr :xml_http_request
|
||||
|
||||
def follow_redirect
|
||||
if @response.redirected_to[:controller]
|
||||
raise "Can't follow redirects outside of current controller (#{@response.redirected_to[:controller]})"
|
||||
end
|
||||
|
||||
get(@response.redirected_to.delete(:action), @response.redirected_to.stringify_keys)
|
||||
end
|
||||
|
||||
def assigns(key = nil)
|
||||
if key.nil?
|
||||
@response.template.assigns
|
||||
else
|
||||
@response.template.assigns[key.to_s]
|
||||
end
|
||||
end
|
||||
|
||||
def session
|
||||
@response.session
|
||||
end
|
||||
|
||||
def flash
|
||||
@response.flash
|
||||
end
|
||||
|
||||
def cookies
|
||||
@response.cookies
|
||||
end
|
||||
|
||||
def redirect_to_url
|
||||
@response.redirect_url
|
||||
end
|
||||
|
||||
def build_request_uri(action, parameters)
|
||||
unless @request.env['REQUEST_URI']
|
||||
options = @controller.send(:rewrite_options, parameters)
|
||||
options.update(:only_path => true, :action => action)
|
||||
|
||||
url = ActionController::UrlRewriter.new(@request, parameters)
|
||||
@request.set_REQUEST_URI(url.rewrite(options))
|
||||
end
|
||||
end
|
||||
|
||||
def html_document
|
||||
@html_document ||= HTML::Document.new(@response.body)
|
||||
end
|
||||
|
||||
def find_tag(conditions)
|
||||
html_document.find(conditions)
|
||||
end
|
||||
|
||||
def find_all_tag(conditions)
|
||||
html_document.find_all(conditions)
|
||||
end
|
||||
|
||||
def method_missing(selector, *args)
|
||||
return @controller.send(selector, *args) if ActionController::Routing::NamedRoutes::Helpers.include?(selector)
|
||||
return super
|
||||
end
|
||||
|
||||
# Shortcut for ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type). Example:
|
||||
# post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
|
||||
def fixture_file_upload(path, mime_type = nil)
|
||||
ActionController::TestUploadedFile.new(
|
||||
Test::Unit::TestCase.respond_to?(:fixture_path) ? Test::Unit::TestCase.fixture_path + path : path,
|
||||
mime_type
|
||||
)
|
||||
end
|
||||
|
||||
# A helper to make it easier to test different route configurations.
|
||||
# This method temporarily replaces ActionController::Routing::Routes
|
||||
# with a new RouteSet instance.
|
||||
#
|
||||
# The new instance is yielded to the passed block. Typically the block
|
||||
# will create some routes using map.draw { map.connect ... }:
|
||||
#
|
||||
# with_routing do |set|
|
||||
# set.draw { set.connect ':controller/:id/:action' }
|
||||
# assert_equal(
|
||||
# ['/content/10/show', {}],
|
||||
# set.generate(:controller => 'content', :id => 10, :action => 'show')
|
||||
# )
|
||||
# end
|
||||
#
|
||||
def with_routing
|
||||
real_routes = ActionController::Routing::Routes
|
||||
ActionController::Routing.send :remove_const, :Routes
|
||||
|
||||
temporary_routes = ActionController::Routing::RouteSet.new
|
||||
ActionController::Routing.send :const_set, :Routes, temporary_routes
|
||||
|
||||
yield temporary_routes
|
||||
ensure
|
||||
if ActionController::Routing.const_defined? :Routes
|
||||
ActionController::Routing.send(:remove_const, :Routes)
|
||||
end
|
||||
ActionController::Routing.const_set(:Routes, real_routes) if real_routes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Test
|
||||
module Unit
|
||||
class TestCase #:nodoc:
|
||||
include ActionController::TestProcess
|
||||
end
|
||||
end
|
||||
end
|
74
vendor/rails/actionpack/lib/action_controller/url_rewriter.rb
vendored
Normal file
74
vendor/rails/actionpack/lib/action_controller/url_rewriter.rb
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
module ActionController
|
||||
# Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
|
||||
|
||||
class UrlRewriter #:nodoc:
|
||||
RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :trailing_slash, :skip_relative_url_root]
|
||||
def initialize(request, parameters)
|
||||
@request, @parameters = request, parameters
|
||||
end
|
||||
|
||||
def rewrite(options = {})
|
||||
rewrite_url(rewrite_path(options), options)
|
||||
end
|
||||
|
||||
def to_str
|
||||
"#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
|
||||
end
|
||||
|
||||
alias_method :to_s, :to_str
|
||||
|
||||
private
|
||||
def rewrite_url(path, options)
|
||||
rewritten_url = ""
|
||||
unless options[:only_path]
|
||||
rewritten_url << (options[:protocol] || @request.protocol)
|
||||
rewritten_url << (options[:host] || @request.host_with_port)
|
||||
end
|
||||
|
||||
rewritten_url << @request.relative_url_root.to_s unless options[:skip_relative_url_root]
|
||||
rewritten_url << path
|
||||
rewritten_url << '/' if options[:trailing_slash]
|
||||
rewritten_url << "##{options[:anchor]}" if options[:anchor]
|
||||
|
||||
rewritten_url
|
||||
end
|
||||
|
||||
def rewrite_path(options)
|
||||
options = options.symbolize_keys
|
||||
options.update(options[:params].symbolize_keys) if options[:params]
|
||||
if (overwrite = options.delete(:overwrite_params))
|
||||
options.update(@parameters.symbolize_keys)
|
||||
options.update(overwrite)
|
||||
end
|
||||
RESERVED_OPTIONS.each {|k| options.delete k}
|
||||
path, extra_keys = Routing::Routes.generate(options.dup, @request) # Warning: Routes will mutate and violate the options hash
|
||||
|
||||
path << build_query_string(options, extra_keys) unless extra_keys.empty?
|
||||
|
||||
path
|
||||
end
|
||||
|
||||
# Returns a query string with escaped keys and values from the passed hash. If the passed hash contains an "id" it'll
|
||||
# be added as a path element instead of a regular parameter pair.
|
||||
def build_query_string(hash, only_keys = nil)
|
||||
elements = []
|
||||
query_string = ""
|
||||
|
||||
only_keys ||= hash.keys
|
||||
|
||||
only_keys.each do |key|
|
||||
value = hash[key]
|
||||
key = CGI.escape key.to_s
|
||||
if value.class == Array
|
||||
key << '[]'
|
||||
else
|
||||
value = [ value ]
|
||||
end
|
||||
value.each { |val| elements << "#{key}=#{Routing.extract_parameter_value(val)}" }
|
||||
end
|
||||
|
||||
query_string << ("?" + elements.join("&")) unless elements.empty?
|
||||
query_string
|
||||
end
|
||||
end
|
||||
end
|
64
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb
vendored
Normal file
64
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
require File.dirname(__FILE__) + '/tokenizer'
|
||||
require File.dirname(__FILE__) + '/node'
|
||||
|
||||
module HTML #:nodoc:
|
||||
|
||||
# A top-level HTMl document. You give it a body of text, and it will parse that
|
||||
# text into a tree of nodes.
|
||||
class Document #:nodoc:
|
||||
|
||||
# The root of the parsed document.
|
||||
attr_reader :root
|
||||
|
||||
# Create a new Document from the given text.
|
||||
def initialize(text, strict=false, xml=false)
|
||||
tokenizer = Tokenizer.new(text)
|
||||
@root = Node.new(nil)
|
||||
node_stack = [ @root ]
|
||||
while token = tokenizer.next
|
||||
node = Node.parse(node_stack.last, tokenizer.line, tokenizer.position, token)
|
||||
|
||||
node_stack.last.children << node unless node.tag? && node.closing == :close
|
||||
if node.tag?
|
||||
if node_stack.length > 1 && node.closing == :close
|
||||
if node_stack.last.name == node.name
|
||||
node_stack.pop
|
||||
else
|
||||
open_start = node_stack.last.position - 20
|
||||
open_start = 0 if open_start < 0
|
||||
close_start = node.position - 20
|
||||
close_start = 0 if close_start < 0
|
||||
msg = <<EOF.strip
|
||||
ignoring attempt to close #{node_stack.last.name} with #{node.name}
|
||||
opened at byte #{node_stack.last.position}, line #{node_stack.last.line}
|
||||
closed at byte #{node.position}, line #{node.line}
|
||||
attributes at open: #{node_stack.last.attributes.inspect}
|
||||
text around open: #{text[open_start,40].inspect}
|
||||
text around close: #{text[close_start,40].inspect}
|
||||
EOF
|
||||
strict ? raise(msg) : warn(msg)
|
||||
end
|
||||
elsif !node.childless?(xml) && node.closing != :close
|
||||
node_stack.push node
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Search the tree for (and return) the first node that matches the given
|
||||
# conditions. The conditions are interpreted differently for different node
|
||||
# types, see HTML::Text#find and HTML::Tag#find.
|
||||
def find(conditions)
|
||||
@root.find(conditions)
|
||||
end
|
||||
|
||||
# Search the tree for (and return) all nodes that match the given
|
||||
# conditions. The conditions are interpreted differently for different node
|
||||
# types, see HTML::Text#find and HTML::Tag#find.
|
||||
def find_all(conditions)
|
||||
@root.find_all(conditions)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
533
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
vendored
Normal file
533
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
vendored
Normal file
|
@ -0,0 +1,533 @@
|
|||
require 'strscan'
|
||||
|
||||
module HTML #:nodoc:
|
||||
|
||||
class Conditions < Hash #:nodoc:
|
||||
def initialize(hash)
|
||||
super()
|
||||
hash = { :content => hash } unless Hash === hash
|
||||
hash = keys_to_symbols(hash)
|
||||
hash.each do |k,v|
|
||||
case k
|
||||
when :tag, :content then
|
||||
# keys are valid, and require no further processing
|
||||
when :attributes then
|
||||
hash[k] = keys_to_strings(v)
|
||||
when :parent, :child, :ancestor, :descendant, :sibling, :before,
|
||||
:after
|
||||
hash[k] = Conditions.new(v)
|
||||
when :children
|
||||
hash[k] = v = keys_to_symbols(v)
|
||||
v.each do |k,v2|
|
||||
case k
|
||||
when :count, :greater_than, :less_than
|
||||
# keys are valid, and require no further processing
|
||||
when :only
|
||||
v[k] = Conditions.new(v2)
|
||||
else
|
||||
raise "illegal key #{k.inspect} => #{v2.inspect}"
|
||||
end
|
||||
end
|
||||
else
|
||||
raise "illegal key #{k.inspect} => #{v.inspect}"
|
||||
end
|
||||
end
|
||||
update hash
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def keys_to_strings(hash)
|
||||
hash.keys.inject({}) do |h,k|
|
||||
h[k.to_s] = hash[k]
|
||||
h
|
||||
end
|
||||
end
|
||||
|
||||
def keys_to_symbols(hash)
|
||||
hash.keys.inject({}) do |h,k|
|
||||
raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
|
||||
h[k.to_sym] = hash[k]
|
||||
h
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The base class of all nodes, textual and otherwise, in an HTML document.
|
||||
class Node #:nodoc:
|
||||
# The array of children of this node. Not all nodes have children.
|
||||
attr_reader :children
|
||||
|
||||
# The parent node of this node. All nodes have a parent, except for the
|
||||
# root node.
|
||||
attr_reader :parent
|
||||
|
||||
# The line number of the input where this node was begun
|
||||
attr_reader :line
|
||||
|
||||
# The byte position in the input where this node was begun
|
||||
attr_reader :position
|
||||
|
||||
# Create a new node as a child of the given parent.
|
||||
def initialize(parent, line=0, pos=0)
|
||||
@parent = parent
|
||||
@children = []
|
||||
@line, @position = line, pos
|
||||
end
|
||||
|
||||
# Return a textual representation of the node.
|
||||
def to_s
|
||||
s = ""
|
||||
@children.each { |child| s << child.to_s }
|
||||
s
|
||||
end
|
||||
|
||||
# Return false (subclasses must override this to provide specific matching
|
||||
# behavior.) +conditions+ may be of any type.
|
||||
def match(conditions)
|
||||
false
|
||||
end
|
||||
|
||||
# Search the children of this node for the first node for which #find
|
||||
# returns non +nil+. Returns the result of the #find call that succeeded.
|
||||
def find(conditions)
|
||||
conditions = validate_conditions(conditions)
|
||||
|
||||
@children.each do |child|
|
||||
node = child.find(conditions)
|
||||
return node if node
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Search for all nodes that match the given conditions, and return them
|
||||
# as an array.
|
||||
def find_all(conditions)
|
||||
conditions = validate_conditions(conditions)
|
||||
|
||||
matches = []
|
||||
matches << self if match(conditions)
|
||||
@children.each do |child|
|
||||
matches.concat child.find_all(conditions)
|
||||
end
|
||||
matches
|
||||
end
|
||||
|
||||
# Returns +false+. Subclasses may override this if they define a kind of
|
||||
# tag.
|
||||
def tag?
|
||||
false
|
||||
end
|
||||
|
||||
def validate_conditions(conditions)
|
||||
Conditions === conditions ? conditions : Conditions.new(conditions)
|
||||
end
|
||||
|
||||
def ==(node)
|
||||
return false unless self.class == node.class && children.size == node.children.size
|
||||
|
||||
equivalent = true
|
||||
|
||||
children.size.times do |i|
|
||||
equivalent &&= children[i] == node.children[i]
|
||||
end
|
||||
|
||||
equivalent
|
||||
end
|
||||
|
||||
class <<self
|
||||
def parse(parent, line, pos, content, strict=true)
|
||||
if content !~ /^<\S/
|
||||
Text.new(parent, line, pos, content)
|
||||
else
|
||||
scanner = StringScanner.new(content)
|
||||
|
||||
unless scanner.skip(/</)
|
||||
if strict
|
||||
raise "expected <"
|
||||
else
|
||||
return Text.new(parent, line, pos, content)
|
||||
end
|
||||
end
|
||||
|
||||
if scanner.skip(/!\[CDATA\[/)
|
||||
scanner.scan_until(/\]\]>/)
|
||||
return CDATA.new(parent, line, pos, scanner.pre_match)
|
||||
end
|
||||
|
||||
closing = ( scanner.scan(/\//) ? :close : nil )
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:]+/)
|
||||
name.downcase!
|
||||
|
||||
unless closing
|
||||
scanner.skip(/\s*/)
|
||||
attributes = {}
|
||||
while attr = scanner.scan(/[-\w:]+/)
|
||||
value = true
|
||||
if scanner.scan(/\s*=\s*/)
|
||||
if delim = scanner.scan(/['"]/)
|
||||
value = ""
|
||||
while text = scanner.scan(/[^#{delim}\\]+|./)
|
||||
case text
|
||||
when "\\" then
|
||||
value << text
|
||||
value << scanner.getch
|
||||
when delim
|
||||
break
|
||||
else value << text
|
||||
end
|
||||
end
|
||||
else
|
||||
value = scanner.scan(/[^\s>\/]+/)
|
||||
end
|
||||
end
|
||||
attributes[attr.downcase] = value
|
||||
scanner.skip(/\s*/)
|
||||
end
|
||||
|
||||
closing = ( scanner.scan(/\//) ? :self : nil )
|
||||
end
|
||||
|
||||
unless scanner.scan(/\s*>/)
|
||||
if strict
|
||||
raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
|
||||
else
|
||||
# throw away all text until we find what we're looking for
|
||||
scanner.skip_until(/>/) or scanner.terminate
|
||||
end
|
||||
end
|
||||
|
||||
Tag.new(parent, line, pos, name, attributes, closing)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# A node that represents text, rather than markup.
|
||||
class Text < Node #:nodoc:
|
||||
|
||||
attr_reader :content
|
||||
|
||||
# Creates a new text node as a child of the given parent, with the given
|
||||
# content.
|
||||
def initialize(parent, line, pos, content)
|
||||
super(parent, line, pos)
|
||||
@content = content
|
||||
end
|
||||
|
||||
# Returns the content of this node.
|
||||
def to_s
|
||||
@content
|
||||
end
|
||||
|
||||
# Returns +self+ if this node meets the given conditions. Text nodes support
|
||||
# conditions of the following kinds:
|
||||
#
|
||||
# * if +conditions+ is a string, it must be a substring of the node's
|
||||
# content
|
||||
# * if +conditions+ is a regular expression, it must match the node's
|
||||
# content
|
||||
# * if +conditions+ is a hash, it must contain a <tt>:content</tt> key that
|
||||
# is either a string or a regexp, and which is interpreted as described
|
||||
# above.
|
||||
def find(conditions)
|
||||
match(conditions) && self
|
||||
end
|
||||
|
||||
# Returns non-+nil+ if this node meets the given conditions, or +nil+
|
||||
# otherwise. See the discussion of #find for the valid conditions.
|
||||
def match(conditions)
|
||||
case conditions
|
||||
when String
|
||||
@content.index(conditions)
|
||||
when Regexp
|
||||
@content =~ conditions
|
||||
when Hash
|
||||
conditions = validate_conditions(conditions)
|
||||
|
||||
# Text nodes only have :content, :parent, :ancestor
|
||||
unless (conditions.keys - [:content, :parent, :ancestor]).empty?
|
||||
return false
|
||||
end
|
||||
|
||||
match(conditions[:content])
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def ==(node)
|
||||
return false unless super
|
||||
content == node.content
|
||||
end
|
||||
end
|
||||
|
||||
# A CDATA node is simply a text node with a specialized way of displaying
|
||||
# itself.
|
||||
class CDATA < Text #:nodoc:
|
||||
def to_s
|
||||
"<![CDATA[#{super}]>"
|
||||
end
|
||||
end
|
||||
|
||||
# A Tag is any node that represents markup. It may be an opening tag, a
|
||||
# closing tag, or a self-closing tag. It has a name, and may have a hash of
|
||||
# attributes.
|
||||
class Tag < Node #:nodoc:
|
||||
|
||||
# Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
|
||||
attr_reader :closing
|
||||
|
||||
# Either +nil+, or a hash of attributes for this node.
|
||||
attr_reader :attributes
|
||||
|
||||
# The name of this tag.
|
||||
attr_reader :name
|
||||
|
||||
# Create a new node as a child of the given parent, using the given content
|
||||
# to describe the node. It will be parsed and the node name, attributes and
|
||||
# closing status extracted.
|
||||
def initialize(parent, line, pos, name, attributes, closing)
|
||||
super(parent, line, pos)
|
||||
@name = name
|
||||
@attributes = attributes
|
||||
@closing = closing
|
||||
end
|
||||
|
||||
# A convenience for obtaining an attribute of the node. Returns +nil+ if
|
||||
# the node has no attributes.
|
||||
def [](attr)
|
||||
@attributes ? @attributes[attr] : nil
|
||||
end
|
||||
|
||||
# Returns non-+nil+ if this tag can contain child nodes.
|
||||
def childless?(xml = false)
|
||||
return false if xml && @closing.nil?
|
||||
!@closing.nil? ||
|
||||
@name =~ /^(img|br|hr|link|meta|area|base|basefont|
|
||||
col|frame|input|isindex|param)$/ox
|
||||
end
|
||||
|
||||
# Returns a textual representation of the node
|
||||
def to_s
|
||||
if @closing == :close
|
||||
"</#{@name}>"
|
||||
else
|
||||
s = "<#{@name}"
|
||||
@attributes.each do |k,v|
|
||||
s << " #{k}"
|
||||
s << "='#{v.gsub(/'/,"\\\\'")}'" if String === v
|
||||
end
|
||||
s << " /" if @closing == :self
|
||||
s << ">"
|
||||
@children.each { |child| s << child.to_s }
|
||||
s << "</#{@name}>" if @closing != :self && !@children.empty?
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
# If either the node or any of its children meet the given conditions, the
|
||||
# matching node is returned. Otherwise, +nil+ is returned. (See the
|
||||
# description of the valid conditions in the +match+ method.)
|
||||
def find(conditions)
|
||||
match(conditions) && self || super
|
||||
end
|
||||
|
||||
# Returns +true+, indicating that this node represents an HTML tag.
|
||||
def tag?
|
||||
true
|
||||
end
|
||||
|
||||
# Returns +true+ if the node meets any of the given conditions. The
|
||||
# +conditions+ parameter must be a hash of any of the following keys
|
||||
# (all are optional):
|
||||
#
|
||||
# * <tt>:tag</tt>: the node name must match the corresponding value
|
||||
# * <tt>:attributes</tt>: a hash. The node's values must match the
|
||||
# corresponding values in the hash.
|
||||
# * <tt>:parent</tt>: a hash. The node's parent must match the
|
||||
# corresponding hash.
|
||||
# * <tt>:child</tt>: a hash. At least one of the node's immediate children
|
||||
# must meet the criteria described by the hash.
|
||||
# * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
|
||||
# meet the criteria described by the hash.
|
||||
# * <tt>:descendant</tt>: a hash. At least one of the node's descendants
|
||||
# must meet the criteria described by the hash.
|
||||
# * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
|
||||
# meet the criteria described by the hash.
|
||||
# * <tt>:after</tt>: a hash. The node must be after any sibling meeting
|
||||
# the criteria described by the hash, and at least one sibling must match.
|
||||
# * <tt>:before</tt>: a hash. The node must be before any sibling meeting
|
||||
# the criteria described by the hash, and at least one sibling must match.
|
||||
# * <tt>:children</tt>: a hash, for counting children of a node. Accepts the
|
||||
# keys:
|
||||
# ** <tt>:count</tt>: either a number or a range which must equal (or
|
||||
# include) the number of children that match.
|
||||
# ** <tt>:less_than</tt>: the number of matching children must be less than
|
||||
# this number.
|
||||
# ** <tt>:greater_than</tt>: the number of matching children must be
|
||||
# greater than this number.
|
||||
# ** <tt>:only</tt>: another hash consisting of the keys to use
|
||||
# to match on the children, and only matching children will be
|
||||
# counted.
|
||||
#
|
||||
# Conditions are matched using the following algorithm:
|
||||
#
|
||||
# * if the condition is a string, it must be a substring of the value.
|
||||
# * if the condition is a regexp, it must match the value.
|
||||
# * if the condition is a number, the value must match number.to_s.
|
||||
# * if the condition is +true+, the value must not be +nil+.
|
||||
# * if the condition is +false+ or +nil+, the value must be +nil+.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# # test if the node is a "span" tag
|
||||
# node.match :tag => "span"
|
||||
#
|
||||
# # test if the node's parent is a "div"
|
||||
# node.match :parent => { :tag => "div" }
|
||||
#
|
||||
# # test if any of the node's ancestors are "table" tags
|
||||
# node.match :ancestor => { :tag => "table" }
|
||||
#
|
||||
# # test if any of the node's immediate children are "em" tags
|
||||
# node.match :child => { :tag => "em" }
|
||||
#
|
||||
# # test if any of the node's descendants are "strong" tags
|
||||
# node.match :descendant => { :tag => "strong" }
|
||||
#
|
||||
# # test if the node has between 2 and 4 span tags as immediate children
|
||||
# node.match :children => { :count => 2..4, :only => { :tag => "span" } }
|
||||
#
|
||||
# # get funky: test to see if the node is a "div", has a "ul" ancestor
|
||||
# # and an "li" parent (with "class" = "enum"), and whether or not it has
|
||||
# # a "span" descendant that contains # text matching /hello world/:
|
||||
# node.match :tag => "div",
|
||||
# :ancestor => { :tag => "ul" },
|
||||
# :parent => { :tag => "li",
|
||||
# :attributes => { :class => "enum" } },
|
||||
# :descendant => { :tag => "span",
|
||||
# :child => /hello world/ }
|
||||
def match(conditions)
|
||||
conditions = validate_conditions(conditions)
|
||||
|
||||
# check content of child nodes
|
||||
if conditions[:content]
|
||||
if children.empty?
|
||||
return false unless match_condition("", conditions[:content])
|
||||
else
|
||||
return false unless children.find { |child| child.match(conditions[:content]) }
|
||||
end
|
||||
end
|
||||
|
||||
# test the name
|
||||
return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]
|
||||
|
||||
# test attributes
|
||||
(conditions[:attributes] || {}).each do |key, value|
|
||||
return false unless match_condition(self[key], value)
|
||||
end
|
||||
|
||||
# test parent
|
||||
return false unless parent.match(conditions[:parent]) if conditions[:parent]
|
||||
|
||||
# test children
|
||||
return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
|
||||
|
||||
# test ancestors
|
||||
if conditions[:ancestor]
|
||||
return false unless catch :found do
|
||||
p = self
|
||||
throw :found, true if p.match(conditions[:ancestor]) while p = p.parent
|
||||
end
|
||||
end
|
||||
|
||||
# test descendants
|
||||
if conditions[:descendant]
|
||||
return false unless children.find do |child|
|
||||
# test the child
|
||||
child.match(conditions[:descendant]) ||
|
||||
# test the child's descendants
|
||||
child.match(:descendant => conditions[:descendant])
|
||||
end
|
||||
end
|
||||
|
||||
# count children
|
||||
if opts = conditions[:children]
|
||||
matches = children.select do |c|
|
||||
c.match(/./) or
|
||||
(c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
|
||||
end
|
||||
|
||||
matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
|
||||
opts.each do |key, value|
|
||||
next if key == :only
|
||||
case key
|
||||
when :count
|
||||
if Integer === value
|
||||
return false if matches.length != value
|
||||
else
|
||||
return false unless value.include?(matches.length)
|
||||
end
|
||||
when :less_than
|
||||
return false unless matches.length < value
|
||||
when :greater_than
|
||||
return false unless matches.length > value
|
||||
else raise "unknown count condition #{key}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# test siblings
|
||||
if conditions[:sibling] || conditions[:before] || conditions[:after]
|
||||
siblings = parent ? parent.children : []
|
||||
self_index = siblings.index(self)
|
||||
|
||||
if conditions[:sibling]
|
||||
return false unless siblings.detect do |s|
|
||||
s != self && s.match(conditions[:sibling])
|
||||
end
|
||||
end
|
||||
|
||||
if conditions[:before]
|
||||
return false unless siblings[self_index+1..-1].detect do |s|
|
||||
s != self && s.match(conditions[:before])
|
||||
end
|
||||
end
|
||||
|
||||
if conditions[:after]
|
||||
return false unless siblings[0,self_index].detect do |s|
|
||||
s != self && s.match(conditions[:after])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def ==(node)
|
||||
return false unless super
|
||||
return false unless closing == node.closing && self.name == node.name
|
||||
attributes == node.attributes
|
||||
end
|
||||
|
||||
private
|
||||
# Match the given value to the given condition.
|
||||
def match_condition(value, condition)
|
||||
case condition
|
||||
when String
|
||||
value && value == condition
|
||||
when Regexp
|
||||
value && value.match(condition)
|
||||
when Numeric
|
||||
value == condition.to_s
|
||||
when true
|
||||
!value.nil?
|
||||
when false, nil
|
||||
value.nil?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
105
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
vendored
Normal file
105
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
require 'strscan'
|
||||
|
||||
module HTML #:nodoc:
|
||||
|
||||
# A simple HTML tokenizer. It simply breaks a stream of text into tokens, where each
|
||||
# token is a string. Each string represents either "text", or an HTML element.
|
||||
#
|
||||
# This currently assumes valid XHTML, which means no free < or > characters.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# tokenizer = HTML::Tokenizer.new(text)
|
||||
# while token = tokenizer.next
|
||||
# p token
|
||||
# end
|
||||
class Tokenizer #:nodoc:
|
||||
|
||||
# The current (byte) position in the text
|
||||
attr_reader :position
|
||||
|
||||
# The current line number
|
||||
attr_reader :line
|
||||
|
||||
# Create a new Tokenizer for the given text.
|
||||
def initialize(text)
|
||||
@scanner = StringScanner.new(text)
|
||||
@position = 0
|
||||
@line = 0
|
||||
@current_line = 1
|
||||
end
|
||||
|
||||
# Return the next token in the sequence, or +nil+ if there are no more tokens in
|
||||
# the stream.
|
||||
def next
|
||||
return nil if @scanner.eos?
|
||||
@position = @scanner.pos
|
||||
@line = @current_line
|
||||
if @scanner.check(/<\S/)
|
||||
update_current_line(scan_tag)
|
||||
else
|
||||
update_current_line(scan_text)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Treat the text at the current position as a tag, and scan it. Supports
|
||||
# comments, doctype tags, and regular tags, and ignores less-than and
|
||||
# greater-than characters within quoted strings.
|
||||
def scan_tag
|
||||
tag = @scanner.getch
|
||||
if @scanner.scan(/!--/) # comment
|
||||
tag << @scanner.matched
|
||||
tag << (@scanner.scan_until(/--\s*>/) || @scanner.scan_until(/\Z/))
|
||||
elsif @scanner.scan(/!\[CDATA\[/)
|
||||
tag << @scanner.matched
|
||||
tag << @scanner.scan_until(/\]\]>/)
|
||||
elsif @scanner.scan(/!/) # doctype
|
||||
tag << @scanner.matched
|
||||
tag << consume_quoted_regions
|
||||
else
|
||||
tag << consume_quoted_regions
|
||||
end
|
||||
tag
|
||||
end
|
||||
|
||||
# Scan all text up to the next < character and return it.
|
||||
def scan_text
|
||||
"#{@scanner.getch}#{@scanner.scan(/[^<]*/)}"
|
||||
end
|
||||
|
||||
# Counts the number of newlines in the text and updates the current line
|
||||
# accordingly.
|
||||
def update_current_line(text)
|
||||
text.scan(/\r?\n/) { @current_line += 1 }
|
||||
end
|
||||
|
||||
# Skips over quoted strings, so that less-than and greater-than characters
|
||||
# within the strings are ignored.
|
||||
def consume_quoted_regions
|
||||
text = ""
|
||||
loop do
|
||||
match = @scanner.scan_until(/['"<>]/) or break
|
||||
|
||||
delim = @scanner.matched
|
||||
if delim == "<"
|
||||
match = match.chop
|
||||
@scanner.pos -= 1
|
||||
end
|
||||
|
||||
text << match
|
||||
break if delim == "<" || delim == ">"
|
||||
|
||||
# consume the quoted region
|
||||
while match = @scanner.scan_until(/[\\#{delim}]/)
|
||||
text << match
|
||||
break if @scanner.matched == delim
|
||||
text << @scanner.getch # skip the escaped character
|
||||
end
|
||||
end
|
||||
text
|
||||
end
|
||||
end
|
||||
|
||||
end
|
11
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/version.rb
vendored
Normal file
11
vendor/rails/actionpack/lib/action_controller/vendor/html-scanner/html/version.rb
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
module HTML #:nodoc:
|
||||
module Version #:nodoc:
|
||||
|
||||
MAJOR = 0
|
||||
MINOR = 5
|
||||
TINY = 3
|
||||
|
||||
STRING = [ MAJOR, MINOR, TINY ].join(".")
|
||||
|
||||
end
|
||||
end
|
97
vendor/rails/actionpack/lib/action_controller/vendor/xml_node.rb
vendored
Normal file
97
vendor/rails/actionpack/lib/action_controller/vendor/xml_node.rb
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
require 'rexml/document'
|
||||
|
||||
# SimpleXML like xml parser. Written by leon breet from the ruby on rails Mailing list
|
||||
class XmlNode #:nodoc:
|
||||
attr :node
|
||||
|
||||
def initialize(node, options = {})
|
||||
@node = node
|
||||
@children = {}
|
||||
@raise_errors = options[:raise_errors]
|
||||
end
|
||||
|
||||
def self.from_xml(xml_or_io)
|
||||
document = REXML::Document.new(xml_or_io)
|
||||
if document.root
|
||||
XmlNode.new(document.root)
|
||||
else
|
||||
XmlNode.new(document)
|
||||
end
|
||||
end
|
||||
|
||||
def node_encoding
|
||||
@node.encoding
|
||||
end
|
||||
|
||||
def node_name
|
||||
@node.name
|
||||
end
|
||||
|
||||
def node_value
|
||||
@node.text
|
||||
end
|
||||
|
||||
def node_value=(value)
|
||||
@node.text = value
|
||||
end
|
||||
|
||||
def xpath(expr)
|
||||
matches = nil
|
||||
REXML::XPath.each(@node, expr) do |element|
|
||||
matches ||= XmlNodeList.new
|
||||
matches << (@children[element] ||= XmlNode.new(element))
|
||||
end
|
||||
matches
|
||||
end
|
||||
|
||||
def method_missing(name, *args)
|
||||
name = name.to_s
|
||||
nodes = nil
|
||||
@node.each_element(name) do |element|
|
||||
nodes ||= XmlNodeList.new
|
||||
nodes << (@children[element] ||= XmlNode.new(element))
|
||||
end
|
||||
nodes
|
||||
end
|
||||
|
||||
def <<(node)
|
||||
if node.is_a? REXML::Node
|
||||
child = node
|
||||
elsif node.respond_to? :node
|
||||
child = node.node
|
||||
end
|
||||
@node.add_element child
|
||||
@children[child] ||= XmlNode.new(child)
|
||||
end
|
||||
|
||||
def [](name)
|
||||
@node.attributes[name.to_s]
|
||||
end
|
||||
|
||||
def []=(name, value)
|
||||
@node.attributes[name.to_s] = value
|
||||
end
|
||||
|
||||
def to_s
|
||||
@node.to_s
|
||||
end
|
||||
|
||||
def to_i
|
||||
to_s.to_i
|
||||
end
|
||||
end
|
||||
|
||||
class XmlNodeList < Array #:nodoc:
|
||||
def [](i)
|
||||
i.is_a?(String) ? super(0)[i] : super(i)
|
||||
end
|
||||
|
||||
def []=(i, value)
|
||||
i.is_a?(String) ? self[0][i] = value : super(i, value)
|
||||
end
|
||||
|
||||
def method_missing(name, *args)
|
||||
name = name.to_s
|
||||
self[0].__send__(name, *args)
|
||||
end
|
||||
end
|
1019
vendor/rails/actionpack/lib/action_controller/vendor/xml_simple.rb
vendored
Normal file
1019
vendor/rails/actionpack/lib/action_controller/vendor/xml_simple.rb
vendored
Normal file
File diff suppressed because it is too large
Load diff
96
vendor/rails/actionpack/lib/action_controller/verification.rb
vendored
Normal file
96
vendor/rails/actionpack/lib/action_controller/verification.rb
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
module ActionController #:nodoc:
|
||||
module Verification #:nodoc:
|
||||
def self.append_features(base) #:nodoc:
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# This module provides a class-level method for specifying that certain
|
||||
# actions are guarded against being called without certain prerequisites
|
||||
# being met. This is essentially a special kind of before_filter.
|
||||
#
|
||||
# An action may be guarded against being invoked without certain request
|
||||
# parameters being set, or without certain session values existing.
|
||||
#
|
||||
# When a verification is violated, values may be inserted into the flash, and
|
||||
# a specified redirection is triggered.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# class GlobalController < ActionController::Base
|
||||
# # prevent the #update_settings action from being invoked unless
|
||||
# # the 'admin_privileges' request parameter exists.
|
||||
# verify :params => "admin_privileges", :only => :update_post,
|
||||
# :redirect_to => { :action => "settings" }
|
||||
#
|
||||
# # disallow a post from being updated if there was no information
|
||||
# # submitted with the post, and if there is no active post in the
|
||||
# # session, and if there is no "note" key in the flash.
|
||||
# verify :params => "post", :session => "post", "flash" => "note",
|
||||
# :only => :update_post,
|
||||
# :add_flash => { "alert" => "Failed to create your message" },
|
||||
# :redirect_to => :category_url
|
||||
#
|
||||
module ClassMethods
|
||||
# Verify the given actions so that if certain prerequisites are not met,
|
||||
# the user is redirected to a different action. The +options+ parameter
|
||||
# is a hash consisting of the following key/value pairs:
|
||||
#
|
||||
# * <tt>:params</tt>: a single key or an array of keys that must
|
||||
# be in the <tt>params</tt> hash in order for the action(s) to be safely
|
||||
# called.
|
||||
# * <tt>:session</tt>: a single key or an array of keys that must
|
||||
# be in the @session in order for the action(s) to be safely called.
|
||||
# * <tt>:flash</tt>: a single key or an array of keys that must
|
||||
# be in the flash in order for the action(s) to be safely called.
|
||||
# * <tt>:method</tt>: a single key or an array of keys--any one of which
|
||||
# must match the current request method in order for the action(s) to
|
||||
# be safely called. (The key should be a symbol: <tt>:get</tt> or
|
||||
# <tt>:post</tt>, for example.)
|
||||
# * <tt>:xhr</tt>: true/false option to ensure that the request is coming
|
||||
# from an Ajax call or not.
|
||||
# * <tt>:add_flash</tt>: a hash of name/value pairs that should be merged
|
||||
# into the session's flash if the prerequisites cannot be satisfied.
|
||||
# * <tt>:redirect_to</tt>: the redirection parameters to be used when
|
||||
# redirecting if the prerequisites cannot be satisfied.
|
||||
# * <tt>:render</tt>: the render parameters to be used when
|
||||
# the prerequisites cannot be satisfied.
|
||||
# * <tt>:only</tt>: only apply this verification to the actions specified
|
||||
# in the associated array (may also be a single value).
|
||||
# * <tt>:except</tt>: do not apply this verification to the actions
|
||||
# specified in the associated array (may also be a single value).
|
||||
def verify(options={})
|
||||
filter_opts = { :only => options[:only], :except => options[:except] }
|
||||
before_filter(filter_opts) do |c|
|
||||
c.send :verify_action, options
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def verify_action(options) #:nodoc:
|
||||
prereqs_invalid =
|
||||
[*options[:params] ].find { |v| @params[v].nil? } ||
|
||||
[*options[:session]].find { |v| @session[v].nil? } ||
|
||||
[*options[:flash] ].find { |v| flash[v].nil? }
|
||||
|
||||
if !prereqs_invalid && options[:method]
|
||||
prereqs_invalid ||=
|
||||
[*options[:method]].all? { |v| @request.method != v.to_sym }
|
||||
end
|
||||
|
||||
prereqs_invalid ||= (request.xhr? != options[:xhr]) unless options[:xhr].nil?
|
||||
|
||||
if prereqs_invalid
|
||||
flash.update(options[:add_flash]) if options[:add_flash]
|
||||
unless performed?
|
||||
render(options[:render]) if options[:render]
|
||||
redirect_to(options[:redirect_to]) if options[:redirect_to]
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
private :verify_action
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue