mirror of https://github.com/rails/rails
Merge remote branch 'rails/master'
This commit is contained in:
commit
c63cf7bf0d
2
Gemfile
2
Gemfile
|
@ -1,7 +1,7 @@
|
|||
source 'http://rubygems.org'
|
||||
|
||||
gem "arel", :git => "git://github.com/rails/arel.git"
|
||||
gem "rack-mount", :git => "git://github.com/rails/rack-mount.git"
|
||||
#gem "rack-mount", :git => "git://github.com/rails/rack-mount.git"
|
||||
gem "rails", :path => File.dirname(__FILE__)
|
||||
|
||||
gem "rake", ">= 0.8.7"
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
*Rails 3.0.0 [Release Candidate] (unreleased)*
|
||||
|
||||
* link_to, button_to, and tag/tag_options now rely on html_escape instead of escape_once. [fxn]
|
||||
|
||||
* url_for returns always unescaped strings, and the :escape option is gone. [fxn]
|
||||
|
||||
* Added accept-charset parameter and _snowman hidden field to force the contents
|
||||
of Rails POSTed forms to be in UTF-8 [Yehuda Katz]
|
||||
|
||||
* Upgrade to Rack 1.2.1 [Jeremy Kemper]
|
||||
|
||||
* Allow :path to be given to match/get/post/put/delete instead of :path_names in the new router [Carlos Antônio da Silva]
|
||||
|
||||
* Added resources_path_names to the new router DSL [José Valim]
|
||||
|
@ -22,7 +31,6 @@
|
|||
|
||||
* Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino]
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
|
||||
|
||||
* Remove middleware laziness [José Valim]
|
||||
|
|
|
@ -23,9 +23,9 @@ Gem::Specification.new do |s|
|
|||
s.add_dependency('activemodel', version)
|
||||
s.add_dependency('builder', '~> 2.1.2')
|
||||
s.add_dependency('i18n', '~> 0.4.1')
|
||||
s.add_dependency('rack', '~> 1.1.0')
|
||||
s.add_dependency('rack', '~> 1.2.1')
|
||||
s.add_dependency('rack-test', '~> 0.5.4')
|
||||
s.add_dependency('rack-mount', '~> 0.6.5')
|
||||
#s.add_dependency('rack-mount', '~> 0.6.6')
|
||||
s.add_dependency('tzinfo', '~> 0.3.16')
|
||||
s.add_dependency('erubis', '~> 2.6.5')
|
||||
end
|
||||
|
|
|
@ -58,12 +58,12 @@ module ActionController
|
|||
|
||||
module ClassMethods
|
||||
def helpers_dir
|
||||
ActiveSupport::Deprecation.warn "helpers_dir is deprecated, use helpers_path instead"
|
||||
ActiveSupport::Deprecation.warn "helpers_dir is deprecated, use helpers_path instead", caller
|
||||
self.helpers_path
|
||||
end
|
||||
|
||||
def helpers_dir=(value)
|
||||
ActiveSupport::Deprecation.warn "helpers_dir= is deprecated, use helpers_path= instead"
|
||||
ActiveSupport::Deprecation.warn "helpers_dir= is deprecated, use helpers_path= instead", caller
|
||||
self.helpers_path = Array(value)
|
||||
end
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ module HTML #:nodoc:
|
|||
case text
|
||||
when "\\" then
|
||||
value << text
|
||||
break if scanner.eos?
|
||||
value << scanner.getch
|
||||
when delim
|
||||
break
|
||||
|
|
|
@ -96,6 +96,7 @@ module HTML #:nodoc:
|
|||
while match = @scanner.scan_until(/[\\#{delim}]/)
|
||||
text << match
|
||||
break if @scanner.matched == delim
|
||||
break if @scanner.eos?
|
||||
text << @scanner.getch # skip the escaped character
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,7 +89,7 @@ module ActionDispatch
|
|||
if etag? || last_modified? || !@cache_control.empty?
|
||||
set_conditional_cache_control!
|
||||
elsif nonempty_ok_response?
|
||||
self.etag = @body
|
||||
self.etag = body
|
||||
|
||||
if request && request.etag_matches?(etag)
|
||||
self.status = 304
|
||||
|
|
|
@ -6,7 +6,11 @@ module ActionDispatch
|
|||
module Parameters
|
||||
# Returns both GET and POST \parameters in a single hash.
|
||||
def parameters
|
||||
@env["action_dispatch.request.parameters"] ||= request_parameters.merge(query_parameters).update(path_parameters).with_indifferent_access
|
||||
@env["action_dispatch.request.parameters"] ||= begin
|
||||
params = request_parameters.merge(query_parameters)
|
||||
params.merge!(path_parameters)
|
||||
encode_params(params).with_indifferent_access
|
||||
end
|
||||
end
|
||||
alias :params :parameters
|
||||
|
||||
|
@ -32,6 +36,31 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO: Validate that the characters are UTF-8. If they aren't,
|
||||
# you'll get a weird error down the road, but our form handling
|
||||
# should really prevent that from happening
|
||||
def encode_params(params)
|
||||
return params unless "ruby".encoding_aware?
|
||||
|
||||
if params.is_a?(String)
|
||||
return params.force_encoding("UTF-8").encode!
|
||||
elsif !params.is_a?(Hash)
|
||||
return params
|
||||
end
|
||||
|
||||
params.each do |k, v|
|
||||
case v
|
||||
when Hash
|
||||
encode_params(v)
|
||||
when Array
|
||||
v.map! {|el| encode_params(el) }
|
||||
else
|
||||
encode_params(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Convert nested Hash to HashWithIndifferentAccess
|
||||
def normalize_parameters(value)
|
||||
case value
|
||||
|
|
|
@ -30,6 +30,8 @@ module ActionDispatch
|
|||
|
||||
class DeprecatedMapper #:nodoc:
|
||||
def initialize(set) #:nodoc:
|
||||
ActiveSupport::Deprecation.warn "You are using the old router DSL which will be removed in Rails 3.1. " <<
|
||||
"Please check how to update your router file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
|
||||
@set = set
|
||||
end
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
defaults[:controller] ||= default_controller
|
||||
defaults[:action] ||= default_action
|
||||
|
||||
defaults.delete(:controller) if defaults[:controller].blank?
|
||||
defaults.delete(:action) if defaults[:action].blank?
|
||||
|
@ -187,6 +188,12 @@ module ActionDispatch
|
|||
@scope[:controller].to_s
|
||||
end
|
||||
end
|
||||
|
||||
def default_action
|
||||
if @options[:action]
|
||||
@options[:action].to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Invokes Rack::Mount::Utils.normalize path and ensure that
|
||||
|
@ -433,10 +440,14 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
module Resources
|
||||
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
|
||||
# a path appended since they fit properly in their scope level.
|
||||
VALID_ON_OPTIONS = [:new, :collection, :member]
|
||||
CANONICAL_ACTIONS = [:index, :create, :new, :show, :update, :destroy]
|
||||
MERGE_FROM_SCOPE_OPTIONS = [:shallow, :constraints]
|
||||
|
||||
class Resource #:nodoc:
|
||||
def self.default_actions
|
||||
[:index, :create, :new, :show, :update, :destroy, :edit]
|
||||
end
|
||||
DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
|
||||
|
||||
attr_reader :controller, :path, :options
|
||||
|
||||
|
@ -449,7 +460,7 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
def default_actions
|
||||
self.class.default_actions
|
||||
self.class::DEFAULT_ACTIONS
|
||||
end
|
||||
|
||||
def actions
|
||||
|
@ -533,8 +544,8 @@ module ActionDispatch
|
|||
["#{path}/:id", options]
|
||||
end
|
||||
|
||||
def new_scope
|
||||
[path]
|
||||
def new_scope(new_path)
|
||||
["#{path}/#{new_path}"]
|
||||
end
|
||||
|
||||
def nested_scope
|
||||
|
@ -543,9 +554,7 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
class SingletonResource < Resource #:nodoc:
|
||||
def self.default_actions
|
||||
[:show, :create, :update, :destroy, :new, :edit]
|
||||
end
|
||||
DEFAULT_ACTIONS = [:show, :create, :update, :destroy, :new, :edit]
|
||||
|
||||
def initialize(entities, options)
|
||||
@name = entities.to_s
|
||||
|
@ -591,8 +600,6 @@ module ActionDispatch
|
|||
|
||||
def resource(*resources, &block)
|
||||
options = resources.extract_options!
|
||||
options = (@scope[:options] || {}).merge(options)
|
||||
options[:shallow] = true if @scope[:shallow] && !options.has_key?(:shallow)
|
||||
|
||||
if apply_common_behavior_for(:resource, resources, options, &block)
|
||||
return self
|
||||
|
@ -602,9 +609,12 @@ module ActionDispatch
|
|||
yield if block_given?
|
||||
|
||||
collection_scope do
|
||||
post :create if parent_resource.actions.include?(:create)
|
||||
get :new if parent_resource.actions.include?(:new)
|
||||
end
|
||||
post :create
|
||||
end if parent_resource.actions.include?(:create)
|
||||
|
||||
new_scope do
|
||||
get :new
|
||||
end if parent_resource.actions.include?(:new)
|
||||
|
||||
member_scope do
|
||||
get :show if parent_resource.actions.include?(:show)
|
||||
|
@ -619,8 +629,6 @@ module ActionDispatch
|
|||
|
||||
def resources(*resources, &block)
|
||||
options = resources.extract_options!
|
||||
options = (@scope[:options] || {}).merge(options)
|
||||
options[:shallow] = true if @scope[:shallow] && !options.has_key?(:shallow)
|
||||
|
||||
if apply_common_behavior_for(:resources, resources, options, &block)
|
||||
return self
|
||||
|
@ -632,9 +640,12 @@ module ActionDispatch
|
|||
collection_scope do
|
||||
get :index if parent_resource.actions.include?(:index)
|
||||
post :create if parent_resource.actions.include?(:create)
|
||||
get :new if parent_resource.actions.include?(:new)
|
||||
end
|
||||
|
||||
new_scope do
|
||||
get :new
|
||||
end if parent_resource.actions.include?(:new)
|
||||
|
||||
member_scope do
|
||||
get :show if parent_resource.actions.include?(:show)
|
||||
put :update if parent_resource.actions.include?(:update)
|
||||
|
@ -671,14 +682,10 @@ module ActionDispatch
|
|||
raise ArgumentError, "can't use new outside resource(s) scope"
|
||||
end
|
||||
|
||||
with_scope_level(:new) do
|
||||
scope(*parent_resource.new_scope) do
|
||||
scope(action_path(:new)) do
|
||||
new_scope do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def nested
|
||||
unless resource_scope?
|
||||
|
@ -717,8 +724,7 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
def match(*args)
|
||||
options = args.extract_options!
|
||||
|
||||
options = args.extract_options!.dup
|
||||
options[:anchor] = true unless options.key?(:anchor)
|
||||
|
||||
if args.length > 1
|
||||
|
@ -726,17 +732,12 @@ module ActionDispatch
|
|||
return self
|
||||
end
|
||||
|
||||
if [:collection, :member, :new].include?(options[:on])
|
||||
on = options.delete(:on)
|
||||
if VALID_ON_OPTIONS.include?(on)
|
||||
args.push(options)
|
||||
|
||||
case options.delete(:on)
|
||||
when :collection
|
||||
return collection { match(*args) }
|
||||
when :member
|
||||
return member { match(*args) }
|
||||
when :new
|
||||
return new { match(*args) }
|
||||
end
|
||||
return send(on){ match(*args) }
|
||||
elsif on
|
||||
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
||||
end
|
||||
|
||||
if @scope[:scope_level] == :resource
|
||||
|
@ -745,10 +746,12 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
path = options.delete(:path)
|
||||
action = args.first
|
||||
|
||||
if args.first.is_a?(Symbol)
|
||||
path = path_for_action(args.first, path)
|
||||
options = options_for_action(args.first, options)
|
||||
if action.is_a?(Symbol)
|
||||
path = path_for_action(action, path)
|
||||
options[:to] ||= action
|
||||
options[:as] = name_for_action(action, options[:as])
|
||||
|
||||
with_exclusive_scope do
|
||||
return super(path, options)
|
||||
|
@ -786,11 +789,11 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
protected
|
||||
|
||||
def parent_resource #:nodoc:
|
||||
@scope[:scope_level_resource]
|
||||
end
|
||||
|
||||
private
|
||||
def apply_common_behavior_for(method, resources, options, &block)
|
||||
if resources.length > 1
|
||||
resources.each { |r| send(method, r, options, &block) }
|
||||
|
@ -804,6 +807,10 @@ module ActionDispatch
|
|||
return true
|
||||
end
|
||||
|
||||
scope_options = @scope.slice(*MERGE_FROM_SCOPE_OPTIONS).delete_if{ |k,v| v.blank? }
|
||||
options.reverse_merge!(scope_options) unless scope_options.empty?
|
||||
options.reverse_merge!(@scope[:options]) unless @scope[:options].blank?
|
||||
|
||||
if resource_scope?
|
||||
nested do
|
||||
send(method, resources.pop, options, &block)
|
||||
|
@ -852,6 +859,14 @@ module ActionDispatch
|
|||
end
|
||||
end
|
||||
|
||||
def new_scope
|
||||
with_scope_level(:new) do
|
||||
scope(*parent_resource.new_scope(action_path(:new))) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def collection_scope
|
||||
with_scope_level(:collection) do
|
||||
scope(*parent_resource.collection_scope) do
|
||||
|
@ -868,101 +883,73 @@ module ActionDispatch
|
|||
end
|
||||
end
|
||||
|
||||
def canonical_action?(action, flag)
|
||||
flag && CANONICAL_ACTIONS.include?(action)
|
||||
end
|
||||
|
||||
def shallow_scoping?
|
||||
parent_resource && parent_resource.shallow? && @scope[:scope_level] == :member
|
||||
end
|
||||
|
||||
def path_for_action(action, path)
|
||||
case action
|
||||
when :index, :create
|
||||
"#{@scope[:path]}(.:format)"
|
||||
when :show, :update, :destroy
|
||||
if parent_resource.shallow?
|
||||
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id(.:format)"
|
||||
prefix = shallow_scoping? ?
|
||||
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
|
||||
|
||||
if canonical_action?(action, path.blank?)
|
||||
"#{prefix}(.:format)"
|
||||
else
|
||||
"#{@scope[:path]}(.:format)"
|
||||
end
|
||||
when :new
|
||||
"#{@scope[:path]}/#{action_path(:new)}(.:format)"
|
||||
when :edit
|
||||
if parent_resource.shallow?
|
||||
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(:edit)}(.:format)"
|
||||
else
|
||||
"#{@scope[:path]}/#{action_path(:edit)}(.:format)"
|
||||
end
|
||||
else
|
||||
case @scope[:scope_level]
|
||||
when :collection, :new
|
||||
"#{@scope[:path]}/#{action_path(action, path)}(.:format)"
|
||||
else
|
||||
if parent_resource.shallow?
|
||||
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(action, path)}(.:format)"
|
||||
else
|
||||
"#{@scope[:path]}/#{action_path(action, path)}(.:format)"
|
||||
end
|
||||
end
|
||||
"#{prefix}/#{action_path(action, path)}(.:format)"
|
||||
end
|
||||
end
|
||||
|
||||
def path_for_custom_action
|
||||
case @scope[:scope_level]
|
||||
when :collection, :new
|
||||
@scope[:path]
|
||||
else
|
||||
if parent_resource.shallow?
|
||||
if shallow_scoping?
|
||||
"#{@scope[:shallow_path]}/#{parent_resource.path}/:id"
|
||||
else
|
||||
@scope[:path]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def action_path(name, path = nil)
|
||||
path || @scope[:path_names][name.to_sym] || name.to_s
|
||||
end
|
||||
|
||||
def options_for_action(action, options)
|
||||
options.reverse_merge(
|
||||
:to => action,
|
||||
:as => name_for_action(action)
|
||||
)
|
||||
def prefix_name_for_action(action, as)
|
||||
if as.present?
|
||||
"#{as}_"
|
||||
elsif as
|
||||
""
|
||||
elsif !canonical_action?(action, @scope[:scope_level])
|
||||
"#{action}_"
|
||||
end
|
||||
end
|
||||
|
||||
def name_for_action(action)
|
||||
name_prefix = @scope[:as].blank? ? "" : "#{@scope[:as]}_"
|
||||
shallow_prefix = @scope[:shallow_prefix].blank? ? "" : "#{@scope[:shallow_prefix]}_"
|
||||
def name_for_action(action, as=nil)
|
||||
prefix = prefix_name_for_action(action, as)
|
||||
name_prefix = @scope[:as]
|
||||
|
||||
case action
|
||||
when :index, :create
|
||||
"#{name_prefix}#{parent_resource.collection_name}"
|
||||
when :show, :update, :destroy
|
||||
if parent_resource.shallow?
|
||||
"#{shallow_prefix}#{parent_resource.member_name}"
|
||||
else
|
||||
"#{name_prefix}#{parent_resource.member_name}"
|
||||
if parent_resource
|
||||
collection_name = parent_resource.collection_name
|
||||
member_name = parent_resource.member_name
|
||||
name_prefix = "#{name_prefix}_" if name_prefix.present?
|
||||
end
|
||||
when :edit
|
||||
if parent_resource.shallow?
|
||||
"edit_#{shallow_prefix}#{parent_resource.member_name}"
|
||||
else
|
||||
"edit_#{name_prefix}#{parent_resource.member_name}"
|
||||
end
|
||||
when :new
|
||||
"new_#{name_prefix}#{parent_resource.member_name}"
|
||||
else
|
||||
|
||||
case @scope[:scope_level]
|
||||
when :collection
|
||||
"#{action}_#{name_prefix}#{parent_resource.collection_name}"
|
||||
"#{prefix}#{name_prefix}#{collection_name}"
|
||||
when :new
|
||||
"#{action}_new_#{name_prefix}#{parent_resource.member_name}"
|
||||
"#{prefix}new_#{name_prefix}#{member_name}"
|
||||
else
|
||||
if parent_resource.shallow?
|
||||
"#{action}_#{shallow_prefix}#{parent_resource.member_name}"
|
||||
if shallow_scoping?
|
||||
shallow_prefix = "#{@scope[:shallow_prefix]}_" if @scope[:shallow_prefix].present?
|
||||
"#{prefix}#{shallow_prefix}#{member_name}"
|
||||
else
|
||||
"#{action}_#{name_prefix}#{parent_resource.member_name}"
|
||||
"#{prefix}#{name_prefix}#{member_name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
include Base
|
||||
include HttpHelpers
|
||||
include Scoping
|
||||
|
|
|
@ -2,9 +2,10 @@ module ActionDispatch
|
|||
module Routing
|
||||
class Route #:nodoc:
|
||||
attr_reader :app, :conditions, :defaults, :name
|
||||
attr_reader :path, :requirements
|
||||
attr_reader :path, :requirements, :set
|
||||
|
||||
def initialize(app, conditions, requirements, defaults, name, anchor)
|
||||
def initialize(set, app, conditions, requirements, defaults, name, anchor)
|
||||
@set = set
|
||||
@app = app
|
||||
@defaults = defaults
|
||||
@name = name
|
||||
|
@ -24,6 +25,9 @@ module ActionDispatch
|
|||
h[k] = Rack::Mount::RegexpWithNamedGroups.new(v)
|
||||
h
|
||||
}
|
||||
|
||||
@conditions.delete_if{ |k,v| k != :path_info && !valid_condition?(k) }
|
||||
@requirements.delete_if{ |k,v| !valid_condition?(k) }
|
||||
end
|
||||
|
||||
def verb
|
||||
|
@ -50,6 +54,11 @@ module ActionDispatch
|
|||
"%-6s %-40s %s" % [(verb || :any).to_s.upcase, path, requirements.inspect]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def valid_condition?(method)
|
||||
segment_keys.include?(method) || set.valid_conditions.include?(method)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
require 'rack/mount'
|
||||
require 'forwardable'
|
||||
require 'active_support/core_ext/object/to_query'
|
||||
require 'action_dispatch/routing/deprecated_mapper'
|
||||
|
||||
$: << File.expand_path('../../vendor/rack-mount-0.6.6.pre', __FILE__)
|
||||
require 'rack/mount'
|
||||
|
||||
module ActionDispatch
|
||||
module Routing
|
||||
class RouteSet #:nodoc:
|
||||
|
@ -187,7 +189,7 @@ module ActionDispatch
|
|||
|
||||
attr_accessor :set, :routes, :named_routes
|
||||
attr_accessor :disable_clear_and_finalize, :resources_path_names
|
||||
attr_accessor :default_url_options, :request_class
|
||||
attr_accessor :default_url_options, :request_class, :valid_conditions
|
||||
|
||||
def self.default_resources_path_names
|
||||
{ :new => 'new', :edit => 'edit' }
|
||||
|
@ -199,7 +201,11 @@ module ActionDispatch
|
|||
self.resources_path_names = self.class.default_resources_path_names.dup
|
||||
self.controller_namespaces = Set.new
|
||||
self.default_url_options = {}
|
||||
|
||||
self.request_class = request_class
|
||||
self.valid_conditions = request_class.public_instance_methods.map { |m| m.to_sym }
|
||||
self.valid_conditions.delete(:id)
|
||||
self.valid_conditions.push(:controller, :action)
|
||||
|
||||
@disable_clear_and_finalize = false
|
||||
clear!
|
||||
|
@ -277,7 +283,7 @@ module ActionDispatch
|
|||
end
|
||||
|
||||
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
|
||||
route = Route.new(app, conditions, requirements, defaults, name, anchor)
|
||||
route = Route.new(self, app, conditions, requirements, defaults, name, anchor)
|
||||
@set.add_route(*route)
|
||||
named_routes[name] = route if name
|
||||
routes << route
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
require 'rack'
|
||||
|
||||
module Rack #:nodoc:
|
||||
# A stackable dynamic tree based Rack router.
|
||||
#
|
||||
# Rack::Mount supports Rack's Cascade style of trying several routes until
|
||||
# it finds one that is not a 404. This allows multiple routes to be nested
|
||||
# or stacked on top of each other. Since the application endpoint can
|
||||
# trigger the router to continue matching, middleware can be used to add
|
||||
# arbitrary conditions to any route. This allows you to route based on
|
||||
# other request attributes, session information, or even data dynamically
|
||||
# pulled from a database.
|
||||
module Mount
|
||||
autoload :CodeGeneration, 'rack/mount/code_generation'
|
||||
autoload :GeneratableRegexp, 'rack/mount/generatable_regexp'
|
||||
autoload :Multimap, 'rack/mount/multimap'
|
||||
autoload :Prefix, 'rack/mount/prefix'
|
||||
autoload :RegexpWithNamedGroups, 'rack/mount/regexp_with_named_groups'
|
||||
autoload :Route, 'rack/mount/route'
|
||||
autoload :RouteSet, 'rack/mount/route_set'
|
||||
autoload :RoutingError, 'rack/mount/route_set'
|
||||
autoload :Strexp, 'rack/mount/strexp'
|
||||
autoload :Utils, 'rack/mount/utils'
|
||||
autoload :Version, 'rack/mount/version'
|
||||
|
||||
module Analysis #:nodoc:
|
||||
autoload :Frequency, 'rack/mount/analysis/frequency'
|
||||
autoload :Histogram, 'rack/mount/analysis/histogram'
|
||||
autoload :Splitting, 'rack/mount/analysis/splitting'
|
||||
end
|
||||
end
|
||||
end
|
60
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb
vendored
Normal file
60
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
require 'rack/mount/utils'
|
||||
|
||||
module Rack::Mount
|
||||
module Analysis
|
||||
class Frequency #:nodoc:
|
||||
def initialize(*keys)
|
||||
clear
|
||||
keys.each { |key| self << key }
|
||||
end
|
||||
|
||||
def clear
|
||||
@raw_keys = []
|
||||
@key_frequency = Analysis::Histogram.new
|
||||
self
|
||||
end
|
||||
|
||||
def <<(key)
|
||||
raise ArgumentError unless key.is_a?(Hash)
|
||||
@raw_keys << key
|
||||
nil
|
||||
end
|
||||
|
||||
def possible_keys
|
||||
@possible_keys ||= begin
|
||||
@raw_keys.map do |key|
|
||||
key.inject({}) { |requirements, (method, requirement)|
|
||||
process_key(requirements, method, requirement)
|
||||
requirements
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process_key(requirements, method, requirement)
|
||||
if requirement.is_a?(Regexp)
|
||||
expression = Utils.parse_regexp(requirement)
|
||||
|
||||
if expression.is_a?(Regin::Expression) && expression.anchored_to_line?
|
||||
expression = Regin::Expression.new(expression.reject { |e| e.is_a?(Regin::Anchor) })
|
||||
return requirements[method] = expression.to_s if expression.literal?
|
||||
end
|
||||
end
|
||||
|
||||
requirements[method] = requirement
|
||||
end
|
||||
|
||||
def report
|
||||
@report ||= begin
|
||||
possible_keys.each { |keys| keys.each_pair { |key, _| @key_frequency << key } }
|
||||
return [] if @key_frequency.count <= 1
|
||||
@key_frequency.keys_in_upper_quartile
|
||||
end
|
||||
end
|
||||
|
||||
def expire!
|
||||
@possible_keys = @report = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
74
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb
vendored
Normal file
74
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
module Rack::Mount
|
||||
module Analysis
|
||||
class Histogram < Hash #:nodoc:
|
||||
attr_reader :count
|
||||
|
||||
def initialize
|
||||
@count = 0
|
||||
super(0)
|
||||
expire_caches!
|
||||
end
|
||||
|
||||
def <<(value)
|
||||
@count += 1
|
||||
self[value] += 1 if value
|
||||
expire_caches!
|
||||
self
|
||||
end
|
||||
|
||||
def sorted_by_frequency
|
||||
sort_by { |_, value| value }.reverse!
|
||||
end
|
||||
|
||||
def max
|
||||
@max ||= values.max || 0
|
||||
end
|
||||
|
||||
def min
|
||||
@min ||= values.min || 0
|
||||
end
|
||||
|
||||
def mean
|
||||
@mean ||= calculate_mean
|
||||
end
|
||||
|
||||
def standard_deviation
|
||||
@standard_deviation ||= calculate_standard_deviation
|
||||
end
|
||||
|
||||
def upper_quartile_limit
|
||||
@upper_quartile_limit ||= calculate_upper_quartile_limit
|
||||
end
|
||||
|
||||
def keys_in_upper_quartile
|
||||
@keys_in_upper_quartile ||= compute_keys_in_upper_quartile
|
||||
end
|
||||
|
||||
private
|
||||
def calculate_mean
|
||||
count / size
|
||||
end
|
||||
|
||||
def calculate_variance
|
||||
values.inject(0) { |sum, e| sum + (e - mean) ** 2 } / count.to_f
|
||||
end
|
||||
|
||||
def calculate_standard_deviation
|
||||
Math.sqrt(calculate_variance)
|
||||
end
|
||||
|
||||
def calculate_upper_quartile_limit
|
||||
mean + standard_deviation
|
||||
end
|
||||
|
||||
def compute_keys_in_upper_quartile
|
||||
sorted_by_frequency.select { |_, value| value >= upper_quartile_limit }.map! { |key, _| key }
|
||||
end
|
||||
|
||||
def expire_caches!
|
||||
@max = @min = @mean = @standard_deviation = nil
|
||||
@keys_in_upper_quartile = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
159
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb
vendored
Normal file
159
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
require 'rack/mount/utils'
|
||||
|
||||
module Rack::Mount
|
||||
module Analysis
|
||||
class Splitting < Frequency
|
||||
NULL = "\0".freeze
|
||||
|
||||
class Key < Struct.new(:method, :index, :separators)
|
||||
def self.split(value, separator_pattern)
|
||||
keys = value.split(separator_pattern)
|
||||
keys.shift if keys[0] == ''
|
||||
keys << NULL
|
||||
keys
|
||||
end
|
||||
|
||||
def call(cache, obj)
|
||||
(cache[method] ||= self.class.split(obj.send(method), separators))[index]
|
||||
end
|
||||
|
||||
def call_source(cache, obj)
|
||||
"(#{cache}[:#{method}] ||= Analysis::Splitting::Key.split(#{obj}.#{method}, #{separators.inspect}))[#{index}]"
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#{method}[#{index}]"
|
||||
end
|
||||
end
|
||||
|
||||
def clear
|
||||
@boundaries = {}
|
||||
super
|
||||
end
|
||||
|
||||
def <<(key)
|
||||
super
|
||||
key.each_pair do |k, v|
|
||||
analyze_capture_boundaries(v, @boundaries[k] ||= Histogram.new)
|
||||
end
|
||||
end
|
||||
|
||||
def separators(key)
|
||||
(@boundaries[key].keys_in_upper_quartile + ['/']).uniq
|
||||
end
|
||||
|
||||
def process_key(requirements, method, requirement)
|
||||
separators = separators(method)
|
||||
if requirement.is_a?(Regexp) && separators.any?
|
||||
generate_split_keys(requirement, separators).each_with_index do |value, index|
|
||||
requirements[Key.new(method, index, Regexp.union(*separators))] = value
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def analyze_capture_boundaries(regexp, boundaries) #:nodoc:
|
||||
return boundaries unless regexp.is_a?(Regexp)
|
||||
|
||||
parts = Utils.parse_regexp(regexp)
|
||||
parts.each_with_index do |part, index|
|
||||
if part.is_a?(Regin::Group)
|
||||
if index > 0
|
||||
previous = parts[index-1]
|
||||
if previous.is_a?(Regin::Character) && previous.literal?
|
||||
boundaries << previous.to_s
|
||||
end
|
||||
end
|
||||
|
||||
if inside = part.expression[0]
|
||||
if inside.is_a?(Regin::Character) && inside.literal?
|
||||
boundaries << inside.to_s
|
||||
end
|
||||
end
|
||||
|
||||
if index < parts.length
|
||||
following = parts[index+1]
|
||||
if following.is_a?(Regin::Character) && following.literal?
|
||||
boundaries << following.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
boundaries
|
||||
end
|
||||
|
||||
def generate_split_keys(regexp, separators) #:nodoc:
|
||||
segments = []
|
||||
buf = nil
|
||||
parts = Utils.parse_regexp(regexp)
|
||||
parts.each_with_index do |part, index|
|
||||
case part
|
||||
when Regin::Anchor
|
||||
if part.value == '$' || part.value == '\Z'
|
||||
segments << join_buffer(buf, regexp) if buf
|
||||
segments << NULL
|
||||
buf = nil
|
||||
break
|
||||
end
|
||||
when Regin::CharacterClass
|
||||
break if separators.any? { |s| part.include?(s) }
|
||||
buf = nil
|
||||
segments << part.to_regexp(true)
|
||||
when Regin::Character
|
||||
if separators.any? { |s| part.include?(s) }
|
||||
segments << join_buffer(buf, regexp) if buf
|
||||
peek = parts[index+1]
|
||||
if peek.is_a?(Regin::Character) && separators.include?(peek.value)
|
||||
segments << ''
|
||||
end
|
||||
buf = nil
|
||||
else
|
||||
buf ||= Regin::Expression.new([])
|
||||
buf += [part]
|
||||
end
|
||||
when Regin::Group
|
||||
if part.quantifier == '?'
|
||||
value = part.expression.first
|
||||
if separators.any? { |s| value.include?(s) }
|
||||
segments << join_buffer(buf, regexp) if buf
|
||||
buf = nil
|
||||
end
|
||||
break
|
||||
elsif part.quantifier == nil
|
||||
break if separators.any? { |s| part.include?(s) }
|
||||
buf = nil
|
||||
segments << part.to_regexp(true)
|
||||
else
|
||||
break
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
|
||||
if index + 1 == parts.size
|
||||
segments << join_buffer(buf, regexp) if buf
|
||||
buf = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
while segments.length > 0 && (segments.last.nil? || segments.last == '')
|
||||
segments.pop
|
||||
end
|
||||
|
||||
segments
|
||||
end
|
||||
|
||||
def join_buffer(parts, regexp)
|
||||
if parts.literal?
|
||||
parts.to_s
|
||||
else
|
||||
parts.to_regexp(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
113
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb
vendored
Normal file
113
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
module Rack::Mount
|
||||
module CodeGeneration #:nodoc:
|
||||
def _expired_recognize(env) #:nodoc:
|
||||
raise 'route set not finalized'
|
||||
end
|
||||
|
||||
def rehash
|
||||
super
|
||||
optimize_recognize!
|
||||
end
|
||||
|
||||
private
|
||||
def expire!
|
||||
if @optimized_recognize_defined
|
||||
remove_metaclass_method :recognize
|
||||
|
||||
class << self
|
||||
alias_method :recognize, :_expired_recognize
|
||||
end
|
||||
|
||||
@optimized_recognize_defined = false
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def optimize_container_iterator(container)
|
||||
body = []
|
||||
|
||||
container.each_with_index { |route, i|
|
||||
body << "route = self[#{i}]"
|
||||
body << 'matches = {}'
|
||||
body << 'params = route.defaults.dup'
|
||||
|
||||
conditions = []
|
||||
route.conditions.each do |method, condition|
|
||||
b = []
|
||||
if condition.is_a?(Regexp)
|
||||
b << "if m = obj.#{method}.match(#{condition.inspect})"
|
||||
b << "matches[:#{method}] = m"
|
||||
if (named_captures = route.named_captures[method]) && named_captures.any?
|
||||
b << 'captures = m.captures'
|
||||
b << 'p = nil'
|
||||
b << named_captures.map { |k, j| "params[#{k.inspect}] = p if p = captures[#{j}]" }.join('; ')
|
||||
end
|
||||
else
|
||||
b << "if m = obj.#{method} == route.conditions[:#{method}]"
|
||||
end
|
||||
b << 'true'
|
||||
b << 'end'
|
||||
conditions << "(#{b.join('; ')})"
|
||||
end
|
||||
|
||||
body << <<-RUBY
|
||||
if #{conditions.join(' && ')}
|
||||
yield route, matches, params
|
||||
end
|
||||
RUBY
|
||||
}
|
||||
|
||||
container.instance_eval(<<-RUBY, __FILE__, __LINE__)
|
||||
def optimized_each(obj)
|
||||
#{body.join("\n")}
|
||||
nil
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
def optimize_recognize!
|
||||
keys = @recognition_keys.map { |key|
|
||||
if key.respond_to?(:call_source)
|
||||
key.call_source(:cache, :obj)
|
||||
else
|
||||
"obj.#{key}"
|
||||
end
|
||||
}.join(', ')
|
||||
|
||||
@optimized_recognize_defined = true
|
||||
|
||||
remove_metaclass_method :recognize
|
||||
|
||||
instance_eval(<<-RUBY, __FILE__, __LINE__)
|
||||
def recognize(obj)
|
||||
cache = {}
|
||||
container = @recognition_graph[#{keys}]
|
||||
optimize_container_iterator(container) unless container.respond_to?(:optimized_each)
|
||||
|
||||
if block_given?
|
||||
container.optimized_each(obj) do |route, matches, params|
|
||||
yield route, matches, params
|
||||
end
|
||||
else
|
||||
container.optimized_each(obj) do |route, matches, params|
|
||||
return route, matches, params
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
# method_defined? can't distinguish between instance
|
||||
# and meta methods. So we have to rescue if the method
|
||||
# has not been defined in the metaclass yet.
|
||||
def remove_metaclass_method(symbol)
|
||||
metaclass = class << self; self; end
|
||||
metaclass.send(:remove_method, symbol)
|
||||
rescue NameError => e
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
210
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb
vendored
Normal file
210
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb
vendored
Normal file
|
@ -0,0 +1,210 @@
|
|||
require 'rack/mount/utils'
|
||||
|
||||
module Rack::Mount
|
||||
class GeneratableRegexp < Regexp #:nodoc:
|
||||
class DynamicSegment #:nodoc:
|
||||
attr_reader :name, :requirement
|
||||
|
||||
def initialize(name, requirement)
|
||||
@name, @requirement = name.to_sym, requirement
|
||||
freeze
|
||||
end
|
||||
|
||||
def ==(obj)
|
||||
@name == obj.name && @requirement == obj.requirement
|
||||
end
|
||||
|
||||
def =~(str)
|
||||
@requirement =~ str
|
||||
end
|
||||
|
||||
def to_hash
|
||||
{ @name => @requirement }
|
||||
end
|
||||
|
||||
def inspect
|
||||
"/(?<#{@name}>#{@requirement.source})/"
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def self.extended(obj)
|
||||
obj.segments
|
||||
end
|
||||
|
||||
def defaults=(defaults)
|
||||
@required_captures = nil
|
||||
@required_params = nil
|
||||
@required_defaults = nil
|
||||
@defaults = defaults
|
||||
end
|
||||
|
||||
def defaults
|
||||
@defaults ||= {}
|
||||
end
|
||||
|
||||
def generatable?
|
||||
segments.any?
|
||||
end
|
||||
|
||||
def generate(params = {}, recall = {}, options = {})
|
||||
return nil unless generatable?
|
||||
|
||||
merged = recall.merge(params)
|
||||
return nil unless required_params.all? { |p| merged.include?(p) }
|
||||
return nil unless required_defaults.all? { |k, v| merged[k] == v }
|
||||
|
||||
generate_from_segments(segments, params, merged, options)
|
||||
end
|
||||
|
||||
def segments
|
||||
@segments ||= begin
|
||||
defaults
|
||||
segments = []
|
||||
catch(:halt) do
|
||||
expression = Utils.parse_regexp(self)
|
||||
segments = parse_segments(expression)
|
||||
end
|
||||
segments
|
||||
end
|
||||
end
|
||||
|
||||
def captures
|
||||
segments.flatten.find_all { |s| s.is_a?(DynamicSegment) }
|
||||
end
|
||||
|
||||
def required_captures
|
||||
@required_captures ||= segments.find_all { |s|
|
||||
s.is_a?(DynamicSegment) && !@defaults.include?(s.name)
|
||||
}.freeze
|
||||
end
|
||||
|
||||
def required_params
|
||||
@required_params ||= required_captures.map { |s| s.name }.freeze
|
||||
end
|
||||
|
||||
def required_defaults
|
||||
@required_defaults ||= begin
|
||||
required_defaults = @defaults.dup
|
||||
captures.inject({}) { |h, s| h.merge!(s.to_hash) }.keys.each { |name|
|
||||
required_defaults.delete(name)
|
||||
}
|
||||
required_defaults
|
||||
end
|
||||
end
|
||||
|
||||
def freeze
|
||||
segments
|
||||
captures
|
||||
required_captures
|
||||
required_params
|
||||
required_defaults
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
def parse_segments(segments)
|
||||
s = []
|
||||
segments.each_with_index do |part, index|
|
||||
case part
|
||||
when Regin::Anchor
|
||||
# ignore
|
||||
when Regin::Character
|
||||
throw :halt unless part.literal?
|
||||
|
||||
if s.last.is_a?(String)
|
||||
s.last << part.value.dup
|
||||
else
|
||||
s << part.value.dup
|
||||
end
|
||||
when Regin::Group
|
||||
if part.name
|
||||
s << DynamicSegment.new(part.name, part.expression.to_regexp(true))
|
||||
else
|
||||
s << parse_segments(part.expression)
|
||||
end
|
||||
when Regin::Expression
|
||||
return parse_segments(part)
|
||||
else
|
||||
throw :halt
|
||||
end
|
||||
end
|
||||
|
||||
s
|
||||
end
|
||||
|
||||
EMPTY_STRING = ''.freeze
|
||||
|
||||
def generate_from_segments(segments, params, merged, options, optional = false)
|
||||
if optional
|
||||
return EMPTY_STRING if segments.all? { |s| s.is_a?(String) }
|
||||
return EMPTY_STRING unless segments.flatten.any? { |s|
|
||||
params.has_key?(s.name) if s.is_a?(DynamicSegment)
|
||||
}
|
||||
return EMPTY_STRING if segments.any? { |segment|
|
||||
if segment.is_a?(DynamicSegment)
|
||||
value = merged[segment.name] || @defaults[segment.name]
|
||||
value = parameterize(segment.name, value, options)
|
||||
|
||||
merged_value = parameterize(segment.name, merged[segment.name], options)
|
||||
default_value = parameterize(segment.name, @defaults[segment.name], options)
|
||||
|
||||
if value.nil? || segment !~ value
|
||||
true
|
||||
elsif merged_value == default_value
|
||||
# Nasty control flow
|
||||
return :clear_remaining_segments
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
generated = segments.map do |segment|
|
||||
case segment
|
||||
when String
|
||||
segment
|
||||
when DynamicSegment
|
||||
value = params[segment.name] || merged[segment.name] || @defaults[segment.name]
|
||||
value = parameterize(segment.name, value, options)
|
||||
if value && segment =~ value.to_s
|
||||
value
|
||||
else
|
||||
return
|
||||
end
|
||||
when Array
|
||||
value = generate_from_segments(segment, params, merged, options, true)
|
||||
if value == :clear_remaining_segments
|
||||
segment.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) }
|
||||
EMPTY_STRING
|
||||
elsif value.nil?
|
||||
EMPTY_STRING
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Delete any used items from the params
|
||||
segments.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) }
|
||||
|
||||
generated.join
|
||||
end
|
||||
|
||||
def parameterize(name, value, options)
|
||||
if block = options[:parameterize]
|
||||
block.call(name, value)
|
||||
else
|
||||
value
|
||||
end
|
||||
end
|
||||
end
|
||||
include InstanceMethods
|
||||
|
||||
def initialize(regexp)
|
||||
super
|
||||
segments
|
||||
end
|
||||
end
|
||||
end
|
53
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb
vendored
Normal file
53
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
begin
|
||||
require 'nested_multimap'
|
||||
rescue LoadError
|
||||
$: << File.expand_path(File.join(File.dirname(__FILE__), 'vendor/multimap'))
|
||||
require 'nested_multimap'
|
||||
end
|
||||
|
||||
module Rack::Mount
|
||||
class Multimap < NestedMultimap #:nodoc:
|
||||
def store(*args)
|
||||
keys = args.dup
|
||||
value = keys.pop
|
||||
key = keys.shift
|
||||
|
||||
raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
|
||||
|
||||
unless key.respond_to?(:=~)
|
||||
raise ArgumentError, "unsupported key: #{args.first.inspect}"
|
||||
end
|
||||
|
||||
if key.is_a?(Regexp)
|
||||
if keys.empty?
|
||||
@hash.each_pair { |k, l| l << value if k =~ key }
|
||||
self.default << value
|
||||
else
|
||||
@hash.each_pair { |k, _|
|
||||
if k =~ key
|
||||
args[0] = k
|
||||
super(*args)
|
||||
end
|
||||
}
|
||||
|
||||
self.default = self.class.new(default) unless default.is_a?(self.class)
|
||||
default[*keys.dup] = value
|
||||
end
|
||||
else
|
||||
super(*args)
|
||||
end
|
||||
end
|
||||
alias_method :[]=, :store
|
||||
|
||||
undef :index, :invert
|
||||
|
||||
def height
|
||||
containers_with_default.max { |a, b| a.length <=> b.length }.length
|
||||
end
|
||||
|
||||
def average_height
|
||||
lengths = containers_with_default.map { |e| e.length }
|
||||
lengths.inject(0) { |sum, len| sum += len }.to_f / lengths.size
|
||||
end
|
||||
end
|
||||
end
|
36
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb
vendored
Normal file
36
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
require 'rack/mount/utils'
|
||||
|
||||
module Rack::Mount
|
||||
class Prefix #:nodoc:
|
||||
EMPTY_STRING = ''.freeze
|
||||
PATH_INFO = 'PATH_INFO'.freeze
|
||||
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
||||
SLASH = '/'.freeze
|
||||
|
||||
KEY = 'rack.mount.prefix'.freeze
|
||||
|
||||
def initialize(app, prefix = nil)
|
||||
@app, @prefix = app, prefix.freeze
|
||||
freeze
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if prefix = env[KEY] || @prefix
|
||||
old_path_info = env[PATH_INFO].dup
|
||||
old_script_name = env[SCRIPT_NAME].dup
|
||||
|
||||
begin
|
||||
env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO].sub(prefix, EMPTY_STRING))
|
||||
env[PATH_INFO] = EMPTY_STRING if env[PATH_INFO] == SLASH
|
||||
env[SCRIPT_NAME] = Utils.normalize_path(env[SCRIPT_NAME].to_s + prefix)
|
||||
@app.call(env)
|
||||
ensure
|
||||
env[PATH_INFO] = old_path_info
|
||||
env[SCRIPT_NAME] = old_script_name
|
||||
end
|
||||
else
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,69 @@
|
|||
module Rack::Mount
|
||||
if Regin.regexp_supports_named_captures?
|
||||
RegexpWithNamedGroups = Regexp
|
||||
else
|
||||
require 'strscan'
|
||||
|
||||
# A wrapper that adds shim named capture support to older
|
||||
# versions of Ruby.
|
||||
#
|
||||
# Because the named capture syntax causes a parse error, an
|
||||
# alternate syntax is used to indicate named captures.
|
||||
#
|
||||
# Ruby 1.9+ named capture syntax:
|
||||
#
|
||||
# /(?<foo>[a-z]+)/
|
||||
#
|
||||
# Ruby 1.8 shim syntax:
|
||||
#
|
||||
# /(?:<foo>[a-z]+)/
|
||||
class RegexpWithNamedGroups < Regexp
|
||||
def self.new(regexp) #:nodoc:
|
||||
if regexp.is_a?(RegexpWithNamedGroups)
|
||||
regexp
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# Wraps Regexp with named capture support.
|
||||
def initialize(regexp)
|
||||
regexp = Regexp.compile(regexp) unless regexp.is_a?(Regexp)
|
||||
source, options = regexp.source, regexp.options
|
||||
@names, scanner = [], StringScanner.new(source)
|
||||
|
||||
while scanner.skip_until(/\(/)
|
||||
if scanner.scan(/\?:<([^>]+)>/)
|
||||
@names << scanner[1]
|
||||
elsif scanner.scan(/\?(i?m?x?\-?i?m?x?)?:/)
|
||||
# ignore noncapture
|
||||
else
|
||||
@names << nil
|
||||
end
|
||||
end
|
||||
source.gsub!(/\?:<([^>]+)>/, '')
|
||||
|
||||
@names = [] unless @names.any?
|
||||
@names.freeze
|
||||
|
||||
super(source, options)
|
||||
end
|
||||
|
||||
def names
|
||||
@names.dup
|
||||
end
|
||||
|
||||
def named_captures
|
||||
named_captures = {}
|
||||
names.each_with_index { |n, i|
|
||||
named_captures[n] = [i+1] if n
|
||||
}
|
||||
named_captures
|
||||
end
|
||||
|
||||
def eql?(other)
|
||||
super && @names.eql?(other.names)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
130
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb
vendored
Normal file
130
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
require 'rack/mount/generatable_regexp'
|
||||
require 'rack/mount/regexp_with_named_groups'
|
||||
require 'rack/mount/utils'
|
||||
|
||||
module Rack::Mount
|
||||
# Route is an internal class used to wrap a single route attributes.
|
||||
#
|
||||
# Plugins should not depend on any method on this class or instantiate
|
||||
# new Route objects. Instead use the factory method, RouteSet#add_route
|
||||
# to create new routes and add them to the set.
|
||||
class Route
|
||||
# Valid rack application to call if conditions are met
|
||||
attr_reader :app
|
||||
|
||||
# A hash of conditions to match against. Conditions may be expressed
|
||||
# as strings or regexps to match against.
|
||||
attr_reader :conditions
|
||||
|
||||
# A hash of values that always gets merged into the parameters hash
|
||||
attr_reader :defaults
|
||||
|
||||
# Symbol identifier for the route used with named route generations
|
||||
attr_reader :name
|
||||
|
||||
attr_reader :named_captures
|
||||
|
||||
def initialize(app, conditions, defaults, name)
|
||||
unless app.respond_to?(:call)
|
||||
raise ArgumentError, 'app must be a valid rack application' \
|
||||
' and respond to call'
|
||||
end
|
||||
@app = app
|
||||
|
||||
@name = name ? name.to_sym : nil
|
||||
@defaults = (defaults || {}).freeze
|
||||
|
||||
@conditions = {}
|
||||
|
||||
conditions.each do |method, pattern|
|
||||
next unless method && pattern
|
||||
|
||||
pattern = Regexp.compile("\\A#{Regexp.escape(pattern)}\\Z") if pattern.is_a?(String)
|
||||
|
||||
if pattern.is_a?(Regexp)
|
||||
pattern = Utils.normalize_extended_expression(pattern)
|
||||
pattern = RegexpWithNamedGroups.new(pattern)
|
||||
pattern.extend(GeneratableRegexp::InstanceMethods)
|
||||
pattern.defaults = @defaults
|
||||
end
|
||||
|
||||
@conditions[method] = pattern.freeze
|
||||
end
|
||||
|
||||
@named_captures = {}
|
||||
@conditions.map { |method, condition|
|
||||
next unless condition.respond_to?(:named_captures)
|
||||
@named_captures[method] = condition.named_captures.inject({}) { |named_captures, (k, v)|
|
||||
named_captures[k.to_sym] = v.last - 1
|
||||
named_captures
|
||||
}.freeze
|
||||
}
|
||||
@named_captures.freeze
|
||||
|
||||
@has_significant_params = @conditions.any? { |method, condition|
|
||||
(condition.respond_to?(:required_params) && condition.required_params.any?) ||
|
||||
(condition.respond_to?(:required_defaults) && condition.required_defaults.any?)
|
||||
}
|
||||
|
||||
if @conditions.has_key?(:path_info) &&
|
||||
!Utils.regexp_anchored?(@conditions[:path_info])
|
||||
@prefix = true
|
||||
@app = Prefix.new(@app)
|
||||
else
|
||||
@prefix = false
|
||||
end
|
||||
|
||||
@conditions.freeze
|
||||
end
|
||||
|
||||
def prefix?
|
||||
@prefix
|
||||
end
|
||||
|
||||
|
||||
def generation_keys
|
||||
@conditions.inject({}) { |keys, (method, condition)|
|
||||
if condition.respond_to?(:required_defaults)
|
||||
keys.merge!(condition.required_defaults)
|
||||
else
|
||||
keys
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def significant_params?
|
||||
@has_significant_params
|
||||
end
|
||||
|
||||
def generate(method, params = {}, recall = {}, options = {})
|
||||
if method.nil?
|
||||
result = @conditions.inject({}) { |h, (m, condition)|
|
||||
if condition.respond_to?(:generate)
|
||||
h[m] = condition.generate(params, recall, options)
|
||||
end
|
||||
h
|
||||
}
|
||||
return nil if result.values.compact.empty?
|
||||
else
|
||||
if condition = @conditions[method]
|
||||
if condition.respond_to?(:generate)
|
||||
result = condition.generate(params, recall, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if result
|
||||
@defaults.each do |key, value|
|
||||
params.delete(key) if params[key] == value
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
|
||||
def inspect #:nodoc:
|
||||
"#<#{self.class.name} @app=#{@app.inspect} @conditions=#{@conditions.inspect} @defaults=#{@defaults.inspect} @name=#{@name.inspect}>"
|
||||
end
|
||||
end
|
||||
end
|
409
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb
vendored
Normal file
409
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb
vendored
Normal file
|
@ -0,0 +1,409 @@
|
|||
require 'rack/mount/multimap'
|
||||
require 'rack/mount/route'
|
||||
require 'rack/mount/utils'
|
||||
|
||||
module Rack::Mount
|
||||
class RoutingError < StandardError; end
|
||||
|
||||
class RouteSet
|
||||
# Initialize a new RouteSet without optimizations
|
||||
def self.new_without_optimizations(options = {}, &block)
|
||||
new(options.merge(:_optimize => false), &block)
|
||||
end
|
||||
|
||||
# Basic RouteSet initializer.
|
||||
#
|
||||
# If a block is given, the set is yielded and finalized.
|
||||
#
|
||||
# See other aspects for other valid options:
|
||||
# - <tt>Generation::RouteSet.new</tt>
|
||||
# - <tt>Recognition::RouteSet.new</tt>
|
||||
def initialize(options = {}, &block)
|
||||
@parameters_key = options.delete(:parameters_key) || 'rack.routing_args'
|
||||
@parameters_key.freeze
|
||||
|
||||
@named_routes = {}
|
||||
|
||||
@recognition_key_analyzer = Analysis::Splitting.new
|
||||
@generation_key_analyzer = Analysis::Frequency.new
|
||||
|
||||
@request_class = options.delete(:request_class) || Rack::Request
|
||||
@valid_conditions = @request_class.public_instance_methods.map! { |m| m.to_sym }
|
||||
|
||||
extend CodeGeneration unless options[:_optimize] == false
|
||||
@optimized_recognize_defined = false
|
||||
|
||||
@routes = []
|
||||
expire!
|
||||
|
||||
if block_given?
|
||||
yield self
|
||||
rehash
|
||||
end
|
||||
end
|
||||
|
||||
# Builder method to add a route to the set
|
||||
#
|
||||
# <tt>app</tt>:: A valid Rack app to call if the conditions are met.
|
||||
# <tt>conditions</tt>:: A hash of conditions to match against.
|
||||
# Conditions may be expressed as strings or
|
||||
# regexps to match against.
|
||||
# <tt>defaults</tt>:: A hash of values that always gets merged in
|
||||
# <tt>name</tt>:: Symbol identifier for the route used with named
|
||||
# route generations
|
||||
def add_route(app, conditions = {}, defaults = {}, name = nil)
|
||||
unless conditions.is_a?(Hash)
|
||||
raise ArgumentError, 'conditions must be a Hash'
|
||||
end
|
||||
|
||||
unless conditions.all? { |method, pattern|
|
||||
@valid_conditions.include?(method)
|
||||
}
|
||||
raise ArgumentError, 'conditions may only include ' +
|
||||
@valid_conditions.inspect
|
||||
end
|
||||
|
||||
route = Route.new(app, conditions, defaults, name)
|
||||
@routes << route
|
||||
|
||||
@recognition_key_analyzer << route.conditions
|
||||
|
||||
@named_routes[route.name] = route if route.name
|
||||
@generation_key_analyzer << route.generation_keys
|
||||
|
||||
expire!
|
||||
route
|
||||
end
|
||||
|
||||
def recognize(obj)
|
||||
raise 'route set not finalized' unless @recognition_graph
|
||||
|
||||
cache = {}
|
||||
keys = @recognition_keys.map { |key|
|
||||
if key.respond_to?(:call)
|
||||
key.call(cache, obj)
|
||||
else
|
||||
obj.send(key)
|
||||
end
|
||||
}
|
||||
|
||||
@recognition_graph[*keys].each do |route|
|
||||
matches = {}
|
||||
params = route.defaults.dup
|
||||
|
||||
if route.conditions.all? { |method, condition|
|
||||
value = obj.send(method)
|
||||
if condition.is_a?(Regexp) && (m = value.match(condition))
|
||||
matches[method] = m
|
||||
captures = m.captures
|
||||
route.named_captures[method].each do |k, i|
|
||||
if v = captures[i]
|
||||
params[k] = v
|
||||
end
|
||||
end
|
||||
true
|
||||
elsif value == condition
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
}
|
||||
if block_given?
|
||||
yield route, matches, params
|
||||
else
|
||||
return route, matches, params
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
X_CASCADE = 'X-Cascade'.freeze
|
||||
PASS = 'pass'.freeze
|
||||
PATH_INFO = 'PATH_INFO'.freeze
|
||||
|
||||
# Rack compatible recognition and dispatching method. Routes are
|
||||
# tried until one returns a non-catch status code. If no routes
|
||||
# match, the catch status code is returned.
|
||||
#
|
||||
# This method can only be invoked after the RouteSet has been
|
||||
# finalized.
|
||||
def call(env)
|
||||
raise 'route set not finalized' unless @recognition_graph
|
||||
|
||||
env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO])
|
||||
|
||||
request = nil
|
||||
req = @request_class.new(env)
|
||||
recognize(req) do |route, matches, params|
|
||||
# TODO: We only want to unescape params from uri related methods
|
||||
params.each { |k, v| params[k] = Utils.unescape_uri(v) if v.is_a?(String) }
|
||||
|
||||
if route.prefix?
|
||||
env[Prefix::KEY] = matches[:path_info].to_s
|
||||
end
|
||||
|
||||
env[@parameters_key] = params
|
||||
result = route.app.call(env)
|
||||
return result unless result[1][X_CASCADE] == PASS
|
||||
end
|
||||
|
||||
request || [404, {'Content-Type' => 'text/html', 'X-Cascade' => 'pass'}, ['Not Found']]
|
||||
end
|
||||
|
||||
# Generates a url from Rack env and identifiers or significant keys.
|
||||
#
|
||||
# To generate a url by named route, pass the name in as a +Symbol+.
|
||||
# url(env, :dashboard) # => "/dashboard"
|
||||
#
|
||||
# Additional parameters can be passed in as a hash
|
||||
# url(env, :people, :id => "1") # => "/people/1"
|
||||
#
|
||||
# If no name route is given, it will fall back to a slower
|
||||
# generation search.
|
||||
# url(env, :controller => "people", :action => "show", :id => "1")
|
||||
# # => "/people/1"
|
||||
def url(env, *args)
|
||||
named_route, params = nil, {}
|
||||
|
||||
case args.length
|
||||
when 2
|
||||
named_route, params = args[0], args[1].dup
|
||||
when 1
|
||||
if args[0].is_a?(Hash)
|
||||
params = args[0].dup
|
||||
else
|
||||
named_route = args[0]
|
||||
end
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
|
||||
only_path = params.delete(:only_path)
|
||||
recall = env[@parameters_key] || {}
|
||||
|
||||
unless result = generate(:all, named_route, params, recall,
|
||||
:parameterize => lambda { |name, param| Utils.escape_uri(param) })
|
||||
return
|
||||
end
|
||||
|
||||
parts, params = result
|
||||
return unless parts
|
||||
|
||||
params.each do |k, v|
|
||||
if v
|
||||
params[k] = v
|
||||
else
|
||||
params.delete(k)
|
||||
end
|
||||
end
|
||||
|
||||
req = stubbed_request_class.new(env)
|
||||
req._stubbed_values = parts.merge(:query_string => Utils.build_nested_query(params))
|
||||
only_path ? req.fullpath : req.url
|
||||
end
|
||||
|
||||
def generate(method, *args) #:nodoc:
|
||||
raise 'route set not finalized' unless @generation_graph
|
||||
|
||||
method = nil if method == :all
|
||||
named_route, params, recall, options = extract_params!(*args)
|
||||
merged = recall.merge(params)
|
||||
route = nil
|
||||
|
||||
if named_route
|
||||
if route = @named_routes[named_route.to_sym]
|
||||
recall = route.defaults.merge(recall)
|
||||
url = route.generate(method, params, recall, options)
|
||||
[url, params]
|
||||
else
|
||||
raise RoutingError, "#{named_route} failed to generate from #{params.inspect}"
|
||||
end
|
||||
else
|
||||
keys = @generation_keys.map { |key|
|
||||
if k = merged[key]
|
||||
k.to_s
|
||||
else
|
||||
nil
|
||||
end
|
||||
}
|
||||
@generation_graph[*keys].each do |r|
|
||||
next unless r.significant_params?
|
||||
if url = r.generate(method, params, recall, options)
|
||||
return [url, params]
|
||||
end
|
||||
end
|
||||
|
||||
raise RoutingError, "No route matches #{params.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
# Number of routes in the set
|
||||
def length
|
||||
@routes.length
|
||||
end
|
||||
|
||||
def rehash #:nodoc:
|
||||
@recognition_keys = build_recognition_keys
|
||||
@recognition_graph = build_recognition_graph
|
||||
@generation_keys = build_generation_keys
|
||||
@generation_graph = build_generation_graph
|
||||
end
|
||||
|
||||
# Finalizes the set and builds optimized data structures. You *must*
|
||||
# freeze the set before you can use <tt>call</tt> and <tt>url</tt>.
|
||||
# So remember to call freeze after you are done adding routes.
|
||||
def freeze
|
||||
unless frozen?
|
||||
rehash
|
||||
|
||||
@recognition_key_analyzer = nil
|
||||
@generation_key_analyzer = nil
|
||||
@valid_conditions = nil
|
||||
|
||||
@routes.each { |route| route.freeze }
|
||||
@routes.freeze
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def marshal_dump #:nodoc:
|
||||
hash = {}
|
||||
|
||||
instance_variables_to_serialize.each do |ivar|
|
||||
hash[ivar] = instance_variable_get(ivar)
|
||||
end
|
||||
|
||||
if graph = hash[:@recognition_graph]
|
||||
hash[:@recognition_graph] = graph.dup
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
|
||||
def marshal_load(hash) #:nodoc:
|
||||
hash.each do |ivar, value|
|
||||
instance_variable_set(ivar, value)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def recognition_stats
|
||||
{ :keys => @recognition_keys,
|
||||
:keys_size => @recognition_keys.size,
|
||||
:graph_size => @recognition_graph.size,
|
||||
:graph_height => @recognition_graph.height,
|
||||
:graph_average_height => @recognition_graph.average_height }
|
||||
end
|
||||
|
||||
private
|
||||
def expire! #:nodoc:
|
||||
@recognition_keys = @recognition_graph = nil
|
||||
@recognition_key_analyzer.expire!
|
||||
|
||||
@generation_keys = @generation_graph = nil
|
||||
@generation_key_analyzer.expire!
|
||||
end
|
||||
|
||||
def instance_variables_to_serialize
|
||||
instance_variables.map { |ivar| ivar.to_sym } - [:@stubbed_request_class, :@optimized_recognize_defined]
|
||||
end
|
||||
|
||||
# An internal helper method for constructing a nested set from
|
||||
# the linear route set.
|
||||
#
|
||||
# build_nested_route_set([:request_method, :path_info]) { |route, method|
|
||||
# route.send(method)
|
||||
# }
|
||||
def build_nested_route_set(keys, &block)
|
||||
graph = Multimap.new
|
||||
@routes.each_with_index do |route, index|
|
||||
catch(:skip) do
|
||||
k = keys.map { |key| block.call(key, index) }
|
||||
Utils.pop_trailing_nils!(k)
|
||||
k.map! { |key| key || /.+/ }
|
||||
graph[*k] = route
|
||||
end
|
||||
end
|
||||
graph
|
||||
end
|
||||
|
||||
def build_recognition_graph
|
||||
build_nested_route_set(@recognition_keys) { |k, i|
|
||||
@recognition_key_analyzer.possible_keys[i][k]
|
||||
}
|
||||
end
|
||||
|
||||
def build_recognition_keys
|
||||
@recognition_key_analyzer.report
|
||||
end
|
||||
|
||||
def build_generation_graph
|
||||
build_nested_route_set(@generation_keys) { |k, i|
|
||||
throw :skip unless @routes[i].significant_params?
|
||||
|
||||
if k = @generation_key_analyzer.possible_keys[i][k]
|
||||
k.to_s
|
||||
else
|
||||
nil
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def build_generation_keys
|
||||
@generation_key_analyzer.report
|
||||
end
|
||||
|
||||
def extract_params!(*args)
|
||||
case args.length
|
||||
when 4
|
||||
named_route, params, recall, options = args
|
||||
when 3
|
||||
if args[0].is_a?(Hash)
|
||||
params, recall, options = args
|
||||
else
|
||||
named_route, params, recall = args
|
||||
end
|
||||
when 2
|
||||
if args[0].is_a?(Hash)
|
||||
params, recall = args
|
||||
else
|
||||
named_route, params = args
|
||||
end
|
||||
when 1
|
||||
if args[0].is_a?(Hash)
|
||||
params = args[0]
|
||||
else
|
||||
named_route = args[0]
|
||||
end
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
|
||||
named_route ||= nil
|
||||
params ||= {}
|
||||
recall ||= {}
|
||||
options ||= {}
|
||||
|
||||
[named_route, params.dup, recall.dup, options.dup]
|
||||
end
|
||||
|
||||
def stubbed_request_class
|
||||
@stubbed_request_class ||= begin
|
||||
klass = Class.new(@request_class)
|
||||
klass.public_instance_methods.each do |method|
|
||||
next if method =~ /^__|object_id/
|
||||
klass.class_eval <<-RUBY
|
||||
def #{method}(*args, &block)
|
||||
@_stubbed_values[:#{method}] || super
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
klass.class_eval { attr_accessor :_stubbed_values }
|
||||
klass
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
68
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb
vendored
Normal file
68
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
require 'rack/mount/strexp/parser'
|
||||
|
||||
module Rack::Mount
|
||||
class Strexp
|
||||
class << self
|
||||
# Parses segmented string expression and converts it into a Regexp
|
||||
#
|
||||
# Strexp.compile('foo')
|
||||
# # => %r{\Afoo\Z}
|
||||
#
|
||||
# Strexp.compile('foo/:bar', {}, ['/'])
|
||||
# # => %r{\Afoo/(?<bar>[^/]+)\Z}
|
||||
#
|
||||
# Strexp.compile(':foo.example.com')
|
||||
# # => %r{\A(?<foo>.+)\.example\.com\Z}
|
||||
#
|
||||
# Strexp.compile('foo/:bar', {:bar => /[a-z]+/}, ['/'])
|
||||
# # => %r{\Afoo/(?<bar>[a-z]+)\Z}
|
||||
#
|
||||
# Strexp.compile('foo(.:extension)')
|
||||
# # => %r{\Afoo(\.(?<extension>.+))?\Z}
|
||||
#
|
||||
# Strexp.compile('src/*files')
|
||||
# # => %r{\Asrc/(?<files>.+)\Z}
|
||||
def compile(str, requirements = {}, separators = [], anchor = true)
|
||||
return Regexp.compile(str) if str.is_a?(Regexp)
|
||||
|
||||
requirements = requirements ? requirements.dup : {}
|
||||
normalize_requirements!(requirements, separators)
|
||||
|
||||
parser = StrexpParser.new
|
||||
parser.anchor = anchor
|
||||
parser.requirements = requirements
|
||||
|
||||
begin
|
||||
re = parser.scan_str(str)
|
||||
rescue Racc::ParseError => e
|
||||
raise RegexpError, e.message
|
||||
end
|
||||
|
||||
Regexp.compile(re)
|
||||
end
|
||||
alias_method :new, :compile
|
||||
|
||||
private
|
||||
def normalize_requirements!(requirements, separators)
|
||||
requirements.each do |key, value|
|
||||
if value.is_a?(Regexp)
|
||||
if regexp_has_modifiers?(value)
|
||||
requirements[key] = value
|
||||
else
|
||||
requirements[key] = value.source
|
||||
end
|
||||
else
|
||||
requirements[key] = Regexp.escape(value)
|
||||
end
|
||||
end
|
||||
requirements.default ||= separators.any? ?
|
||||
"[^#{separators.join}]+" : '.+'
|
||||
requirements
|
||||
end
|
||||
|
||||
def regexp_has_modifiers?(regexp)
|
||||
regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
160
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb
vendored
Normal file
160
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
#
|
||||
# DO NOT MODIFY!!!!
|
||||
# This file is automatically generated by Racc 1.4.6
|
||||
# from Racc grammer file "".
|
||||
#
|
||||
|
||||
require 'racc/parser.rb'
|
||||
|
||||
require 'rack/mount/utils'
|
||||
require 'rack/mount/strexp/tokenizer'
|
||||
|
||||
module Rack
|
||||
module Mount
|
||||
class StrexpParser < Racc::Parser
|
||||
|
||||
|
||||
if Regin.regexp_supports_named_captures?
|
||||
REGEXP_NAMED_CAPTURE = '(?<%s>%s)'.freeze
|
||||
else
|
||||
REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze
|
||||
end
|
||||
|
||||
attr_accessor :anchor, :requirements
|
||||
##### State transition tables begin ###
|
||||
|
||||
racc_action_table = [
|
||||
1, 2, 3, 9, 4, 1, 2, 3, 12, 4,
|
||||
1, 2, 3, 11, 4, 1, 2, 3, nil, 4 ]
|
||||
|
||||
racc_action_check = [
|
||||
0, 0, 0, 5, 0, 3, 3, 3, 9, 3,
|
||||
8, 8, 8, 8, 8, 6, 6, 6, nil, 6 ]
|
||||
|
||||
racc_action_pointer = [
|
||||
-2, nil, nil, 3, nil, 3, 13, nil, 8, 8,
|
||||
nil, nil, nil ]
|
||||
|
||||
racc_action_default = [
|
||||
-8, -4, -5, -8, -7, -8, -1, -3, -8, -8,
|
||||
-2, -6, 13 ]
|
||||
|
||||
racc_goto_table = [
|
||||
6, 5, 10, 8, 10 ]
|
||||
|
||||
racc_goto_check = [
|
||||
2, 1, 3, 2, 3 ]
|
||||
|
||||
racc_goto_pointer = [
|
||||
nil, 1, 0, -4 ]
|
||||
|
||||
racc_goto_default = [
|
||||
nil, nil, nil, 7 ]
|
||||
|
||||
racc_reduce_table = [
|
||||
0, 0, :racc_error,
|
||||
1, 8, :_reduce_1,
|
||||
2, 9, :_reduce_2,
|
||||
1, 9, :_reduce_none,
|
||||
1, 10, :_reduce_4,
|
||||
1, 10, :_reduce_5,
|
||||
3, 10, :_reduce_6,
|
||||
1, 10, :_reduce_7 ]
|
||||
|
||||
racc_reduce_n = 8
|
||||
|
||||
racc_shift_n = 13
|
||||
|
||||
racc_token_table = {
|
||||
false => 0,
|
||||
:error => 1,
|
||||
:PARAM => 2,
|
||||
:GLOB => 3,
|
||||
:LPAREN => 4,
|
||||
:RPAREN => 5,
|
||||
:CHAR => 6 }
|
||||
|
||||
racc_nt_base = 7
|
||||
|
||||
racc_use_result_var = true
|
||||
|
||||
Racc_arg = [
|
||||
racc_action_table,
|
||||
racc_action_check,
|
||||
racc_action_default,
|
||||
racc_action_pointer,
|
||||
racc_goto_table,
|
||||
racc_goto_check,
|
||||
racc_goto_default,
|
||||
racc_goto_pointer,
|
||||
racc_nt_base,
|
||||
racc_reduce_table,
|
||||
racc_token_table,
|
||||
racc_shift_n,
|
||||
racc_reduce_n,
|
||||
racc_use_result_var ]
|
||||
|
||||
Racc_token_to_s_table = [
|
||||
"$end",
|
||||
"error",
|
||||
"PARAM",
|
||||
"GLOB",
|
||||
"LPAREN",
|
||||
"RPAREN",
|
||||
"CHAR",
|
||||
"$start",
|
||||
"target",
|
||||
"expr",
|
||||
"token" ]
|
||||
|
||||
Racc_debug_parser = false
|
||||
|
||||
##### State transition tables end #####
|
||||
|
||||
# reduce 0 omitted
|
||||
|
||||
def _reduce_1(val, _values, result)
|
||||
result = anchor ? "\\A#{val.join}\\Z" : "\\A#{val.join}"
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_2(val, _values, result)
|
||||
result = val.join
|
||||
result
|
||||
end
|
||||
|
||||
# reduce 3 omitted
|
||||
|
||||
def _reduce_4(val, _values, result)
|
||||
name = val[0].to_sym
|
||||
requirement = requirements[name]
|
||||
result = REGEXP_NAMED_CAPTURE % [name, requirement]
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_5(val, _values, result)
|
||||
name = val[0].to_sym
|
||||
requirement = requirements[name]
|
||||
result = REGEXP_NAMED_CAPTURE % [name, '.+' || requirement]
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_6(val, _values, result)
|
||||
result = "(?:#{val[1]})?"
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_7(val, _values, result)
|
||||
result = Regexp.escape(val[0])
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_none(val, _values, result)
|
||||
val[0]
|
||||
end
|
||||
|
||||
end # class StrexpParser
|
||||
end # module Mount
|
||||
end # module Rack
|
34
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y
vendored
Normal file
34
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
class Rack::Mount::StrexpParser
|
||||
rule
|
||||
target: expr { result = anchor ? "\\A#{val.join}\\Z" : "\\A#{val.join}" }
|
||||
|
||||
expr: expr token { result = val.join }
|
||||
| token
|
||||
|
||||
token: PARAM {
|
||||
name = val[0].to_sym
|
||||
requirement = requirements[name]
|
||||
result = REGEXP_NAMED_CAPTURE % [name, requirement]
|
||||
}
|
||||
| GLOB {
|
||||
name = val[0].to_sym
|
||||
requirement = requirements[name]
|
||||
result = REGEXP_NAMED_CAPTURE % [name, '.+' || requirement]
|
||||
}
|
||||
| LPAREN expr RPAREN { result = "(?:#{val[1]})?" }
|
||||
| CHAR { result = Regexp.escape(val[0]) }
|
||||
end
|
||||
|
||||
---- header ----
|
||||
require 'rack/mount/utils'
|
||||
require 'rack/mount/strexp/tokenizer'
|
||||
|
||||
---- inner
|
||||
|
||||
if Regin.regexp_supports_named_captures?
|
||||
REGEXP_NAMED_CAPTURE = '(?<%s>%s)'.freeze
|
||||
else
|
||||
REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze
|
||||
end
|
||||
|
||||
attr_accessor :anchor, :requirements
|
83
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb
vendored
Normal file
83
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
#--
|
||||
# DO NOT MODIFY!!!!
|
||||
# This file is automatically generated by rex 1.0.5.beta1
|
||||
# from lexical definition file "lib/rack/mount/strexp/tokenizer.rex".
|
||||
#++
|
||||
|
||||
require 'racc/parser'
|
||||
class Rack::Mount::StrexpParser < Racc::Parser
|
||||
require 'strscan'
|
||||
|
||||
class ScanError < StandardError ; end
|
||||
|
||||
attr_reader :lineno
|
||||
attr_reader :filename
|
||||
attr_accessor :state
|
||||
|
||||
def scan_setup(str)
|
||||
@ss = StringScanner.new(str)
|
||||
@lineno = 1
|
||||
@state = nil
|
||||
end
|
||||
|
||||
def action
|
||||
yield
|
||||
end
|
||||
|
||||
def scan_str(str)
|
||||
scan_setup(str)
|
||||
do_parse
|
||||
end
|
||||
alias :scan :scan_str
|
||||
|
||||
def load_file( filename )
|
||||
@filename = filename
|
||||
open(filename, "r") do |f|
|
||||
scan_setup(f.read)
|
||||
end
|
||||
end
|
||||
|
||||
def scan_file( filename )
|
||||
load_file(filename)
|
||||
do_parse
|
||||
end
|
||||
|
||||
|
||||
def next_token
|
||||
return if @ss.eos?
|
||||
|
||||
text = @ss.peek(1)
|
||||
@lineno += 1 if text == "\n"
|
||||
token = case @state
|
||||
when nil
|
||||
case
|
||||
when (text = @ss.scan(/\\(\(|\)|:|\*)/))
|
||||
action { [:CHAR, @ss[1]] }
|
||||
|
||||
when (text = @ss.scan(/\:([a-zA-Z_]\w*)/))
|
||||
action { [:PARAM, @ss[1]] }
|
||||
|
||||
when (text = @ss.scan(/\*([a-zA-Z_]\w*)/))
|
||||
action { [:GLOB, @ss[1]] }
|
||||
|
||||
when (text = @ss.scan(/\(/))
|
||||
action { [:LPAREN, text] }
|
||||
|
||||
when (text = @ss.scan(/\)/))
|
||||
action { [:RPAREN, text] }
|
||||
|
||||
when (text = @ss.scan(/./))
|
||||
action { [:CHAR, text] }
|
||||
|
||||
else
|
||||
text = @ss.string[@ss.pos .. -1]
|
||||
raise ScanError, "can not match: '" + text + "'"
|
||||
end # if
|
||||
|
||||
else
|
||||
raise ScanError, "undefined state: '" + state.to_s + "'"
|
||||
end # case state
|
||||
token
|
||||
end # def next_token
|
||||
|
||||
end # class
|
12
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex
vendored
Normal file
12
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
class Rack::Mount::StrexpParser
|
||||
macro
|
||||
RESERVED \(|\)|:|\*
|
||||
ALPHA_U [a-zA-Z_]
|
||||
rule
|
||||
\\({RESERVED}) { [:CHAR, @ss[1]] }
|
||||
\:({ALPHA_U}\w*) { [:PARAM, @ss[1]] }
|
||||
\*({ALPHA_U}\w*) { [:GLOB, @ss[1]] }
|
||||
\( { [:LPAREN, text] }
|
||||
\) { [:RPAREN, text] }
|
||||
. { [:CHAR, text] }
|
||||
end
|
148
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb
vendored
Normal file
148
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
begin
|
||||
require 'regin'
|
||||
rescue LoadError
|
||||
$: << File.expand_path(File.join(File.dirname(__FILE__), 'vendor/regin'))
|
||||
require 'regin'
|
||||
end
|
||||
|
||||
require 'uri'
|
||||
|
||||
module Rack::Mount
|
||||
# Private utility methods used throughout Rack::Mount.
|
||||
#--
|
||||
# This module is a trash can. Try to move these functions into
|
||||
# more appropriate contexts.
|
||||
#++
|
||||
module Utils
|
||||
# Normalizes URI path.
|
||||
#
|
||||
# Strips off trailing slash and ensures there is a leading slash.
|
||||
#
|
||||
# normalize_path("/foo") # => "/foo"
|
||||
# normalize_path("/foo/") # => "/foo"
|
||||
# normalize_path("foo") # => "/foo"
|
||||
# normalize_path("") # => "/"
|
||||
def normalize_path(path)
|
||||
path = "/#{path}"
|
||||
path.squeeze!('/')
|
||||
path.sub!(%r{/+\Z}, '')
|
||||
path = '/' if path == ''
|
||||
path
|
||||
end
|
||||
module_function :normalize_path
|
||||
|
||||
# Removes trailing nils from array.
|
||||
#
|
||||
# pop_trailing_nils!([1, 2, 3]) # => [1, 2, 3]
|
||||
# pop_trailing_nils!([1, 2, 3, nil, nil]) # => [1, 2, 3]
|
||||
# pop_trailing_nils!([nil]) # => []
|
||||
def pop_trailing_nils!(ary)
|
||||
while ary.length > 0 && ary.last.nil?
|
||||
ary.pop
|
||||
end
|
||||
ary
|
||||
end
|
||||
module_function :pop_trailing_nils!
|
||||
|
||||
RESERVED_PCHAR = ':@&=+$,;%'
|
||||
SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
|
||||
if RUBY_VERSION >= '1.9'
|
||||
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze
|
||||
else
|
||||
UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
|
||||
end
|
||||
|
||||
Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
||||
|
||||
def escape_uri(uri)
|
||||
Parser.escape(uri.to_s, UNSAFE_PCHAR)
|
||||
end
|
||||
module_function :escape_uri
|
||||
|
||||
if ''.respond_to?(:force_encoding)
|
||||
def unescape_uri(uri)
|
||||
Parser.unescape(uri).force_encoding('utf-8')
|
||||
end
|
||||
else
|
||||
def unescape_uri(uri)
|
||||
URI.unescape(uri)
|
||||
end
|
||||
end
|
||||
module_function :unescape_uri
|
||||
|
||||
# Taken from Rack 1.1.x to build nested query strings
|
||||
def build_nested_query(value, prefix = nil) #:nodoc:
|
||||
case value
|
||||
when Array
|
||||
value.map { |v|
|
||||
build_nested_query(v, "#{prefix}[]")
|
||||
}.join("&")
|
||||
when Hash
|
||||
value.map { |k, v|
|
||||
build_nested_query(v, prefix ? "#{prefix}[#{Rack::Utils.escape(k)}]" : Rack::Utils.escape(k))
|
||||
}.join("&")
|
||||
when String
|
||||
raise ArgumentError, "value must be a Hash" if prefix.nil?
|
||||
"#{prefix}=#{Rack::Utils.escape(value)}"
|
||||
else
|
||||
prefix
|
||||
end
|
||||
end
|
||||
module_function :build_nested_query
|
||||
|
||||
# Determines whether the regexp must match the entire string.
|
||||
#
|
||||
# regexp_anchored?(/^foo$/) # => true
|
||||
# regexp_anchored?(/foo/) # => false
|
||||
# regexp_anchored?(/^foo/) # => false
|
||||
# regexp_anchored?(/foo$/) # => false
|
||||
def regexp_anchored?(regexp)
|
||||
regexp.source =~ /\A(\\A|\^).*(\\Z|\$)\Z/m ? true : false
|
||||
end
|
||||
module_function :regexp_anchored?
|
||||
|
||||
def normalize_extended_expression(regexp)
|
||||
return regexp unless regexp.options & Regexp::EXTENDED != 0
|
||||
source = regexp.source
|
||||
source.gsub!(/#.+$/, '')
|
||||
source.gsub!(/\s+/, '')
|
||||
source.gsub!(/\\\//, '/')
|
||||
Regexp.compile(source)
|
||||
end
|
||||
module_function :normalize_extended_expression
|
||||
|
||||
def parse_regexp(regexp)
|
||||
cache = @@_parse_regexp_cache ||= {}
|
||||
|
||||
if expression = cache[regexp]
|
||||
return expression
|
||||
end
|
||||
|
||||
unless regexp.is_a?(RegexpWithNamedGroups)
|
||||
regexp = RegexpWithNamedGroups.new(regexp)
|
||||
end
|
||||
|
||||
expression = Regin.parse(regexp)
|
||||
|
||||
unless Regin.regexp_supports_named_captures?
|
||||
tag_captures = Proc.new do |group|
|
||||
case group
|
||||
when Regin::Group
|
||||
# TODO: dup instead of mutating
|
||||
group.instance_variable_set('@name', regexp.names[group.index]) if group.index
|
||||
tag_captures.call(group.expression)
|
||||
when Regin::Expression
|
||||
group.each { |child| tag_captures.call(child) }
|
||||
end
|
||||
end
|
||||
tag_captures.call(expression)
|
||||
end
|
||||
|
||||
cache[regexp] = expression.freeze
|
||||
expression
|
||||
rescue Racc::ParseError, Regin::Parser::ScanError
|
||||
[]
|
||||
end
|
||||
module_function :parse_regexp
|
||||
end
|
||||
end
|
569
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb
vendored
Normal file
569
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb
vendored
Normal file
|
@ -0,0 +1,569 @@
|
|||
require 'forwardable'
|
||||
require 'multiset'
|
||||
|
||||
# Multimap is a generalization of a map or associative array
|
||||
# abstract data type in which more than one value may be associated
|
||||
# with and returned for a given key.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# require 'multimap'
|
||||
# map = Multimap.new
|
||||
# map["a"] = 100
|
||||
# map["b"] = 200
|
||||
# map["a"] = 300
|
||||
# map["a"] # -> [100, 300]
|
||||
# map["b"] # -> [200]
|
||||
# map.keys # -> #<Multiset: {a, a, b}>
|
||||
class Multimap
|
||||
extend Forwardable
|
||||
|
||||
include Enumerable
|
||||
|
||||
# call-seq:
|
||||
# Multimap[ [key =>|, value]* ] => multimap
|
||||
#
|
||||
# Creates a new multimap populated with the given objects.
|
||||
#
|
||||
# Multimap["a", 100, "b", 200] #=> {"a"=>[100], "b"=>[200]}
|
||||
# Multimap["a" => 100, "b" => 200] #=> {"a"=>[100], "b"=>[200]}
|
||||
def self.[](*args)
|
||||
default = []
|
||||
|
||||
if args.size == 2 && args.last.is_a?(Hash)
|
||||
default = args.shift
|
||||
elsif !args.first.is_a?(Hash) && args.size % 2 == 1
|
||||
default = args.shift
|
||||
end
|
||||
|
||||
if args.size == 1 && args.first.is_a?(Hash)
|
||||
args[0] = args.first.inject({}) { |hash, (key, value)|
|
||||
unless value.is_a?(default.class)
|
||||
value = (default.dup << value)
|
||||
end
|
||||
hash[key] = value
|
||||
hash
|
||||
}
|
||||
else
|
||||
index = 0
|
||||
args.map! { |value|
|
||||
unless index % 2 == 0 || value.is_a?(default.class)
|
||||
value = (default.dup << value)
|
||||
end
|
||||
index += 1
|
||||
value
|
||||
}
|
||||
end
|
||||
|
||||
map = new
|
||||
map.instance_variable_set(:@hash, Hash[*args])
|
||||
map.default = default
|
||||
map
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# Multimap.new => multimap
|
||||
# Multimap.new(default) => multimap
|
||||
#
|
||||
# Returns a new, empty multimap.
|
||||
#
|
||||
# map = Multimap.new(Set.new)
|
||||
# h["a"] = 100
|
||||
# h["b"] = 200
|
||||
# h["a"] #=> [100].to_set
|
||||
# h["c"] #=> [].to_set
|
||||
def initialize(default = [])
|
||||
@hash = Hash.new(default)
|
||||
end
|
||||
|
||||
def initialize_copy(original) #:nodoc:
|
||||
@hash = Hash.new(original.default.dup)
|
||||
original._internal_hash.each_pair do |key, container|
|
||||
@hash[key] = container.dup
|
||||
end
|
||||
end
|
||||
|
||||
def_delegators :@hash, :clear, :default, :default=, :empty?,
|
||||
:fetch, :has_key?, :key?
|
||||
|
||||
# Retrieves the <i>value</i> object corresponding to the
|
||||
# <i>*keys</i> object.
|
||||
def [](key)
|
||||
@hash[key]
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map[key] = value => value
|
||||
# map.store(key, value) => value
|
||||
#
|
||||
# Associates the value given by <i>value</i> with the key
|
||||
# given by <i>key</i>. Unlike a regular hash, multiple can be
|
||||
# assoicated with the same value.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => 200]
|
||||
# map["a"] = 9
|
||||
# map["c"] = 4
|
||||
# map #=> {"a" => [100, 9], "b" => [200], "c" => [4]}
|
||||
def store(key, value)
|
||||
update_container(key) do |container|
|
||||
container << value
|
||||
container
|
||||
end
|
||||
end
|
||||
alias_method :[]=, :store
|
||||
|
||||
# call-seq:
|
||||
# map.delete(key, value) => value
|
||||
# map.delete(key) => value
|
||||
#
|
||||
# Deletes and returns a key-value pair from <i>map</i>. If only
|
||||
# <i>key</i> is given, all the values matching that key will be
|
||||
# deleted.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.delete("b", 300) #=> 300
|
||||
# map.delete("a") #=> [100]
|
||||
def delete(key, value = nil)
|
||||
if value
|
||||
@hash[key].delete(value)
|
||||
else
|
||||
@hash.delete(key)
|
||||
end
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.each { |key, value| block } => map
|
||||
#
|
||||
# Calls <i>block</i> for each key/value pair in <i>map</i>, passing
|
||||
# the key and value to the block as a two-element array.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.each { |key, value| puts "#{key} is #{value}" }
|
||||
#
|
||||
# <em>produces:</em>
|
||||
#
|
||||
# a is 100
|
||||
# b is 200
|
||||
# b is 300
|
||||
def each
|
||||
each_pair do |key, value|
|
||||
yield [key, value]
|
||||
end
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.each_association { |key, container| block } => map
|
||||
#
|
||||
# Calls <i>block</i> once for each key/container in <i>map</i>, passing
|
||||
# the key and container to the block as parameters.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.each_association { |key, container| puts "#{key} is #{container}" }
|
||||
#
|
||||
# <em>produces:</em>
|
||||
#
|
||||
# a is [100]
|
||||
# b is [200, 300]
|
||||
def each_association(&block)
|
||||
@hash.each_pair(&block)
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.each_container { |container| block } => map
|
||||
#
|
||||
# Calls <i>block</i> for each container in <i>map</i>, passing the
|
||||
# container as a parameter.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.each_container { |container| puts container }
|
||||
#
|
||||
# <em>produces:</em>
|
||||
#
|
||||
# [100]
|
||||
# [200, 300]
|
||||
def each_container
|
||||
each_association do |_, container|
|
||||
yield container
|
||||
end
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.each_key { |key| block } => map
|
||||
#
|
||||
# Calls <i>block</i> for each key in <i>hsh</i>, passing the key
|
||||
# as a parameter.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.each_key { |key| puts key }
|
||||
#
|
||||
# <em>produces:</em>
|
||||
#
|
||||
# a
|
||||
# b
|
||||
# b
|
||||
def each_key
|
||||
each_pair do |key, _|
|
||||
yield key
|
||||
end
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.each_pair { |key_value_array| block } => map
|
||||
#
|
||||
# Calls <i>block</i> for each key/value pair in <i>map</i>,
|
||||
# passing the key and value as parameters.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.each_pair { |key, value| puts "#{key} is #{value}" }
|
||||
#
|
||||
# <em>produces:</em>
|
||||
#
|
||||
# a is 100
|
||||
# b is 200
|
||||
# b is 300
|
||||
def each_pair
|
||||
each_association do |key, values|
|
||||
values.each do |value|
|
||||
yield key, value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.each_value { |value| block } => map
|
||||
#
|
||||
# Calls <i>block</i> for each key in <i>map</i>, passing the
|
||||
# value as a parameter.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.each_value { |value| puts value }
|
||||
#
|
||||
# <em>produces:</em>
|
||||
#
|
||||
# 100
|
||||
# 200
|
||||
# 300
|
||||
def each_value
|
||||
each_pair do |_, value|
|
||||
yield value
|
||||
end
|
||||
end
|
||||
|
||||
def ==(other) #:nodoc:
|
||||
case other
|
||||
when Multimap
|
||||
@hash == other._internal_hash
|
||||
else
|
||||
@hash == other
|
||||
end
|
||||
end
|
||||
|
||||
def eql?(other) #:nodoc:
|
||||
case other
|
||||
when Multimap
|
||||
@hash.eql?(other._internal_hash)
|
||||
else
|
||||
@hash.eql?(other)
|
||||
end
|
||||
end
|
||||
|
||||
def freeze #:nodoc:
|
||||
each_container { |container| container.freeze }
|
||||
default.freeze
|
||||
super
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.has_value?(value) => true or false
|
||||
# map.value?(value) => true or false
|
||||
#
|
||||
# Returns <tt>true</tt> if the given value is present for any key
|
||||
# in <i>map</i>.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.has_value?(300) #=> true
|
||||
# map.has_value?(999) #=> false
|
||||
def has_value?(value)
|
||||
values.include?(value)
|
||||
end
|
||||
alias_method :value?, :has_value?
|
||||
|
||||
# call-seq:
|
||||
# map.index(value) => key
|
||||
#
|
||||
# Returns the key for a given value. If not found, returns
|
||||
# <tt>nil</tt>.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.index(100) #=> "a"
|
||||
# map.index(200) #=> "b"
|
||||
# map.index(999) #=> nil
|
||||
def index(value)
|
||||
invert[value]
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.delete_if {| key, value | block } -> map
|
||||
#
|
||||
# Deletes every key-value pair from <i>map</i> for which <i>block</i>
|
||||
# evaluates to <code>true</code>.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.delete_if {|key, value| value >= 300 }
|
||||
# #=> Multimap["a" => 100, "b" => 200]
|
||||
#
|
||||
def delete_if
|
||||
each_association do |key, container|
|
||||
container.delete_if do |value|
|
||||
yield [key, value]
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.reject {| key, value | block } -> map
|
||||
#
|
||||
# Same as <code>Multimap#delete_if</code>, but works on (and returns) a
|
||||
# copy of the <i>map</i>. Equivalent to
|
||||
# <code><i>map</i>.dup.delete_if</code>.
|
||||
#
|
||||
def reject(&block)
|
||||
dup.delete_if(&block)
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.reject! {| key, value | block } -> map or nil
|
||||
#
|
||||
# Equivalent to <code>Multimap#delete_if</code>, but returns
|
||||
# <code>nil</code> if no changes were made.
|
||||
#
|
||||
def reject!(&block)
|
||||
old_size = size
|
||||
delete_if(&block)
|
||||
old_size == size ? nil : self
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.replace(other_map) => map
|
||||
#
|
||||
# Replaces the contents of <i>map</i> with the contents of
|
||||
# <i>other_map</i>.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => 200]
|
||||
# map.replace({ "c" => 300, "d" => 400 })
|
||||
# #=> Multimap["c" => 300, "d" => 400]
|
||||
def replace(other)
|
||||
case other
|
||||
when Array
|
||||
@hash.replace(self.class[self.default, *other])
|
||||
when Hash
|
||||
@hash.replace(self.class[self.default, other])
|
||||
when self.class
|
||||
@hash.replace(other)
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.invert => multimap
|
||||
#
|
||||
# Returns a new multimap created by using <i>map</i>'s values as keys,
|
||||
# and the keys as values.
|
||||
#
|
||||
# map = Multimap["n" => 100, "m" => 100, "d" => [200, 300]]
|
||||
# map.invert #=> Multimap[100 => ["n", "m"], 200 => "d", 300 => "d"]
|
||||
def invert
|
||||
h = self.class.new(default.dup)
|
||||
each_pair { |key, value| h[value] = key }
|
||||
h
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.keys => multiset
|
||||
#
|
||||
# Returns a new +Multiset+ populated with the keys from this hash. See also
|
||||
# <tt>Multimap#values</tt> and <tt>Multimap#containers</tt>.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
|
||||
# map.keys #=> Multiset.new(["a", "b", "b", "c"])
|
||||
def keys
|
||||
keys = Multiset.new
|
||||
each_key { |key| keys << key }
|
||||
keys
|
||||
end
|
||||
|
||||
# Returns true if the given key is present in Multimap.
|
||||
def include?(key)
|
||||
keys.include?(key)
|
||||
end
|
||||
alias_method :member?, :include?
|
||||
|
||||
# call-seq:
|
||||
# map.length => fixnum
|
||||
# map.size => fixnum
|
||||
#
|
||||
# Returns the number of key-value pairs in the map.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
|
||||
# map.length #=> 4
|
||||
# map.delete("a") #=> 100
|
||||
# map.length #=> 3
|
||||
def size
|
||||
values.size
|
||||
end
|
||||
alias_method :length, :size
|
||||
|
||||
# call-seq:
|
||||
# map.merge(other_map) => multimap
|
||||
#
|
||||
# Returns a new multimap containing the contents of <i>other_map</i> and
|
||||
# the contents of <i>map</i>.
|
||||
#
|
||||
# map1 = Multimap["a" => 100, "b" => 200]
|
||||
# map2 = Multimap["a" => 254, "c" => 300]
|
||||
# map2.merge(map2) #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300]
|
||||
# map1 #=> Multimap["a" => 100, "b" => 200]
|
||||
def merge(other)
|
||||
dup.update(other)
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.merge!(other_map) => multimap
|
||||
# map.update(other_map) => multimap
|
||||
#
|
||||
# Adds each pair from <i>other_map</i> to <i>map</i>.
|
||||
#
|
||||
# map1 = Multimap["a" => 100, "b" => 200]
|
||||
# map2 = Multimap["b" => 254, "c" => 300]
|
||||
#
|
||||
# map1.merge!(map2)
|
||||
# #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300]
|
||||
def update(other)
|
||||
case other
|
||||
when self.class
|
||||
other.each_pair { |key, value| store(key, value) }
|
||||
when Hash
|
||||
update(self.class[self.default, other])
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
self
|
||||
end
|
||||
alias_method :merge!, :update
|
||||
|
||||
# call-seq:
|
||||
# map.select { |key, value| block } => multimap
|
||||
#
|
||||
# Returns a new Multimap consisting of the pairs for which the
|
||||
# block returns true.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => 200, "c" => 300]
|
||||
# map.select { |k,v| k > "a" } #=> Multimap["b" => 200, "c" => 300]
|
||||
# map.select { |k,v| v < 200 } #=> Multimap["a" => 100]
|
||||
def select
|
||||
inject(self.class.new) { |map, (key, value)|
|
||||
map[key] = value if yield([key, value])
|
||||
map
|
||||
}
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.to_a => array
|
||||
#
|
||||
# Converts <i>map</i> to a nested array of [<i>key,
|
||||
# value</i>] arrays.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
|
||||
# map.to_a #=> [["a", 100], ["b", 200], ["b", 300], ["c", 400]]
|
||||
def to_a
|
||||
ary = []
|
||||
each_pair do |key, value|
|
||||
ary << [key, value]
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.to_hash => hash
|
||||
#
|
||||
# Converts <i>map</i> to a basic hash.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.to_hash #=> { "a" => [100], "b" => [200, 300] }
|
||||
def to_hash
|
||||
@hash.dup
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.containers => array
|
||||
#
|
||||
# Returns a new array populated with the containers from <i>map</i>. See
|
||||
# also <tt>Multimap#keys</tt> and <tt>Multimap#values</tt>.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.containers #=> [[100], [200, 300]]
|
||||
def containers
|
||||
containers = []
|
||||
each_container { |container| containers << container }
|
||||
containers
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# map.values => array
|
||||
#
|
||||
# Returns a new array populated with the values from <i>map</i>. See
|
||||
# also <tt>Multimap#keys</tt> and <tt>Multimap#containers</tt>.
|
||||
#
|
||||
# map = Multimap["a" => 100, "b" => [200, 300]]
|
||||
# map.values #=> [100, 200, 300]
|
||||
def values
|
||||
values = []
|
||||
each_value { |value| values << value }
|
||||
values
|
||||
end
|
||||
|
||||
# Return an array containing the values associated with the given keys.
|
||||
def values_at(*keys)
|
||||
@hash.values_at(*keys)
|
||||
end
|
||||
|
||||
def marshal_dump #:nodoc:
|
||||
@hash
|
||||
end
|
||||
|
||||
def marshal_load(hash) #:nodoc:
|
||||
@hash = hash
|
||||
end
|
||||
|
||||
def to_yaml(opts = {}) #:nodoc:
|
||||
YAML::quick_emit(self, opts) do |out|
|
||||
out.map(taguri, to_yaml_style) do |map|
|
||||
@hash.each do |k, v|
|
||||
map.add(k, v)
|
||||
end
|
||||
map.add('__default__', @hash.default)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def yaml_initialize(tag, val) #:nodoc:
|
||||
default = val.delete('__default__')
|
||||
@hash = val
|
||||
@hash.default = default
|
||||
self
|
||||
end
|
||||
|
||||
protected
|
||||
def _internal_hash #:nodoc:
|
||||
@hash
|
||||
end
|
||||
|
||||
def update_container(key) #:nodoc:
|
||||
container = @hash[key]
|
||||
container = container.dup if container.equal?(default)
|
||||
container = yield(container)
|
||||
@hash[key] = container
|
||||
end
|
||||
end
|
185
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb
vendored
Normal file
185
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
require 'set'
|
||||
|
||||
# Multiset implements a collection of unordered values and
|
||||
# allows duplicates.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# require 'multiset'
|
||||
# s1 = Multiset.new [1, 2] # -> #<Multiset: {1, 2}>
|
||||
# s1.add(2) # -> #<Multiset: {1, 2, 2}>
|
||||
# s1.merge([2, 6]) # -> #<Multiset: {1, 2, 2, 2, 3}>
|
||||
# s1.multiplicity(2) # -> 3
|
||||
# s1.multiplicity(3) # -> 1
|
||||
class Multiset < Set
|
||||
def initialize(*args, &block) #:nodoc:
|
||||
@hash = Hash.new(0)
|
||||
super
|
||||
end
|
||||
|
||||
# Returns the number of times an element belongs to the multiset.
|
||||
def multiplicity(e)
|
||||
@hash[e]
|
||||
end
|
||||
|
||||
# Returns the total number of elements in a multiset, including
|
||||
# repeated memberships
|
||||
def cardinality
|
||||
@hash.inject(0) { |s, (e, m)| s += m }
|
||||
end
|
||||
alias_method :size, :cardinality
|
||||
alias_method :length, :cardinality
|
||||
|
||||
# Converts the set to an array. The order of elements is uncertain.
|
||||
def to_a
|
||||
inject([]) { |ary, (key, _)| ary << key }
|
||||
end
|
||||
|
||||
# Returns true if the set is a superset of the given set.
|
||||
def superset?(set)
|
||||
set.is_a?(self.class) or raise ArgumentError, "value must be a set"
|
||||
return false if cardinality < set.cardinality
|
||||
set.all? { |o| set.multiplicity(o) <= multiplicity(o) }
|
||||
end
|
||||
|
||||
# Returns true if the set is a proper superset of the given set.
|
||||
def proper_superset?(set)
|
||||
set.is_a?(self.class) or raise ArgumentError, "value must be a set"
|
||||
return false if cardinality <= set.cardinality
|
||||
set.all? { |o| set.multiplicity(o) <= multiplicity(o) }
|
||||
end
|
||||
|
||||
# Returns true if the set is a subset of the given set.
|
||||
def subset?(set)
|
||||
set.is_a?(self.class) or raise ArgumentError, "value must be a set"
|
||||
return false if set.cardinality < cardinality
|
||||
all? { |o| multiplicity(o) <= set.multiplicity(o) }
|
||||
end
|
||||
|
||||
# Returns true if the set is a proper subset of the given set.
|
||||
def proper_subset?(set)
|
||||
set.is_a?(self.class) or raise ArgumentError, "value must be a set"
|
||||
return false if set.cardinality <= cardinality
|
||||
all? { |o| multiplicity(o) <= set.multiplicity(o) }
|
||||
end
|
||||
|
||||
# Calls the given block once for each element in the set, passing
|
||||
# the element as parameter. Returns an enumerator if no block is
|
||||
# given.
|
||||
def each
|
||||
@hash.each_pair do |key, multiplicity|
|
||||
multiplicity.times do
|
||||
yield(key)
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Adds the given object to the set and returns self. Use +merge+ to
|
||||
# add many elements at once.
|
||||
def add(o)
|
||||
@hash[o] ||= 0
|
||||
@hash[o] += 1
|
||||
self
|
||||
end
|
||||
alias << add
|
||||
|
||||
undef :add?
|
||||
|
||||
# Deletes all the identical object from the set and returns self.
|
||||
# If +n+ is given, it will remove that amount of identical objects
|
||||
# from the set. Use +subtract+ to delete many different items at
|
||||
# once.
|
||||
def delete(o, n = nil)
|
||||
if n
|
||||
@hash[o] ||= 0
|
||||
@hash[o] -= n if @hash[o] > 0
|
||||
@hash.delete(o) if @hash[o] == 0
|
||||
else
|
||||
@hash.delete(o)
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
undef :delete?
|
||||
|
||||
# Deletes every element of the set for which block evaluates to
|
||||
# true, and returns self.
|
||||
def delete_if
|
||||
each { |o| delete(o) if yield(o) }
|
||||
self
|
||||
end
|
||||
|
||||
# Merges the elements of the given enumerable object to the set and
|
||||
# returns self.
|
||||
def merge(enum)
|
||||
enum.each { |o| add(o) }
|
||||
self
|
||||
end
|
||||
|
||||
# Deletes every element that appears in the given enumerable object
|
||||
# and returns self.
|
||||
def subtract(enum)
|
||||
enum.each { |o| delete(o, 1) }
|
||||
self
|
||||
end
|
||||
|
||||
# Returns a new set containing elements common to the set and the
|
||||
# given enumerable object.
|
||||
def &(enum)
|
||||
s = dup
|
||||
n = self.class.new
|
||||
enum.each { |o|
|
||||
if s.include?(o)
|
||||
s.delete(o, 1)
|
||||
n.add(o)
|
||||
end
|
||||
}
|
||||
n
|
||||
end
|
||||
alias intersection &
|
||||
|
||||
# Returns a new set containing elements exclusive between the set
|
||||
# and the given enumerable object. (set ^ enum) is equivalent to
|
||||
# ((set | enum) - (set & enum)).
|
||||
def ^(enum)
|
||||
n = self.class.new(enum)
|
||||
each { |o| n.include?(o) ? n.delete(o, 1) : n.add(o) }
|
||||
n
|
||||
end
|
||||
|
||||
# Returns true if two sets are equal. Two multisets are equal if
|
||||
# they have the same cardinalities and each element has the same
|
||||
# multiplicity in both sets. The equality of each element inside
|
||||
# the multiset is defined according to Object#eql?.
|
||||
def eql?(set)
|
||||
return true if equal?(set)
|
||||
set = self.class.new(set) unless set.is_a?(self.class)
|
||||
return false unless cardinality == set.cardinality
|
||||
superset?(set) && subset?(set)
|
||||
end
|
||||
alias_method :==, :eql?
|
||||
|
||||
def marshal_dump #:nodoc:
|
||||
@hash
|
||||
end
|
||||
|
||||
def marshal_load(hash) #:nodoc:
|
||||
@hash = hash
|
||||
end
|
||||
|
||||
def to_yaml(opts = {}) #:nodoc:
|
||||
YAML::quick_emit(self, opts) do |out|
|
||||
out.map(taguri, to_yaml_style) do |map|
|
||||
@hash.each do |k, v|
|
||||
map.add(k, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def yaml_initialize(tag, val) #:nodoc:
|
||||
@hash = val
|
||||
self
|
||||
end
|
||||
end
|
|
@ -0,0 +1,158 @@
|
|||
require 'multimap'
|
||||
|
||||
# NestedMultimap allows values to be assoicated with a nested
|
||||
# set of keys.
|
||||
class NestedMultimap < Multimap
|
||||
# call-seq:
|
||||
# multimap[*keys] = value => value
|
||||
# multimap.store(*keys, value) => value
|
||||
#
|
||||
# Associates the value given by <i>value</i> with multiple key
|
||||
# given by <i>keys</i>.
|
||||
#
|
||||
# map = NestedMultimap.new
|
||||
# map["a"] = 100
|
||||
# map["a", "b"] = 101
|
||||
# map["a"] = 102
|
||||
# map #=> {"a"=>{"b"=>[100, 101, 102], default => [100, 102]}}
|
||||
def store(*args)
|
||||
keys = args
|
||||
value = args.pop
|
||||
|
||||
raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
|
||||
|
||||
if keys.length > 1
|
||||
update_container(keys.shift) do |container|
|
||||
container = self.class.new(container) unless container.is_a?(self.class)
|
||||
container[*keys] = value
|
||||
container
|
||||
end
|
||||
elsif keys.length == 1
|
||||
super(keys.first, value)
|
||||
else
|
||||
self << value
|
||||
end
|
||||
end
|
||||
alias_method :[]=, :store
|
||||
|
||||
# call-seq:
|
||||
# multimap << obj => multimap
|
||||
#
|
||||
# Pushes the given object on to the end of all the containers.
|
||||
#
|
||||
# map = NestedMultimap["a" => [100], "b" => [200, 300]]
|
||||
# map << 300
|
||||
# map["a"] #=> [100, 300]
|
||||
# map["c"] #=> [300]
|
||||
def <<(value)
|
||||
@hash.each_value { |container| container << value }
|
||||
self.default << value
|
||||
self
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# multimap[*keys] => value
|
||||
# multimap[key1, key2, key3] => value
|
||||
#
|
||||
# Retrieves the <i>value</i> object corresponding to the
|
||||
# <i>*keys</i> object.
|
||||
def [](*keys)
|
||||
i, l, r, k = 0, keys.length, self, self.class
|
||||
while r.is_a?(k)
|
||||
r = i < l ? r._internal_hash[keys[i]] : r.default
|
||||
i += 1
|
||||
end
|
||||
r
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# multimap.each_association { |key, container| block } => multimap
|
||||
#
|
||||
# Calls <i>block</i> once for each key/container in <i>map</i>, passing
|
||||
# the key and container to the block as parameters.
|
||||
#
|
||||
# map = NestedMultimap.new
|
||||
# map["a"] = 100
|
||||
# map["a", "b"] = 101
|
||||
# map["a"] = 102
|
||||
# map["c"] = 200
|
||||
# map.each_association { |key, container| puts "#{key} is #{container}" }
|
||||
#
|
||||
# <em>produces:</em>
|
||||
#
|
||||
# ["a", "b"] is [100, 101, 102]
|
||||
# "c" is [200]
|
||||
def each_association
|
||||
super() do |key, container|
|
||||
if container.respond_to?(:each_association)
|
||||
container.each_association do |nested_key, value|
|
||||
yield [key, nested_key].flatten, value
|
||||
end
|
||||
else
|
||||
yield key, container
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# multimap.each_container_with_default { |container| block } => map
|
||||
#
|
||||
# Calls <i>block</i> for every container in <i>map</i> including
|
||||
# the default, passing the container as a parameter.
|
||||
#
|
||||
# map = NestedMultimap.new
|
||||
# map["a"] = 100
|
||||
# map["a", "b"] = 101
|
||||
# map["a"] = 102
|
||||
# map.each_container_with_default { |container| puts container }
|
||||
#
|
||||
# <em>produces:</em>
|
||||
#
|
||||
# [100, 101, 102]
|
||||
# [100, 102]
|
||||
# []
|
||||
def each_container_with_default(&block)
|
||||
@hash.each_value do |container|
|
||||
iterate_over_container(container, &block)
|
||||
end
|
||||
iterate_over_container(default, &block)
|
||||
self
|
||||
end
|
||||
|
||||
# call-seq:
|
||||
# multimap.containers_with_default => array
|
||||
#
|
||||
# Returns a new array populated with all the containers from
|
||||
# <i>map</i> including the default.
|
||||
#
|
||||
# map = NestedMultimap.new
|
||||
# map["a"] = 100
|
||||
# map["a", "b"] = 101
|
||||
# map["a"] = 102
|
||||
# map.containers_with_default #=> [[100, 101, 102], [100, 102], []]
|
||||
def containers_with_default
|
||||
containers = []
|
||||
each_container_with_default { |container| containers << container }
|
||||
containers
|
||||
end
|
||||
|
||||
def inspect #:nodoc:
|
||||
super.gsub(/\}$/, ", default => #{default.inspect}}")
|
||||
end
|
||||
|
||||
private
|
||||
def iterate_over_container(container)
|
||||
if container.respond_to?(:each_container_with_default)
|
||||
container.each_container_with_default do |value|
|
||||
yield value
|
||||
end
|
||||
else
|
||||
yield container
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'nested_multimap_ext'
|
||||
rescue LoadError
|
||||
end
|
45
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb
vendored
Normal file
45
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
module Regin
|
||||
autoload :Alternation, 'regin/alternation'
|
||||
autoload :Anchor, 'regin/anchor'
|
||||
autoload :Atom, 'regin/atom'
|
||||
autoload :Character, 'regin/character'
|
||||
autoload :CharacterClass, 'regin/character_class'
|
||||
autoload :Collection, 'regin/collection'
|
||||
autoload :Expression, 'regin/expression'
|
||||
autoload :Group, 'regin/group'
|
||||
autoload :Options, 'regin/options'
|
||||
autoload :Parser, 'regin/parser'
|
||||
|
||||
class << self
|
||||
begin
|
||||
eval('foo = /(?<foo>.*)/').named_captures
|
||||
|
||||
# Returns true if the interpreter is using the Oniguruma Regexp lib
|
||||
# and supports named captures.
|
||||
#
|
||||
# /(?<foo>bar)/
|
||||
def regexp_supports_named_captures?
|
||||
true
|
||||
end
|
||||
rescue SyntaxError, NoMethodError
|
||||
def regexp_supports_named_captures? #:nodoc:
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Parses Regexp and returns a Expression data structure.
|
||||
def parse(regexp)
|
||||
Parser.parse_regexp(regexp)
|
||||
end
|
||||
|
||||
# Recompiles Regexp by parsing it and turning it back into a Regexp.
|
||||
#
|
||||
# (In the future Regin will perform some Regexp optimizations
|
||||
# such as removing unnecessary captures and options)
|
||||
def compile(source)
|
||||
regexp = Regexp.compile(source)
|
||||
expression = parse(regexp)
|
||||
Regexp.compile(expression.to_s(true), expression.flags)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
module Regin
|
||||
class Alternation < Collection
|
||||
def initialize(*args)
|
||||
args, options = extract_options(args)
|
||||
|
||||
if args.length == 1 && args.first.instance_of?(Array)
|
||||
super(args.first)
|
||||
else
|
||||
super(args)
|
||||
end
|
||||
|
||||
if options.key?(:ignorecase)
|
||||
@array.map! { |e| e.dup(:ignorecase => options[:ignorecase]) }
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if expression could be treated as a literal string.
|
||||
#
|
||||
# Alternation groups are never literal.
|
||||
def literal?
|
||||
false
|
||||
end
|
||||
|
||||
def flags
|
||||
0
|
||||
end
|
||||
|
||||
def dup(options = {})
|
||||
self.class.new(to_a, options)
|
||||
end
|
||||
|
||||
def to_s(parent = false)
|
||||
map { |e| e.to_s(parent) }.join('|')
|
||||
end
|
||||
|
||||
def inspect #:nodoc:
|
||||
to_s.inspect
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
module Regin
|
||||
class Anchor < Atom
|
||||
end
|
||||
end
|
59
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb
vendored
Normal file
59
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
module Regin
|
||||
class Atom
|
||||
attr_reader :value, :ignorecase
|
||||
|
||||
def initialize(value, options = {})
|
||||
@value = value
|
||||
@ignorecase = options[:ignorecase]
|
||||
end
|
||||
|
||||
def option_names
|
||||
%w( ignorecase )
|
||||
end
|
||||
|
||||
# Returns true if expression could be treated as a literal string.
|
||||
def literal?
|
||||
false
|
||||
end
|
||||
|
||||
def casefold?
|
||||
ignorecase ? true : false
|
||||
end
|
||||
|
||||
def dup(options = {})
|
||||
original_options = option_names.inject({}) do |h, m|
|
||||
h[m.to_sym] = send(m)
|
||||
h
|
||||
end
|
||||
self.class.new(value, original_options.merge(options))
|
||||
end
|
||||
|
||||
def to_s(parent = false)
|
||||
"#{value}"
|
||||
end
|
||||
|
||||
def inspect #:nodoc:
|
||||
"#<#{self.class.to_s.sub('Regin::', '')} #{to_s.inspect}>"
|
||||
end
|
||||
|
||||
def ==(other) #:nodoc:
|
||||
case other
|
||||
when String
|
||||
other == to_s
|
||||
else
|
||||
eql?(other)
|
||||
end
|
||||
end
|
||||
|
||||
def eql?(other) #:nodoc:
|
||||
other.instance_of?(self.class) &&
|
||||
self.value.eql?(other.value) &&
|
||||
(!!self.ignorecase).eql?(!!other.ignorecase)
|
||||
end
|
||||
|
||||
def freeze #:nodoc:
|
||||
value.freeze
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
module Regin
|
||||
class Character < Atom
|
||||
attr_reader :quantifier
|
||||
|
||||
def initialize(value, options = {})
|
||||
@quantifier = options[:quantifier]
|
||||
super
|
||||
end
|
||||
|
||||
def option_names
|
||||
%w( quantifier ) + super
|
||||
end
|
||||
|
||||
# Returns true if expression could be treated as a literal string.
|
||||
#
|
||||
# A Character is literal is there is no quantifier attached to it.
|
||||
def literal?
|
||||
quantifier.nil? && !ignorecase
|
||||
end
|
||||
|
||||
def to_s(parent = false)
|
||||
if !parent && ignorecase
|
||||
"(?i-mx:#{value})#{quantifier}"
|
||||
else
|
||||
"#{value}#{quantifier}"
|
||||
end
|
||||
end
|
||||
|
||||
def to_regexp(anchored = false)
|
||||
re = to_s(true)
|
||||
re = "\\A#{re}\\Z" if anchored
|
||||
Regexp.compile(re, ignorecase)
|
||||
end
|
||||
|
||||
def match(char)
|
||||
to_regexp(true).match(char)
|
||||
end
|
||||
|
||||
def include?(char)
|
||||
if ignorecase
|
||||
value.downcase == char.downcase
|
||||
else
|
||||
value == char
|
||||
end
|
||||
end
|
||||
|
||||
def eql?(other) #:nodoc:
|
||||
super && quantifier.eql?(other.quantifier)
|
||||
end
|
||||
|
||||
def freeze #:nodoc:
|
||||
quantifier.freeze if quantifier
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,55 @@
|
|||
module Regin
|
||||
class CharacterClass < Character
|
||||
def initialize(value, options = {})
|
||||
@negate = options[:negate]
|
||||
super
|
||||
end
|
||||
|
||||
def option_names
|
||||
%w( negate ) + super
|
||||
end
|
||||
|
||||
attr_reader :negate
|
||||
|
||||
def negated?
|
||||
negate ? true : false
|
||||
end
|
||||
|
||||
# Returns true if expression could be treated as a literal string.
|
||||
#
|
||||
# A CharacterClass is never literal.
|
||||
def literal?
|
||||
false
|
||||
end
|
||||
|
||||
def bracketed?
|
||||
value != '.' && value !~ /^\\[dDsSwW]$/
|
||||
end
|
||||
|
||||
def to_s(parent = false)
|
||||
if bracketed?
|
||||
if !parent && ignorecase
|
||||
"(?i-mx:[#{negate && '^'}#{value}])#{quantifier}"
|
||||
else
|
||||
"[#{negate && '^'}#{value}]#{quantifier}"
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def include?(char)
|
||||
re = quantifier ? to_s.sub(/#{Regexp.escape(quantifier)}$/, '') : to_s
|
||||
Regexp.compile("\\A#{re}\\Z").match(char)
|
||||
end
|
||||
|
||||
def eql?(other) #:nodoc:
|
||||
super && negate == other.negate
|
||||
end
|
||||
|
||||
def freeze #:nodoc:
|
||||
negate.freeze if negate
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,83 @@
|
|||
module Regin
|
||||
class Collection
|
||||
include Enumerable
|
||||
|
||||
def initialize(*args)
|
||||
@array = Array.new(*args)
|
||||
end
|
||||
|
||||
def each
|
||||
@array.each{ |item| yield item }
|
||||
end
|
||||
|
||||
def [](i)
|
||||
@array[i]
|
||||
end
|
||||
|
||||
def length
|
||||
@array.length
|
||||
end
|
||||
alias_method :size, :length
|
||||
|
||||
def first
|
||||
@array.first
|
||||
end
|
||||
|
||||
def last
|
||||
@array.last
|
||||
end
|
||||
|
||||
def +(other)
|
||||
ary = other.is_a?(self.class) ? other.internal_array : other
|
||||
self.class.new(@array + ary)
|
||||
end
|
||||
|
||||
def to_regexp(anchored = false)
|
||||
re = to_s(true)
|
||||
re = "\\A#{re}\\Z" if anchored
|
||||
Regexp.compile(re, flags)
|
||||
end
|
||||
|
||||
def match(char)
|
||||
to_regexp.match(char)
|
||||
end
|
||||
|
||||
def include?(char)
|
||||
any? { |e| e.include?(char) }
|
||||
end
|
||||
|
||||
def ==(other) #:nodoc:
|
||||
case other
|
||||
when String
|
||||
other == to_s
|
||||
when Array
|
||||
other == @array
|
||||
else
|
||||
eql?(other)
|
||||
end
|
||||
end
|
||||
|
||||
def eql?(other) #:nodoc:
|
||||
other.instance_of?(self.class) && @array.eql?(other.internal_array)
|
||||
end
|
||||
|
||||
def freeze #:nodoc:
|
||||
each { |e| e.freeze }
|
||||
@array.freeze
|
||||
super
|
||||
end
|
||||
|
||||
protected
|
||||
def internal_array #:nodoc:
|
||||
@array
|
||||
end
|
||||
|
||||
def extract_options(args)
|
||||
if args.last.is_a?(Hash)
|
||||
return args[0..-2], args.last
|
||||
else
|
||||
return args, {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,126 @@
|
|||
module Regin
|
||||
class Expression < Collection
|
||||
attr_reader :ignorecase, :multiline, :extended
|
||||
|
||||
def initialize(*args)
|
||||
args, options = extract_options(args)
|
||||
|
||||
@multiline = @ignorecase = @extended = nil
|
||||
|
||||
if args.length == 1 && args.first.instance_of?(Array)
|
||||
super(args.first)
|
||||
else
|
||||
args = args.map { |e| e.instance_of?(String) ? Character.new(e) : e }
|
||||
super(args)
|
||||
end
|
||||
|
||||
self.multiline = options[:multiline] if options.key?(:multiline)
|
||||
self.ignorecase = options[:ignorecase] if options.key?(:ignorecase)
|
||||
self.extended = options[:extended] if options.key?(:extended)
|
||||
end
|
||||
|
||||
# Returns true if expression could be treated as a literal string.
|
||||
#
|
||||
# A Expression is literal if all its elements are literal.
|
||||
def literal?
|
||||
!ignorecase && all? { |e| e.literal? }
|
||||
end
|
||||
|
||||
def anchored?
|
||||
anchored_to_start? && anchored_to_end?
|
||||
end
|
||||
|
||||
def anchored_to_start?
|
||||
first.is_a?(Anchor) && first == '\A'
|
||||
end
|
||||
|
||||
def anchored_to_end?
|
||||
last.is_a?(Anchor) && last == '\Z'
|
||||
end
|
||||
|
||||
def anchored_to_line?
|
||||
anchored_to_start_of_line? && anchored_to_end_of_line?
|
||||
end
|
||||
|
||||
def anchored_to_start_of_line?
|
||||
anchored_to_start? || (first.is_a?(Anchor) && first == '^')
|
||||
end
|
||||
|
||||
def anchored_to_end_of_line?
|
||||
anchored_to_end? || (last.is_a?(Anchor) && last == '$')
|
||||
end
|
||||
|
||||
def options?
|
||||
options.any?(true)
|
||||
end
|
||||
|
||||
def flags
|
||||
options.to_i
|
||||
end
|
||||
|
||||
def +(other)
|
||||
ary = other.is_a?(self.class) ? other.internal_array : other
|
||||
ary = @array + ary + [options.to_h(true)]
|
||||
self.class.new(*ary)
|
||||
end
|
||||
|
||||
def dup(options = {})
|
||||
expression = super()
|
||||
expression.multiline = options[:multiline] if options.key?(:multiline)
|
||||
expression.ignorecase = options[:ignorecase] if options.key?(:ignorecase)
|
||||
expression.extended = options[:extended] if options.key?(:extended)
|
||||
expression
|
||||
end
|
||||
|
||||
def to_s(parent = false)
|
||||
if parent || !options?
|
||||
map { |e| e.to_s(parent) }.join
|
||||
else
|
||||
with, without = [], []
|
||||
multiline ? (with << 'm') : (without << 'm')
|
||||
ignorecase ? (with << 'i') : (without << 'i')
|
||||
extended ? (with << 'x') : (without << 'x')
|
||||
|
||||
with = with.join
|
||||
without = without.any? ? "-#{without.join}" : ''
|
||||
|
||||
"(?#{with}#{without}:#{map { |e| e.to_s(true) }.join})"
|
||||
end
|
||||
end
|
||||
|
||||
def inspect #:nodoc:
|
||||
"#<Expression #{to_s.inspect}>"
|
||||
end
|
||||
|
||||
def casefold?
|
||||
ignorecase
|
||||
end
|
||||
|
||||
def eql?(other) #:nodoc:
|
||||
super &&
|
||||
!!self.multiline == !!other.multiline &&
|
||||
!!self.ignorecase == !!other.ignorecase &&
|
||||
!!self.extended == !!other.extended
|
||||
end
|
||||
|
||||
protected
|
||||
def options
|
||||
Options.new(multiline, ignorecase, extended)
|
||||
end
|
||||
|
||||
def multiline=(multiline)
|
||||
@multiline = multiline
|
||||
end
|
||||
|
||||
def ignorecase=(ignorecase)
|
||||
if @ignorecase.nil?
|
||||
@array.map! { |e| e.dup(:ignorecase => ignorecase) }
|
||||
@ignorecase = ignorecase
|
||||
end
|
||||
end
|
||||
|
||||
def extended=(extended)
|
||||
@extended = extended
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,90 @@
|
|||
module Regin
|
||||
class Group
|
||||
attr_reader :expression, :quantifier, :capture, :index, :name
|
||||
|
||||
def initialize(expression, options = {})
|
||||
@quantifier = @index = @name = nil
|
||||
@capture = true
|
||||
@expression = expression.dup(options)
|
||||
|
||||
@quantifier = options[:quantifier] if options.key?(:quantifier)
|
||||
@capture = options[:capture] if options.key?(:capture)
|
||||
@index = options[:index] if options.key?(:index)
|
||||
@name = options[:name] if options.key?(:name)
|
||||
end
|
||||
|
||||
def option_names
|
||||
%w( quantifier capture index name )
|
||||
end
|
||||
|
||||
# Returns true if expression could be treated as a literal string.
|
||||
#
|
||||
# A Group is literal if its expression is literal and it has no quantifier.
|
||||
def literal?
|
||||
quantifier.nil? && expression.literal?
|
||||
end
|
||||
|
||||
def to_s(parent = false)
|
||||
if !expression.options?
|
||||
"(#{capture ? '' : '?:'}#{expression.to_s(parent)})#{quantifier}"
|
||||
elsif capture == false
|
||||
"#{expression.to_s}#{quantifier}"
|
||||
else
|
||||
"(#{expression.to_s})#{quantifier}"
|
||||
end
|
||||
end
|
||||
|
||||
def to_regexp(anchored = false)
|
||||
re = to_s
|
||||
re = "\\A#{re}\\Z" if anchored
|
||||
Regexp.compile(re)
|
||||
end
|
||||
|
||||
def dup(options = {})
|
||||
original_options = option_names.inject({}) do |h, m|
|
||||
h[m.to_sym] = send(m)
|
||||
h
|
||||
end
|
||||
self.class.new(expression, original_options.merge(options))
|
||||
end
|
||||
|
||||
def inspect #:nodoc:
|
||||
to_s.inspect
|
||||
end
|
||||
|
||||
def match(char)
|
||||
to_regexp.match(char)
|
||||
end
|
||||
|
||||
def include?(char)
|
||||
expression.include?(char)
|
||||
end
|
||||
|
||||
def capture?
|
||||
capture
|
||||
end
|
||||
|
||||
def ==(other) #:nodoc:
|
||||
case other
|
||||
when String
|
||||
other == to_s
|
||||
else
|
||||
eql?(other)
|
||||
end
|
||||
end
|
||||
|
||||
def eql?(other) #:nodoc:
|
||||
other.is_a?(self.class) &&
|
||||
self.expression == other.expression &&
|
||||
self.quantifier == other.quantifier &&
|
||||
self.capture == other.capture &&
|
||||
self.index == other.index &&
|
||||
self.name == other.name
|
||||
end
|
||||
|
||||
def freeze #:nodoc:
|
||||
expression.freeze if expression
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,55 @@
|
|||
module Regin
|
||||
class Options
|
||||
def self.from_int(flags)
|
||||
multiline = flags & Regexp::MULTILINE != 0
|
||||
ignorecase = flags & Regexp::IGNORECASE != 0
|
||||
extended = flags & Regexp::EXTENDED != 0
|
||||
|
||||
new(multiline, ignorecase, extended)
|
||||
end
|
||||
|
||||
attr_reader :multiline, :ignorecase, :extended
|
||||
|
||||
def initialize(*args)
|
||||
if args.first.is_a?(Hash)
|
||||
@multiline = args[0][:multiline]
|
||||
@ignorecase = args[0][:ignorecase]
|
||||
@extended = args[0][:extended]
|
||||
else
|
||||
@multiline = args[0]
|
||||
@ignorecase = args[1]
|
||||
@extended = args[2]
|
||||
end
|
||||
end
|
||||
|
||||
def any?(explicit = false)
|
||||
if explicit
|
||||
!multiline.nil? || !ignorecase.nil? || !extended.nil?
|
||||
else
|
||||
multiline || ignorecase || extended
|
||||
end
|
||||
end
|
||||
|
||||
def to_h(explicit = false)
|
||||
if explicit
|
||||
options = {}
|
||||
options[:multiline] = multiline unless multiline.nil?
|
||||
options[:ignorecase] = ignorecase unless ignorecase.nil?
|
||||
options[:extended] = extended unless extended.nil?
|
||||
options
|
||||
else
|
||||
{ :multiline => multiline,
|
||||
:ignorecase => ignorecase,
|
||||
:extended => extended }
|
||||
end
|
||||
end
|
||||
|
||||
def to_i
|
||||
flag = 0
|
||||
flag |= Regexp::MULTILINE if multiline
|
||||
flag |= Regexp::IGNORECASE if ignorecase
|
||||
flag |= Regexp::EXTENDED if extended
|
||||
flag
|
||||
end
|
||||
end
|
||||
end
|
415
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb
vendored
Normal file
415
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb
vendored
Normal file
|
@ -0,0 +1,415 @@
|
|||
#
|
||||
# DO NOT MODIFY!!!!
|
||||
# This file is automatically generated by Racc 1.4.6
|
||||
# from Racc grammer file "".
|
||||
#
|
||||
|
||||
require 'racc/parser.rb'
|
||||
module Regin
|
||||
class Parser < Racc::Parser #:nodoc: all
|
||||
|
||||
def self.parse_regexp(regexp)
|
||||
options = Options.from_int(regexp.options)
|
||||
|
||||
parser = new
|
||||
parser.options_stack << options.to_h
|
||||
|
||||
expression = parser.scan_str(regexp.source)
|
||||
expression = expression.dup(options.to_h) if options.any?
|
||||
expression
|
||||
end
|
||||
|
||||
attr_accessor :options_stack
|
||||
|
||||
def initialize
|
||||
@capture_index = 0
|
||||
@capture_index_stack = []
|
||||
@options_stack = []
|
||||
end
|
||||
|
||||
##### State transition tables begin ###
|
||||
|
||||
racc_action_table = [
|
||||
2, 18, 19, 19, 8, 10, 11, 13, 48, 19,
|
||||
2, 45, 3, 5, 8, 10, 11, 13, 64, 47,
|
||||
2, 55, 3, 5, 8, 10, 11, 13, 29, 19,
|
||||
2, 16, 3, 5, 8, 10, 11, 13, 61, 19,
|
||||
2, 63, 3, 5, 8, 10, 11, 13, 60, 36,
|
||||
2, 34, 3, 5, 8, 10, 11, 13, 28, 49,
|
||||
2, nil, 3, 5, 8, 10, 11, 13, nil, nil,
|
||||
2, nil, 3, 5, 8, 10, 11, 13, nil, 26,
|
||||
42, 43, 3, 5, 37, 38, 40, 21, 44, 37,
|
||||
38, 40, 22, 23, 24, 14, nil, 15, 31, 32,
|
||||
16, nil, 33, 46, 32, nil, nil, 33, 51, 37,
|
||||
38, 40, 58, 37, 38, 40, 37, 38, 40, 37,
|
||||
38, 40, 37, 38, 40, 37, 38, 40, 37, 38,
|
||||
40 ]
|
||||
|
||||
racc_action_check = [
|
||||
0, 4, 27, 4, 0, 0, 0, 0, 36, 56,
|
||||
49, 27, 0, 0, 49, 49, 49, 49, 56, 36,
|
||||
43, 48, 49, 49, 43, 43, 43, 43, 15, 53,
|
||||
6, 15, 43, 43, 6, 6, 6, 6, 53, 52,
|
||||
42, 55, 6, 6, 42, 42, 42, 42, 52, 24,
|
||||
35, 18, 42, 42, 35, 35, 35, 35, 14, 39,
|
||||
19, nil, 35, 35, 19, 19, 19, 19, nil, nil,
|
||||
13, nil, 19, 19, 13, 13, 13, 13, nil, 13,
|
||||
26, 26, 13, 13, 54, 54, 54, 9, 26, 26,
|
||||
26, 26, 9, 9, 9, 2, nil, 2, 17, 17,
|
||||
2, nil, 17, 30, 30, nil, nil, 30, 41, 41,
|
||||
41, 41, 50, 50, 50, 50, 44, 44, 44, 59,
|
||||
59, 59, 58, 58, 58, 51, 51, 51, 62, 62,
|
||||
62 ]
|
||||
|
||||
racc_action_pointer = [
|
||||
-3, nil, 91, nil, 1, nil, 27, nil, nil, 75,
|
||||
nil, nil, nil, 67, 53, 22, nil, 93, 51, 57,
|
||||
nil, nil, nil, nil, 40, nil, 67, 0, nil, nil,
|
||||
98, nil, nil, nil, nil, 47, -1, nil, nil, 46,
|
||||
nil, 87, 37, 17, 94, nil, nil, nil, 12, 7,
|
||||
91, 103, 37, 27, 62, 21, 7, nil, 100, 97,
|
||||
nil, nil, 106, nil, nil, nil, nil, nil ]
|
||||
|
||||
racc_action_default = [
|
||||
-37, -13, -37, -19, -37, -20, -2, -4, -11, -6,
|
||||
-12, -14, -7, -37, -37, -29, -28, -37, -37, -37,
|
||||
-3, -23, -21, -22, -37, -5, -37, -37, -8, -29,
|
||||
-37, -9, -27, -26, 68, -1, -37, -34, -35, -37,
|
||||
-36, -37, -37, -37, -37, -15, -10, -25, -37, -37,
|
||||
-37, -37, -37, -37, -37, -37, -37, -33, -37, -37,
|
||||
-17, -18, -37, -24, -16, -32, -31, -30 ]
|
||||
|
||||
racc_goto_table = [
|
||||
4, 41, 20, 35, 17, 39, 25, nil, nil, nil,
|
||||
nil, nil, nil, 27, nil, nil, 50, 30, nil, 54,
|
||||
nil, nil, nil, nil, nil, 57, 59, nil, nil, 62,
|
||||
nil, 20, nil, 65, 66, nil, nil, 67, nil, nil,
|
||||
nil, nil, 52, 53, nil, nil, nil, nil, nil, 56 ]
|
||||
|
||||
racc_goto_check = [
|
||||
1, 10, 3, 2, 7, 9, 5, nil, nil, nil,
|
||||
nil, nil, nil, 1, nil, nil, 10, 7, nil, 10,
|
||||
nil, nil, nil, nil, nil, 10, 10, nil, nil, 10,
|
||||
nil, 3, nil, 10, 10, nil, nil, 10, nil, nil,
|
||||
nil, nil, 1, 1, nil, nil, nil, nil, nil, 1 ]
|
||||
|
||||
racc_goto_pointer = [
|
||||
nil, 0, -16, -4, nil, -3, nil, 2, nil, -21,
|
||||
-25 ]
|
||||
|
||||
racc_goto_default = [
|
||||
nil, nil, 6, 7, 9, nil, 12, nil, 1, nil,
|
||||
nil ]
|
||||
|
||||
racc_reduce_table = [
|
||||
0, 0, :racc_error,
|
||||
3, 26, :_reduce_1,
|
||||
1, 26, :_reduce_2,
|
||||
2, 27, :_reduce_3,
|
||||
1, 27, :_reduce_4,
|
||||
2, 28, :_reduce_5,
|
||||
1, 28, :_reduce_none,
|
||||
1, 29, :_reduce_none,
|
||||
3, 29, :_reduce_8,
|
||||
3, 29, :_reduce_9,
|
||||
4, 29, :_reduce_10,
|
||||
1, 29, :_reduce_11,
|
||||
1, 29, :_reduce_12,
|
||||
1, 29, :_reduce_13,
|
||||
1, 29, :_reduce_14,
|
||||
3, 31, :_reduce_15,
|
||||
6, 31, :_reduce_16,
|
||||
5, 31, :_reduce_17,
|
||||
5, 31, :_reduce_18,
|
||||
1, 33, :_reduce_none,
|
||||
1, 33, :_reduce_none,
|
||||
1, 30, :_reduce_none,
|
||||
1, 30, :_reduce_none,
|
||||
1, 30, :_reduce_none,
|
||||
5, 30, :_reduce_24,
|
||||
3, 30, :_reduce_25,
|
||||
2, 32, :_reduce_26,
|
||||
2, 32, :_reduce_27,
|
||||
1, 32, :_reduce_none,
|
||||
1, 32, :_reduce_none,
|
||||
4, 34, :_reduce_30,
|
||||
4, 34, :_reduce_31,
|
||||
4, 34, :_reduce_32,
|
||||
3, 34, :_reduce_33,
|
||||
1, 35, :_reduce_34,
|
||||
1, 35, :_reduce_35,
|
||||
1, 35, :_reduce_36 ]
|
||||
|
||||
racc_reduce_n = 37
|
||||
|
||||
racc_shift_n = 68
|
||||
|
||||
racc_token_table = {
|
||||
false => 0,
|
||||
:error => 1,
|
||||
:BAR => 2,
|
||||
:LBRACK => 3,
|
||||
:CTYPE => 4,
|
||||
:RBRACK => 5,
|
||||
:NEGATE => 6,
|
||||
:CCLASS => 7,
|
||||
:DOT => 8,
|
||||
:CHAR => 9,
|
||||
:LPAREN => 10,
|
||||
:RPAREN => 11,
|
||||
:QMARK => 12,
|
||||
:COLON => 13,
|
||||
:NAME => 14,
|
||||
:L_ANCHOR => 15,
|
||||
:R_ANCHOR => 16,
|
||||
:STAR => 17,
|
||||
:PLUS => 18,
|
||||
:LCURLY => 19,
|
||||
:RCURLY => 20,
|
||||
:MINUS => 21,
|
||||
:MULTILINE => 22,
|
||||
:IGNORECASE => 23,
|
||||
:EXTENDED => 24 }
|
||||
|
||||
racc_nt_base = 25
|
||||
|
||||
racc_use_result_var = true
|
||||
|
||||
Racc_arg = [
|
||||
racc_action_table,
|
||||
racc_action_check,
|
||||
racc_action_default,
|
||||
racc_action_pointer,
|
||||
racc_goto_table,
|
||||
racc_goto_check,
|
||||
racc_goto_default,
|
||||
racc_goto_pointer,
|
||||
racc_nt_base,
|
||||
racc_reduce_table,
|
||||
racc_token_table,
|
||||
racc_shift_n,
|
||||
racc_reduce_n,
|
||||
racc_use_result_var ]
|
||||
|
||||
Racc_token_to_s_table = [
|
||||
"$end",
|
||||
"error",
|
||||
"BAR",
|
||||
"LBRACK",
|
||||
"CTYPE",
|
||||
"RBRACK",
|
||||
"NEGATE",
|
||||
"CCLASS",
|
||||
"DOT",
|
||||
"CHAR",
|
||||
"LPAREN",
|
||||
"RPAREN",
|
||||
"QMARK",
|
||||
"COLON",
|
||||
"NAME",
|
||||
"L_ANCHOR",
|
||||
"R_ANCHOR",
|
||||
"STAR",
|
||||
"PLUS",
|
||||
"LCURLY",
|
||||
"RCURLY",
|
||||
"MINUS",
|
||||
"MULTILINE",
|
||||
"IGNORECASE",
|
||||
"EXTENDED",
|
||||
"$start",
|
||||
"expression",
|
||||
"subexpression",
|
||||
"quantified_atom",
|
||||
"atom",
|
||||
"quantifier",
|
||||
"group",
|
||||
"bracket_expression",
|
||||
"anchor",
|
||||
"options",
|
||||
"modifier" ]
|
||||
|
||||
Racc_debug_parser = false
|
||||
|
||||
##### State transition tables end #####
|
||||
|
||||
# reduce 0 omitted
|
||||
|
||||
def _reduce_1(val, _values, result)
|
||||
# TODO remove this conditional by breaking
|
||||
# it into another production
|
||||
if val[0][0].is_a?(Regin::Alternation)
|
||||
alt = val[0][0] + [Expression.new(val[2])]
|
||||
else
|
||||
alt = Alternation.new(val[0], Expression.new(val[2]))
|
||||
end
|
||||
result = Expression.new(alt)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_2(val, _values, result)
|
||||
result = Expression.new(val[0])
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_3(val, _values, result)
|
||||
result = val[0] + [val[1]]
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_4(val, _values, result)
|
||||
result = [val[0]]
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_5(val, _values, result)
|
||||
result = val[0].dup(:quantifier => val[1])
|
||||
result
|
||||
end
|
||||
|
||||
# reduce 6 omitted
|
||||
|
||||
# reduce 7 omitted
|
||||
|
||||
def _reduce_8(val, _values, result)
|
||||
result = CharacterClass.new(val[1])
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_9(val, _values, result)
|
||||
result = CharacterClass.new(val[1])
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_10(val, _values, result)
|
||||
result = CharacterClass.new(val[2], :negate => true)
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_11(val, _values, result)
|
||||
result = CharacterClass.new(val[0])
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_12(val, _values, result)
|
||||
result = CharacterClass.new('.')
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_13(val, _values, result)
|
||||
result = Anchor.new(val[0])
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_14(val, _values, result)
|
||||
result = Character.new(val[0])
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_15(val, _values, result)
|
||||
result = Group.new(val[1], :index => @capture_index_stack.pop)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_16(val, _values, result)
|
||||
result = Group.new(val[4], val[2].merge(:capture => false))
|
||||
@options_stack.pop
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_17(val, _values, result)
|
||||
result = Group.new(val[3], :capture => false);
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_18(val, _values, result)
|
||||
result = Group.new(val[3], :name => val[2], :index => @capture_index_stack.pop);
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# reduce 19 omitted
|
||||
|
||||
# reduce 20 omitted
|
||||
|
||||
# reduce 21 omitted
|
||||
|
||||
# reduce 22 omitted
|
||||
|
||||
# reduce 23 omitted
|
||||
|
||||
def _reduce_24(val, _values, result)
|
||||
result = val.join
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_25(val, _values, result)
|
||||
result = val.join
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_26(val, _values, result)
|
||||
result = val.join
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_27(val, _values, result)
|
||||
result = val.join
|
||||
result
|
||||
end
|
||||
|
||||
# reduce 28 omitted
|
||||
|
||||
# reduce 29 omitted
|
||||
|
||||
def _reduce_30(val, _values, result)
|
||||
@options_stack << result = { val[1] => false, val[2] => false, val[3] => false }
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_31(val, _values, result)
|
||||
@options_stack << result = { val[0] => true, val[2] => false, val[3] => false }
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_32(val, _values, result)
|
||||
@options_stack << result = { val[0] => true, val[1] => true, val[3] => false }
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_33(val, _values, result)
|
||||
@options_stack << result = { val[0] => true, val[1] => true, val[2] => true }
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_34(val, _values, result)
|
||||
result = :multiline
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_35(val, _values, result)
|
||||
result = :ignorecase
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_36(val, _values, result)
|
||||
result = :extended
|
||||
result
|
||||
end
|
||||
|
||||
def _reduce_none(val, _values, result)
|
||||
val[0]
|
||||
end
|
||||
|
||||
end # class Parser
|
||||
end # module Regin
|
||||
|
||||
require 'regin/tokenizer'
|
|
@ -0,0 +1,213 @@
|
|||
#--
|
||||
# DO NOT MODIFY!!!!
|
||||
# This file is automatically generated by rex 1.0.5.beta1
|
||||
# from lexical definition file "lib/regin/tokenizer.rex".
|
||||
#++
|
||||
|
||||
require 'racc/parser'
|
||||
class Regin::Parser < Racc::Parser
|
||||
require 'strscan'
|
||||
|
||||
class ScanError < StandardError ; end
|
||||
|
||||
attr_reader :lineno
|
||||
attr_reader :filename
|
||||
attr_accessor :state
|
||||
|
||||
def scan_setup(str)
|
||||
@ss = StringScanner.new(str)
|
||||
@lineno = 1
|
||||
@state = nil
|
||||
end
|
||||
|
||||
def action
|
||||
yield
|
||||
end
|
||||
|
||||
def scan_str(str)
|
||||
scan_setup(str)
|
||||
do_parse
|
||||
end
|
||||
alias :scan :scan_str
|
||||
|
||||
def load_file( filename )
|
||||
@filename = filename
|
||||
open(filename, "r") do |f|
|
||||
scan_setup(f.read)
|
||||
end
|
||||
end
|
||||
|
||||
def scan_file( filename )
|
||||
load_file(filename)
|
||||
do_parse
|
||||
end
|
||||
|
||||
|
||||
def next_token
|
||||
return if @ss.eos?
|
||||
|
||||
text = @ss.peek(1)
|
||||
@lineno += 1 if text == "\n"
|
||||
token = case @state
|
||||
when nil
|
||||
case
|
||||
when (text = @ss.scan(/\\[dDsSwW]/))
|
||||
action { [:CCLASS, text] }
|
||||
|
||||
when (text = @ss.scan(/\^|\\A/))
|
||||
action { [:L_ANCHOR, text] }
|
||||
|
||||
when (text = @ss.scan(/\$|\\Z/))
|
||||
action { [:R_ANCHOR, text] }
|
||||
|
||||
when (text = @ss.scan(/<(\w+)>/))
|
||||
action { [:NAME, @ss[1]] }
|
||||
|
||||
when (text = @ss.scan(/\(/))
|
||||
action {
|
||||
@capture_index_stack << @capture_index
|
||||
@capture_index += 1
|
||||
@state = :OPTIONS if @ss.peek(1) == '?';
|
||||
[:LPAREN, text]
|
||||
}
|
||||
|
||||
|
||||
when (text = @ss.scan(/\)/))
|
||||
action { [:RPAREN, text] }
|
||||
|
||||
when (text = @ss.scan(/\[/))
|
||||
action { @state = :CCLASS; [:LBRACK, text] }
|
||||
|
||||
when (text = @ss.scan(/\{/))
|
||||
action { [:LCURLY, text] }
|
||||
|
||||
when (text = @ss.scan(/\}/))
|
||||
action { [:RCURLY, text] }
|
||||
|
||||
when (text = @ss.scan(/\|/))
|
||||
action { [:BAR, text] }
|
||||
|
||||
when (text = @ss.scan(/\./))
|
||||
action { [:DOT, text] }
|
||||
|
||||
when (text = @ss.scan(/\?/))
|
||||
action { [:QMARK, text] }
|
||||
|
||||
when (text = @ss.scan(/\+(?:\?)/))
|
||||
action { [:PLUS, text] }
|
||||
|
||||
when (text = @ss.scan(/\*(?:\?)/))
|
||||
action { [:STAR, text] }
|
||||
|
||||
when (text = @ss.scan(/\#/))
|
||||
action {
|
||||
if @options_stack[-1][:extended]
|
||||
@state = :COMMENT;
|
||||
next_token
|
||||
else
|
||||
[:CHAR, text]
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
when (text = @ss.scan(/\s|\n/))
|
||||
action {
|
||||
if @options_stack[-1][:extended]
|
||||
next_token
|
||||
else
|
||||
[:CHAR, text]
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
when (text = @ss.scan(/\\(.)/))
|
||||
action { [:CHAR, @ss[1]] }
|
||||
|
||||
when (text = @ss.scan(/./))
|
||||
action { [:CHAR, text] }
|
||||
|
||||
else
|
||||
text = @ss.string[@ss.pos .. -1]
|
||||
raise ScanError, "can not match: '" + text + "'"
|
||||
end # if
|
||||
|
||||
when :CCLASS
|
||||
case
|
||||
when (text = @ss.scan(/\]/))
|
||||
action { @state = nil; [:RBRACK, text] }
|
||||
|
||||
when (text = @ss.scan(/\^/))
|
||||
action { [:NEGATE, text] }
|
||||
|
||||
when (text = @ss.scan(/:(alnum|alpha|ascii|blank|cntrl|digit|graph|lower|print|punct|space|upper|word|xdigit):/))
|
||||
action { [:CTYPE, text] }
|
||||
|
||||
when (text = @ss.scan(/\\-/))
|
||||
action { [:CHAR, text] }
|
||||
|
||||
when (text = @ss.scan(/\\(.)/))
|
||||
action { [:CHAR, @ss[1]] }
|
||||
|
||||
when (text = @ss.scan(/./))
|
||||
action { [:CHAR, text] }
|
||||
|
||||
else
|
||||
text = @ss.string[@ss.pos .. -1]
|
||||
raise ScanError, "can not match: '" + text + "'"
|
||||
end # if
|
||||
|
||||
when :OPTIONS
|
||||
case
|
||||
when (text = @ss.scan(/\?/))
|
||||
action {
|
||||
@state = nil unless @ss.peek(1) =~ /-|m|i|x|:/
|
||||
[:QMARK, text]
|
||||
}
|
||||
|
||||
|
||||
when (text = @ss.scan(/\-/))
|
||||
action { [:MINUS, text] }
|
||||
|
||||
when (text = @ss.scan(/m/))
|
||||
action { [:MULTILINE, text] }
|
||||
|
||||
when (text = @ss.scan(/i/))
|
||||
action { [:IGNORECASE, text] }
|
||||
|
||||
when (text = @ss.scan(/x/))
|
||||
action { [:EXTENDED, text] }
|
||||
|
||||
when (text = @ss.scan(/\:/))
|
||||
action {
|
||||
@capture_index_stack.pop
|
||||
@capture_index -= 1
|
||||
@state = nil;
|
||||
[:COLON, text]
|
||||
}
|
||||
|
||||
|
||||
else
|
||||
text = @ss.string[@ss.pos .. -1]
|
||||
raise ScanError, "can not match: '" + text + "'"
|
||||
end # if
|
||||
|
||||
when :COMMENT
|
||||
case
|
||||
when (text = @ss.scan(/\n/))
|
||||
action { @state = nil; next_token }
|
||||
|
||||
when (text = @ss.scan(/./))
|
||||
action { next_token }
|
||||
|
||||
else
|
||||
text = @ss.string[@ss.pos .. -1]
|
||||
raise ScanError, "can not match: '" + text + "'"
|
||||
end # if
|
||||
|
||||
else
|
||||
raise ScanError, "undefined state: '" + state.to_s + "'"
|
||||
end # case state
|
||||
token
|
||||
end # def next_token
|
||||
|
||||
end # class
|
|
@ -0,0 +1,3 @@
|
|||
module Regin
|
||||
Version = '0.3.3'
|
||||
end
|
5
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb
vendored
Normal file
5
actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
module Rack
|
||||
module Mount
|
||||
Version = '0.6.6.pre'
|
||||
end
|
||||
end
|
|
@ -178,9 +178,7 @@ module ActionView
|
|||
def flush_output_buffer #:nodoc:
|
||||
if output_buffer && !output_buffer.empty?
|
||||
response.body_parts << output_buffer
|
||||
new = ''
|
||||
new.force_encoding(output_buffer.encoding) if new.respond_to?(:force_encoding)
|
||||
self.output_buffer = new
|
||||
self.output_buffer = output_buffer[0,0]
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
|
@ -529,23 +529,34 @@ module ActionView
|
|||
def html_options_for_form(url_for_options, options, *parameters_for_url)
|
||||
returning options.stringify_keys do |html_options|
|
||||
html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
|
||||
# The following URL is unescaped, this is just a hash of options, and it is the
|
||||
# responsability of the caller to escape all the values.
|
||||
html_options["action"] = url_for(url_for_options, *parameters_for_url)
|
||||
html_options["accept-charset"] = "UTF-8"
|
||||
html_options["data-remote"] = true if html_options.delete("remote")
|
||||
end
|
||||
end
|
||||
|
||||
def extra_tags_for_form(html_options)
|
||||
case method = html_options.delete("method").to_s
|
||||
snowman_tag = tag(:input, :type => "hidden",
|
||||
:name => "_snowman", :value => "☃".html_safe)
|
||||
|
||||
method = html_options.delete("method").to_s
|
||||
|
||||
method_tag = case method
|
||||
when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
|
||||
html_options["method"] = "get"
|
||||
''
|
||||
when /^post$/i, "", nil
|
||||
html_options["method"] = "post"
|
||||
protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0;display:inline') : ''
|
||||
token_tag
|
||||
else
|
||||
html_options["method"] = "post"
|
||||
content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0;display:inline')
|
||||
tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag
|
||||
end
|
||||
|
||||
tags = snowman_tag << method_tag
|
||||
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
|
||||
end
|
||||
|
||||
def form_tag_html(html_options)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
require 'set'
|
||||
require 'active_support/json'
|
||||
require 'active_support/core_ext/object/returning'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionView
|
||||
|
@ -132,7 +131,7 @@ module ActionView
|
|||
|
||||
url_options = options[:url]
|
||||
url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash)
|
||||
function << "'#{escape_javascript(url_for(url_options))}'"
|
||||
function << "'#{html_escape(escape_javascript(url_for(url_options)))}'"
|
||||
function << ", #{javascript_options})"
|
||||
|
||||
function = "#{options[:before]}; #{function}" if options[:before]
|
||||
|
@ -228,7 +227,7 @@ module ActionView
|
|||
# <script> tag.
|
||||
module GeneratorMethods
|
||||
def to_s #:nodoc:
|
||||
returning javascript = @lines * $/ do
|
||||
(@lines * $/).tap do |javascript|
|
||||
if ActionView::Base.debug_rjs
|
||||
source = javascript.dup
|
||||
javascript.replace "try {\n#{source}\n} catch (e) "
|
||||
|
@ -530,9 +529,9 @@ module ActionView
|
|||
end
|
||||
|
||||
def record(line)
|
||||
returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do
|
||||
line = "#{line.to_s.chomp.gsub(/\;\z/, '')};"
|
||||
self << line
|
||||
end
|
||||
line
|
||||
end
|
||||
|
||||
def render(*options)
|
||||
|
|
|
@ -122,7 +122,7 @@ module ActionView
|
|||
attrs << %(#{key}="#{key}") if value
|
||||
elsif !value.nil?
|
||||
final_value = value.is_a?(Array) ? value.join(" ") : value
|
||||
final_value = escape_once(final_value) if escape
|
||||
final_value = html_escape(final_value) if escape
|
||||
attrs << %(#{key}="#{final_value}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,9 +38,6 @@ module ActionView
|
|||
# <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
|
||||
# instead of the fully qualified URL like "http://example.com/controller/action".
|
||||
#
|
||||
# When called from a view, +url_for+ returns an HTML escaped url. If you
|
||||
# need an unescaped url, pass <tt>:escape => false</tt> in the +options+.
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
|
||||
# * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
|
||||
|
@ -50,7 +47,6 @@ module ActionView
|
|||
# * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
|
||||
# * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
|
||||
# * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
|
||||
# * <tt>:escape</tt> - Determines whether the returned URL will be HTML escaped or not (<tt>true</tt> by default).
|
||||
#
|
||||
# ==== Relying on named routes
|
||||
#
|
||||
|
@ -72,10 +68,7 @@ module ActionView
|
|||
# <%= url_for(:action => 'play', :anchor => 'player') %>
|
||||
# # => /messages/play/#player
|
||||
#
|
||||
# <%= url_for(:action => 'checkout', :anchor => 'tax&ship') %>
|
||||
# # => /testing/jump/#tax&ship
|
||||
#
|
||||
# <%= url_for(:action => 'checkout', :anchor => 'tax&ship', :escape => false) %>
|
||||
# <%= url_for(:action => 'jump', :anchor => 'tax&ship') %>
|
||||
# # => /testing/jump/#tax&ship
|
||||
#
|
||||
# <%= url_for(Workshop.new) %>
|
||||
|
@ -100,21 +93,17 @@ module ActionView
|
|||
options ||= {}
|
||||
url = case options
|
||||
when String
|
||||
escape = true
|
||||
options
|
||||
when Hash
|
||||
options = { :only_path => options[:host].nil? }.update(options.symbolize_keys)
|
||||
escape = options.key?(:escape) ? options.delete(:escape) : false
|
||||
super
|
||||
when :back
|
||||
escape = false
|
||||
controller.request.env["HTTP_REFERER"] || 'javascript:history.back()'
|
||||
else
|
||||
escape = false
|
||||
polymorphic_path(options)
|
||||
end
|
||||
|
||||
escape ? escape_once(url).html_safe : url
|
||||
url
|
||||
end
|
||||
|
||||
# Creates a link tag of the given +name+ using a URL created by the set
|
||||
|
@ -254,8 +243,8 @@ module ActionView
|
|||
tag_options = nil
|
||||
end
|
||||
|
||||
href_attr = "href=\"#{url}\"" unless href
|
||||
"<a #{href_attr}#{tag_options}>#{ERB::Util.h(name || url)}</a>".html_safe
|
||||
href_attr = "href=\"#{html_escape(url)}\"" unless href
|
||||
"<a #{href_attr}#{tag_options}>#{html_escape(name || url)}</a>".html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -339,7 +328,7 @@ module ActionView
|
|||
|
||||
html_options.merge!("type" => "submit", "value" => name)
|
||||
|
||||
("<form method=\"#{form_method}\" action=\"#{escape_once url}\" #{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" +
|
||||
("<form method=\"#{form_method}\" action=\"#{html_escape(url)}\" #{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" +
|
||||
method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe
|
||||
end
|
||||
|
||||
|
@ -485,24 +474,27 @@ module ActionView
|
|||
# :subject => "This is an example email"
|
||||
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
|
||||
def mail_to(email_address, name = nil, html_options = {})
|
||||
email_address = html_escape(email_address)
|
||||
|
||||
html_options = html_options.stringify_keys
|
||||
encode = html_options.delete("encode").to_s
|
||||
cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body")
|
||||
|
||||
string = ''
|
||||
extras = ''
|
||||
extras << "cc=#{Rack::Utils.escape(cc).gsub("+", "%20")}&" unless cc.nil?
|
||||
extras << "bcc=#{Rack::Utils.escape(bcc).gsub("+", "%20")}&" unless bcc.nil?
|
||||
extras << "body=#{Rack::Utils.escape(body).gsub("+", "%20")}&" unless body.nil?
|
||||
extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}&" unless subject.nil?
|
||||
extras = "?" << extras.gsub!(/&?$/,"") unless extras.empty?
|
||||
extras = []
|
||||
extras << "cc=#{Rack::Utils.escape(cc).gsub("+", "%20")}" unless cc.nil?
|
||||
extras << "bcc=#{Rack::Utils.escape(bcc).gsub("+", "%20")}" unless bcc.nil?
|
||||
extras << "body=#{Rack::Utils.escape(body).gsub("+", "%20")}" unless body.nil?
|
||||
extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}" unless subject.nil?
|
||||
extras = extras.empty? ? '' : '?' + html_escape(extras.join('&'))
|
||||
|
||||
email_address_obfuscated = html_escape(email_address)
|
||||
email_address_obfuscated = email_address.dup
|
||||
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
|
||||
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
|
||||
|
||||
string = ''
|
||||
|
||||
if encode == "javascript"
|
||||
"document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
|
||||
"document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))}');".each_byte do |c|
|
||||
string << sprintf("%%%x", c)
|
||||
end
|
||||
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe
|
||||
|
@ -519,9 +511,9 @@ module ActionView
|
|||
char = c.chr
|
||||
string << (char =~ /\w/ ? sprintf("%%%x", c) : char)
|
||||
end
|
||||
content_tag "a", name || email_address_encoded.html_safe, html_options.merge({ "href" => "#{string}#{extras}" })
|
||||
content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe)
|
||||
else
|
||||
content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
|
||||
content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -574,7 +566,7 @@ module ActionView
|
|||
"in a #request method"
|
||||
end
|
||||
|
||||
url_string = CGI.unescapeHTML(url_for(options))
|
||||
url_string = url_for(options)
|
||||
|
||||
# We ignore any extra parameters in the request_uri if the
|
||||
# submitted url doesn't have any either. This lets the function
|
||||
|
|
|
@ -12,8 +12,9 @@ module ActionView
|
|||
alias :render_partial :render_template
|
||||
alias :render_collection :render_template
|
||||
|
||||
# TODO: Ideally, ActionView should have its own logger so it does not depend on AC.logger
|
||||
def logger
|
||||
ActionController::Base.logger
|
||||
ActionController::Base.logger if defined?(ActionController::Base)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -156,6 +156,7 @@ module ActionView
|
|||
end
|
||||
|
||||
def inspect
|
||||
@inspect ||=
|
||||
if defined?(Rails.root)
|
||||
identifier.sub("#{Rails.root}/", '')
|
||||
else
|
||||
|
@ -267,9 +268,11 @@ module ActionView
|
|||
end
|
||||
|
||||
def build_method_name(locals)
|
||||
# TODO: is locals.keys.hash reliably the same?
|
||||
@method_names[locals.keys.hash] ||=
|
||||
"_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
|
||||
@method_names[locals.keys.hash] ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
|
||||
end
|
||||
|
||||
def identifier_method_name
|
||||
@identifier_method_name ||= inspect.gsub(/[^a-z_]/, '_')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -181,7 +181,7 @@ module ActionView
|
|||
}
|
||||
|
||||
def _instance_variables
|
||||
instance_variables - EXCLUDE_IVARS
|
||||
instance_variables.map(&:to_s) - EXCLUDE_IVARS
|
||||
end
|
||||
|
||||
def _assigns
|
||||
|
|
|
@ -38,6 +38,17 @@ end
|
|||
|
||||
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
|
||||
|
||||
module Rails
|
||||
end
|
||||
|
||||
# Monkey patch the old router initialization to be silenced.
|
||||
class ActionDispatch::Routing::DeprecatedMapper
|
||||
def initialize_with_silencer(*args)
|
||||
ActiveSupport::Deprecation.silence { initialize_without_silencer(*args) }
|
||||
end
|
||||
alias_method_chain :initialize, :silencer
|
||||
end
|
||||
|
||||
ActiveSupport::Dependencies.hook!
|
||||
|
||||
# Show backtraces for deprecated behavior for quicker cleanup.
|
||||
|
|
|
@ -408,18 +408,18 @@ class PolymorphicRoutesTest < ActionController::TestCase
|
|||
|
||||
def with_admin_test_routes(options = {})
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.namespace :admin do |admin|
|
||||
admin.resources :projects do |projects|
|
||||
projects.resources :tasks
|
||||
projects.resource :bid do |bid|
|
||||
bid.resources :tasks
|
||||
set.draw do
|
||||
namespace :admin do
|
||||
resources :projects do
|
||||
resources :tasks
|
||||
resource :bid do
|
||||
resources :tasks
|
||||
end
|
||||
end
|
||||
admin.resources :taxes do |taxes|
|
||||
taxes.resources :faxes
|
||||
resources :taxes do
|
||||
resources :faxes
|
||||
end
|
||||
admin.resources :series
|
||||
resources :series
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -430,12 +430,12 @@ class PolymorphicRoutesTest < ActionController::TestCase
|
|||
|
||||
def with_admin_and_site_test_routes(options = {})
|
||||
with_routing do |set|
|
||||
set.draw do |map|
|
||||
map.namespace :admin do |admin|
|
||||
admin.resources :projects do |projects|
|
||||
projects.namespace :site do |site|
|
||||
site.resources :tasks do |tasks|
|
||||
tasks.resources :steps
|
||||
set.draw do
|
||||
namespace :admin do
|
||||
resources :projects do
|
||||
namespace :site do
|
||||
resources :tasks do
|
||||
resources :steps
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,6 @@ require 'abstract_unit'
|
|||
require 'logger'
|
||||
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
|
||||
|
||||
module Rails
|
||||
end
|
||||
|
||||
# Provide some controller to run the tests on.
|
||||
module Submodule
|
||||
class ContainedEmptyController < ActionController::Base
|
||||
|
|
|
@ -89,7 +89,12 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
|
|||
assert_equal 19512, file.size
|
||||
end
|
||||
|
||||
# Pending fix in Rack 1.2.2
|
||||
# http://rack.lighthouseapp.com/projects/22435-rack/tickets/79-multipart-handling-incorrectly-assuming-file-upload
|
||||
test "parses mixed files" do
|
||||
if Rack.release <= '1.2.1'
|
||||
$stderr.puts 'multipart/mixed parsing pending fix in Rack 1.2.2'
|
||||
else
|
||||
params = parse_multipart('mixed_files')
|
||||
assert_equal %w(files foo), params.keys.sort
|
||||
assert_equal 'bar', params['foo']
|
||||
|
@ -99,6 +104,7 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest
|
|||
files.force_encoding('ASCII-8BIT') if files.respond_to?(:force_encoding)
|
||||
assert_equal 19756, files.size
|
||||
end
|
||||
end
|
||||
|
||||
test "does not create tempfile if no file has been selected" do
|
||||
params = parse_multipart('none')
|
||||
|
|
|
@ -141,6 +141,29 @@ class UrlEncodedParamsParsingTest < ActionController::IntegrationTest
|
|||
post "/parse", actual
|
||||
assert_response :ok
|
||||
assert_equal(expected, TestController.last_request_parameters)
|
||||
assert_utf8(TestController.last_request_parameters)
|
||||
end
|
||||
end
|
||||
|
||||
def assert_utf8(object)
|
||||
return unless "ruby".encoding_aware?
|
||||
|
||||
correct_encoding = Encoding.default_internal
|
||||
|
||||
unless object.is_a?(Hash)
|
||||
assert_equal correct_encoding, object.encoding, "#{object.inspect} should have been UTF-8"
|
||||
return
|
||||
end
|
||||
|
||||
object.each do |k,v|
|
||||
case v
|
||||
when Hash
|
||||
assert_utf8(v)
|
||||
when Array
|
||||
v.each {|el| assert_utf8(el) }
|
||||
else
|
||||
assert_utf8(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,6 +35,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
|||
end
|
||||
end
|
||||
|
||||
scope "bookmark", :controller => "bookmarks", :as => :bookmark do
|
||||
get :new, :path => "build"
|
||||
post :create, :path => "create", :as => ""
|
||||
put :update
|
||||
get "remove", :action => :destroy, :as => :remove
|
||||
end
|
||||
|
||||
match 'account/logout' => redirect("/logout"), :as => :logout_redirect
|
||||
match 'account/login', :to => redirect("/login")
|
||||
|
||||
|
@ -330,6 +337,17 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
|||
|
||||
resources :content
|
||||
|
||||
scope :constraints => { :id => /\d+/ } do
|
||||
get '/tickets', :to => 'tickets#index', :as => :tickets
|
||||
end
|
||||
|
||||
scope :constraints => { :id => /\d{4}/ } do
|
||||
resources :movies do
|
||||
resources :reviews
|
||||
resource :trailer
|
||||
end
|
||||
end
|
||||
|
||||
match '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/
|
||||
end
|
||||
end
|
||||
|
@ -534,6 +552,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
|||
end
|
||||
end
|
||||
|
||||
def test_bookmarks
|
||||
with_test_routes do
|
||||
get '/bookmark/build'
|
||||
assert_equal 'bookmarks#new', @response.body
|
||||
assert_equal '/bookmark/build', new_bookmark_path
|
||||
|
||||
post '/bookmark/create'
|
||||
assert_equal 'bookmarks#create', @response.body
|
||||
assert_equal '/bookmark/create', bookmark_path
|
||||
|
||||
put '/bookmark'
|
||||
assert_equal 'bookmarks#update', @response.body
|
||||
assert_equal '/bookmark', update_bookmark_path
|
||||
|
||||
get '/bookmark/remove'
|
||||
assert_equal 'bookmarks#destroy', @response.body
|
||||
assert_equal '/bookmark/remove', bookmark_remove_path
|
||||
end
|
||||
end
|
||||
|
||||
def test_admin
|
||||
with_test_routes do
|
||||
get '/admin', {}, {'REMOTE_ADDR' => '192.168.1.100'}
|
||||
|
@ -1546,6 +1584,50 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
|
|||
end
|
||||
end
|
||||
|
||||
def test_router_removes_invalid_conditions
|
||||
with_test_routes do
|
||||
get '/tickets'
|
||||
assert_equal 'tickets#index', @response.body
|
||||
assert_equal '/tickets', tickets_path
|
||||
end
|
||||
end
|
||||
|
||||
def test_constraints_are_merged_from_scope
|
||||
with_test_routes do
|
||||
get '/movies/0001'
|
||||
assert_equal 'movies#show', @response.body
|
||||
assert_equal '/movies/0001', movie_path(:id => '0001')
|
||||
|
||||
get '/movies/00001'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raises(ActionController::RoutingError){ movie_path(:id => '00001') }
|
||||
|
||||
get '/movies/0001/reviews'
|
||||
assert_equal 'reviews#index', @response.body
|
||||
assert_equal '/movies/0001/reviews', movie_reviews_path(:movie_id => '0001')
|
||||
|
||||
get '/movies/00001/reviews'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raises(ActionController::RoutingError){ movie_reviews_path(:movie_id => '00001') }
|
||||
|
||||
get '/movies/0001/reviews/0001'
|
||||
assert_equal 'reviews#show', @response.body
|
||||
assert_equal '/movies/0001/reviews/0001', movie_review_path(:movie_id => '0001', :id => '0001')
|
||||
|
||||
get '/movies/00001/reviews/0001'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raises(ActionController::RoutingError){ movie_path(:movie_id => '00001', :id => '00001') }
|
||||
|
||||
get '/movies/0001/trailer'
|
||||
assert_equal 'trailers#show', @response.body
|
||||
assert_equal '/movies/0001/trailer', movie_trailer_path(:movie_id => '0001')
|
||||
|
||||
get '/movies/00001/trailer'
|
||||
assert_equal 'Not Found', @response.body
|
||||
assert_raises(ActionController::RoutingError){ movie_trailer_path(:movie_id => '00001') }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_test_routes
|
||||
yield
|
||||
|
|
|
@ -5,7 +5,7 @@ module ERBTest
|
|||
class TagHelperTest < BlockTestCase
|
||||
test "form_for works" do
|
||||
output = render_content "form_for(:staticpage, :url => {:controller => 'blah', :action => 'update'})", ""
|
||||
assert_equal "<form action=\"/blah/update\" method=\"post\"></form>", output
|
||||
assert_match %r{<form.*action="/blah/update".*method="post">.*</form>}, output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,8 +28,8 @@ module ERBTest
|
|||
end
|
||||
|
||||
test "percent equals works with form tags" do
|
||||
expected_output = "<form action=\"foo\" method=\"post\">hello</form>"
|
||||
maybe_deprecated { assert_equal expected_output, render_content("form_tag('foo')", "<%= 'hello' %>") }
|
||||
expected_output = %r{<form.*action="foo".*method="post">.*hello*</form>}
|
||||
maybe_deprecated { assert_match expected_output, render_content("form_tag('foo')", "<%= 'hello' %>") }
|
||||
end
|
||||
|
||||
test "percent equals works with fieldset tags" do
|
||||
|
|
|
@ -583,7 +583,8 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' id='create-post' method='post'>" +
|
||||
"<form accept-charset='UTF-8' action='http://www.example.com' id='create-post' method='post'>" +
|
||||
snowman +
|
||||
"<label for='post_title'>The Title</label>" +
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
|
@ -604,15 +605,14 @@ class FormHelperTest < ActionView::TestCase
|
|||
concat f.submit('Create post')
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form class='other_name_edit' method='post' action='/posts/123' id='create-post'>" +
|
||||
"<div style='margin:0;padding:0;display:inline'><input name='_method' value='put' type='hidden' /></div>" +
|
||||
expected = whole_form("/posts/123", "create-post", "other_name_edit", :method => "put") do
|
||||
"<label for='other_name_title'>Title</label>" +
|
||||
"<input name='other_name[title]' size='30' id='other_name_title' value='Hello World' type='text' />" +
|
||||
"<textarea name='other_name[body]' id='other_name_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='other_name[secret]' value='0' type='hidden' />" +
|
||||
"<input name='other_name[secret]' checked='checked' id='other_name_secret' value='1' type='checkbox' />" +
|
||||
"<input name='commit' id='other_name_submit' value='Create post' type='submit' /></form>"
|
||||
"<input name='commit' id='other_name_submit' value='Create post' type='submit' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -626,14 +626,12 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' id='create-post' method='post'>" +
|
||||
"<div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div>" +
|
||||
expected = whole_form("http://www.example.com", "create-post", nil, "put") do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[secret]' type='hidden' value='0' />" +
|
||||
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
|
||||
"</form>"
|
||||
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -647,14 +645,12 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' id='create-post' method='post' data-remote='true'>" +
|
||||
"<div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put' /></div>" +
|
||||
expected = whole_form("http://www.example.com", "create-post", nil, :method => "put", :remote => true) do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[secret]' type='hidden' value='0' />" +
|
||||
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
|
||||
"</form>"
|
||||
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -668,13 +664,12 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' method='post' data-remote='true'>" +
|
||||
expected = whole_form("http://www.example.com", nil, nil, :remote => true) do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[secret]' type='hidden' value='0' />" +
|
||||
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
|
||||
"</form>"
|
||||
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -686,13 +681,12 @@ class FormHelperTest < ActionView::TestCase
|
|||
concat f.check_box(:secret)
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' id='create-post' method='post'>" +
|
||||
expected = whole_form("http://www.example.com", "create-post") do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[secret]' type='hidden' value='0' />" +
|
||||
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" +
|
||||
"</form>"
|
||||
"<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -707,14 +701,13 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' method='post'>" +
|
||||
expected = whole_form do
|
||||
"<label for='post_123_title'>Title</label>" +
|
||||
"<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" +
|
||||
"<textarea name='post[123][body]' id='post_123_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[123][secret]' type='hidden' value='0' />" +
|
||||
"<input name='post[123][secret]' checked='checked' type='checkbox' id='post_123_secret' value='1' />" +
|
||||
"</form>"
|
||||
"<input name='post[123][secret]' checked='checked' type='checkbox' id='post_123_secret' value='1' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -728,13 +721,12 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' method='post'>" +
|
||||
expected = whole_form do
|
||||
"<input name='post[][title]' size='30' type='text' id='post__title' value='Hello World' />" +
|
||||
"<textarea name='post[][body]' id='post__body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[][secret]' type='hidden' value='0' />" +
|
||||
"<input name='post[][secret]' checked='checked' type='checkbox' id='post__secret' value='1' />" +
|
||||
"</form>"
|
||||
"<input name='post[][secret]' checked='checked' type='checkbox' id='post__secret' value='1' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -749,9 +741,10 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='commit' id='post_submit' type='submit' value='Create Post' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='commit' id='post_submit' type='submit' value='Create Post' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
ensure
|
||||
I18n.locale = old_locale
|
||||
|
@ -766,9 +759,10 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='commit' id='post_submit' type='submit' value='Confirm Post changes' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='commit' id='post_submit' type='submit' value='Confirm Post changes' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
ensure
|
||||
I18n.locale = old_locale
|
||||
|
@ -781,9 +775,10 @@ class FormHelperTest < ActionView::TestCase
|
|||
concat f.submit :class => "extra"
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='commit' class='extra' id='post_submit' type='submit' value='Save changes' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='commit' class='extra' id='post_submit' type='submit' value='Save changes' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
ensure
|
||||
I18n.locale = old_locale
|
||||
|
@ -798,9 +793,10 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='commit' id='another_post_submit' type='submit' value='Update your Post' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='commit' id='another_post_submit' type='submit' value='Update your Post' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
ensure
|
||||
I18n.locale = old_locale
|
||||
|
@ -815,9 +811,9 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='post[comment][title]' size='30' type='text' id='post_comment_title' value='Hello World' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='post[comment][title]' size='30' type='text' id='post_comment_title' value='Hello World' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -832,10 +828,10 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
expected = whole_form do
|
||||
"<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" +
|
||||
"<input name='post[123][comment][][name]' size='30' type='text' id='post_123_comment__name' value='new comment' />" +
|
||||
"</form>"
|
||||
"<input name='post[123][comment][][name]' size='30' type='text' id='post_123_comment__name' value='new comment' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -850,10 +846,10 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
expected = whole_form do
|
||||
"<input name='post[1][title]' size='30' type='text' id='post_1_title' value='Hello World' />" +
|
||||
"<input name='post[1][comment][1][name]' size='30' type='text' id='post_1_comment_1_name' value='new comment' />" +
|
||||
"</form>"
|
||||
"<input name='post[1][comment][1][name]' size='30' type='text' id='post_1_comment_1_name' value='new comment' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -867,9 +863,9 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='post[1][comment][title]' size='30' type='text' id='post_1_comment_title' value='Hello World' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='post[1][comment][title]' size='30' type='text' id='post_1_comment_title' value='Hello World' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -883,9 +879,9 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='post[1][comment][5][title]' size='30' type='text' id='post_1_comment_5_title' value='Hello World' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='post[1][comment][5][title]' size='30' type='text' id='post_1_comment_5_title' value='Hello World' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -899,9 +895,9 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='post[123][comment][title]' size='30' type='text' id='post_123_comment_title' value='Hello World' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='post[123][comment][title]' size='30' type='text' id='post_123_comment_title' value='Hello World' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -915,9 +911,9 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='post[comment][5][title]' type='radio' id='post_comment_5_title_hello' value='hello' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='post[comment][5][title]' type='radio' id='post_comment_5_title_hello' value='hello' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -931,9 +927,9 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='post[123][comment][123][title]' size='30' type='text' id='post_123_comment_123_title' value='Hello World' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='post[123][comment][123][title]' size='30' type='text' id='post_123_comment_123_title' value='Hello World' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -952,12 +948,11 @@ class FormHelperTest < ActionView::TestCase
|
|||
}
|
||||
end
|
||||
|
||||
expected = "<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='post[123][comment][5][title]' size='30' type='text' id='post_123_comment_5_title' value='Hello World' />" +
|
||||
"</form>" +
|
||||
"<form action='http://www.example.com' method='post'>" +
|
||||
"<input name='post[1][comment][123][title]' size='30' type='text' id='post_1_comment_123_title' value='Hello World' />" +
|
||||
"</form>"
|
||||
expected = whole_form do
|
||||
"<input name='post[123][comment][5][title]' size='30' type='text' id='post_123_comment_5_title' value='Hello World' />"
|
||||
end + whole_form do
|
||||
"<input name='post[1][comment][123][title]' size='30' type='text' id='post_1_comment_123_title' value='Hello World' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -975,10 +970,10 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="new author" />' +
|
||||
'</form>'
|
||||
'<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="new author" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1006,11 +1001,11 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' +
|
||||
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
|
||||
'</form>'
|
||||
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1028,11 +1023,11 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' +
|
||||
'<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' +
|
||||
'</form>'
|
||||
'<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1051,13 +1046,13 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' +
|
||||
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' +
|
||||
'<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
|
||||
'</form>'
|
||||
'<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1077,13 +1072,13 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
|
||||
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' +
|
||||
'<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' +
|
||||
'</form>'
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1102,11 +1097,11 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="new comment" />' +
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' +
|
||||
'</form>'
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1125,12 +1120,12 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' +
|
||||
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' +
|
||||
'</form>'
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1145,9 +1140,9 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'</form>'
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1164,13 +1159,13 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' +
|
||||
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' +
|
||||
'<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
|
||||
'</form>'
|
||||
'<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1188,13 +1183,13 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' +
|
||||
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' +
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' +
|
||||
'<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' +
|
||||
'</form>'
|
||||
'<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1213,12 +1208,12 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' +
|
||||
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' +
|
||||
'<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' +
|
||||
'</form>'
|
||||
'<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
assert_equal yielded_comments, @post.comments
|
||||
|
@ -1235,10 +1230,10 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" size="30" type="text" value="comment #321" />' +
|
||||
'<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />' +
|
||||
'</form>'
|
||||
'<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1273,7 +1268,7 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected = '<form action="http://www.example.com" method="post">' +
|
||||
expected = whole_form do
|
||||
'<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' +
|
||||
'<input id="post_comments_attributes_0_relevances_attributes_0_value" name="post[comments_attributes][0][relevances_attributes][0][value]" size="30" type="text" value="commentrelevance #314" />' +
|
||||
'<input id="post_comments_attributes_0_relevances_attributes_0_id" name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' +
|
||||
|
@ -1285,8 +1280,8 @@ class FormHelperTest < ActionView::TestCase
|
|||
'<input id="post_tags_attributes_1_value" name="post[tags_attributes][1][value]" size="30" type="text" value="tag #456" />' +
|
||||
'<input id="post_tags_attributes_1_relevances_attributes_0_value" name="post[tags_attributes][1][relevances_attributes][0][value]" size="30" type="text" value="tagrelevance #31415" />' +
|
||||
'<input id="post_tags_attributes_1_relevances_attributes_0_id" name="post[tags_attributes][1][relevances_attributes][0][id]" type="hidden" value="31415" />' +
|
||||
'<input id="post_tags_attributes_1_id" name="post[tags_attributes][1][id]" type="hidden" value="456" />' +
|
||||
'</form>'
|
||||
'<input id="post_tags_attributes_1_id" name="post[tags_attributes][1][id]" type="hidden" value="456" />'
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1426,7 +1421,8 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' id='create-post' method='post'>" +
|
||||
"<form accept-charset='UTF-8' action='http://www.example.com' id='create-post' method='post'>" +
|
||||
snowman +
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='parent_post[secret]' type='hidden' value='0' />" +
|
||||
|
@ -1449,11 +1445,11 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' id='create-post' method='post'>" +
|
||||
whole_form("http://www.example.com", "create-post") do
|
||||
"<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" +
|
||||
"<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
|
||||
"<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' size='30' />" +
|
||||
"</form>"
|
||||
"<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' size='30' />"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1477,16 +1473,42 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' method='post'>" +
|
||||
expected = whole_form do
|
||||
"<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" +
|
||||
"<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" +
|
||||
"<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>" +
|
||||
"</form>"
|
||||
"<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def snowman(method = nil)
|
||||
txt = %{<div style="margin:0;padding:0;display:inline">}
|
||||
txt << %{<input name="_snowman" type="hidden" value="☃" />}
|
||||
txt << %{<input name="_method" type="hidden" value="#{method}" />} if method
|
||||
txt << %{</div>}
|
||||
end
|
||||
|
||||
def form_text(action = "http://www.example.com", id = nil, html_class = nil, remote = nil)
|
||||
txt = %{<form accept-charset="UTF-8" action="#{action}"}
|
||||
txt << %{ data-remote="true"} if remote
|
||||
txt << %{ class="#{html_class}"} if html_class
|
||||
txt << %{ id="#{id}"} if id
|
||||
txt << %{ method="post">}
|
||||
end
|
||||
|
||||
def whole_form(action = "http://www.example.com", id = nil, html_class = nil, options = nil)
|
||||
contents = block_given? ? yield : ""
|
||||
|
||||
if options.is_a?(Hash)
|
||||
method, remote = options.values_at(:method, :remote)
|
||||
else
|
||||
method = options
|
||||
end
|
||||
|
||||
form_text(action, id, html_class, remote) + snowman(method) + contents + "</form>"
|
||||
end
|
||||
|
||||
def test_default_form_builder
|
||||
old_default_form_builder, ActionView::Base.default_form_builder =
|
||||
ActionView::Base.default_form_builder, LabelledFormBuilder
|
||||
|
@ -1499,12 +1521,11 @@ class FormHelperTest < ActionView::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
expected =
|
||||
"<form action='http://www.example.com' method='post'>" +
|
||||
expected = whole_form do
|
||||
"<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" +
|
||||
"<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" +
|
||||
"<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>" +
|
||||
"</form>"
|
||||
"<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>"
|
||||
end
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
ensure
|
||||
|
@ -1577,7 +1598,7 @@ class FormHelperTest < ActionView::TestCase
|
|||
assert_deprecated do
|
||||
form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end
|
||||
end
|
||||
expected = "<form action=\"http://www.example.com\" class=\"some_class\" id=\"some_form\" method=\"post\"></form>"
|
||||
expected = whole_form("http://www.example.com", "some_form", "some_class")
|
||||
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
@ -1587,7 +1608,8 @@ class FormHelperTest < ActionView::TestCase
|
|||
form_for(:post, @post, :url => 'http://www.otherdomain.com') do |f| end
|
||||
end
|
||||
|
||||
assert_equal '<form action="http://www.otherdomain.com" method="post"></form>', output_buffer
|
||||
assert_equal whole_form("http://www.otherdomain.com"), output_buffer
|
||||
# assert_equal '<form action="http://www.otherdomain.com" method="post"></form>', output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_hash_url_option
|
||||
|
@ -1604,14 +1626,15 @@ class FormHelperTest < ActionView::TestCase
|
|||
form_for(:post, @post, :url => @post) do |f| end
|
||||
end
|
||||
|
||||
expected = "<form action=\"/posts/123\" method=\"post\"></form>"
|
||||
expected = whole_form("/posts/123")
|
||||
# expected = "<form action=\"/posts/123\" method=\"post\"></form>"
|
||||
assert_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_existing_object
|
||||
form_for(@post) do |f| end
|
||||
|
||||
expected = "<form action=\"/posts/123\" class=\"edit_post\" id=\"edit_post_123\" method=\"post\"><div style=\"margin:0;padding:0;display:inline\"><input name=\"_method\" type=\"hidden\" value=\"put\" /></div></form>"
|
||||
expected = whole_form("/posts/123", "edit_post_123", "edit_post", "put")
|
||||
assert_equal expected, output_buffer
|
||||
end
|
||||
|
||||
|
@ -1622,7 +1645,7 @@ class FormHelperTest < ActionView::TestCase
|
|||
|
||||
form_for(post) do |f| end
|
||||
|
||||
expected = "<form action=\"/posts\" class=\"new_post\" id=\"new_post\" method=\"post\"></form>"
|
||||
expected = whole_form("/posts", "new_post", "new_post")
|
||||
assert_equal expected, output_buffer
|
||||
end
|
||||
|
||||
|
@ -1630,14 +1653,14 @@ class FormHelperTest < ActionView::TestCase
|
|||
@comment.save
|
||||
form_for([@post, @comment]) {}
|
||||
|
||||
expected = %(<form action="#{comment_path(@post, @comment)}" class="edit_comment" id="edit_comment_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="_method" type="hidden" value="put" /></div></form>)
|
||||
expected = whole_form(comment_path(@post, @comment), "edit_comment_1", "edit_comment", "put")
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_new_object_in_list
|
||||
form_for([@post, @comment]) {}
|
||||
|
||||
expected = %(<form action="#{comments_path(@post)}" class="new_comment" id="new_comment" method="post"></form>)
|
||||
expected = whole_form(comments_path(@post), "new_comment", "new_comment")
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
|
@ -1645,21 +1668,21 @@ class FormHelperTest < ActionView::TestCase
|
|||
@comment.save
|
||||
form_for([:admin, @post, @comment]) {}
|
||||
|
||||
expected = %(<form action="#{admin_comment_path(@post, @comment)}" class="edit_comment" id="edit_comment_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="_method" type="hidden" value="put" /></div></form>)
|
||||
expected = whole_form(admin_comment_path(@post, @comment), "edit_comment_1", "edit_comment", "put")
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_new_object_and_namespace_in_list
|
||||
form_for([:admin, @post, @comment]) {}
|
||||
|
||||
expected = %(<form action="#{admin_comments_path(@post)}" class="new_comment" id="new_comment" method="post"></form>)
|
||||
expected = whole_form(admin_comments_path(@post), "new_comment", "new_comment")
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_for_with_existing_object_and_custom_url
|
||||
form_for(@post, :url => "/super_posts") do |f| end
|
||||
|
||||
expected = "<form action=\"/super_posts\" class=\"edit_post\" id=\"edit_post_123\" method=\"post\"><div style=\"margin:0;padding:0;display:inline\"><input name=\"_method\" type=\"hidden\" value=\"put\" /></div></form>"
|
||||
expected = whole_form("/super_posts", "edit_post_123", "edit_post", "put")
|
||||
assert_equal expected, output_buffer
|
||||
end
|
||||
|
||||
|
|
|
@ -8,6 +8,36 @@ class FormTagHelperTest < ActionView::TestCase
|
|||
@controller = BasicController.new
|
||||
end
|
||||
|
||||
def snowman(options = {})
|
||||
method = options[:method]
|
||||
|
||||
txt = %{<div style="margin:0;padding:0;display:inline">}
|
||||
txt << %{<input name="_snowman" type="hidden" value="☃" />}
|
||||
txt << %{<input name="_method" type="hidden" value="#{method}" />} if method
|
||||
txt << %{</div>}
|
||||
end
|
||||
|
||||
def form_text(action = "http://www.example.com", options = {})
|
||||
remote, enctype, html_class, id = options.values_at(:remote, :enctype, :html_class, :id)
|
||||
|
||||
txt = %{<form accept-charset="UTF-8" action="#{action}"}
|
||||
txt << %{ enctype="multipart/form-data"} if enctype
|
||||
txt << %{ data-remote="true"} if remote
|
||||
txt << %{ class="#{html_class}"} if html_class
|
||||
txt << %{ id="#{id}"} if id
|
||||
txt << %{ method="post">}
|
||||
end
|
||||
|
||||
def whole_form(action = "http://www.example.com", options = {})
|
||||
out = form_text(action, options) + snowman(options)
|
||||
|
||||
if block_given?
|
||||
out << yield << "</form>"
|
||||
end
|
||||
|
||||
out
|
||||
end
|
||||
|
||||
def url_for(options)
|
||||
if options.is_a?(Hash)
|
||||
"http://www.example.com"
|
||||
|
@ -31,51 +61,57 @@ class FormTagHelperTest < ActionView::TestCase
|
|||
|
||||
def test_form_tag
|
||||
actual = form_tag
|
||||
expected = %(<form action="http://www.example.com" method="post">)
|
||||
expected = whole_form
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_form_tag_multipart
|
||||
actual = form_tag({}, { 'multipart' => true })
|
||||
expected = %(<form action="http://www.example.com" enctype="multipart/form-data" method="post">)
|
||||
expected = whole_form("http://www.example.com", :enctype => true)
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_form_tag_with_method_put
|
||||
actual = form_tag({}, { :method => :put })
|
||||
expected = %(<form action="http://www.example.com" method="post"><div style='margin:0;padding:0;display:inline'><input type="hidden" name="_method" value="put" /></div>)
|
||||
expected = whole_form("http://www.example.com", :method => :put)
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_form_tag_with_method_delete
|
||||
actual = form_tag({}, { :method => :delete })
|
||||
expected = %(<form action="http://www.example.com" method="post"><div style='margin:0;padding:0;display:inline'><input type="hidden" name="_method" value="delete" /></div>)
|
||||
|
||||
expected = whole_form("http://www.example.com", :method => :delete)
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_form_tag_with_remote
|
||||
actual = form_tag({}, :remote => true)
|
||||
expected = %(<form action="http://www.example.com" method="post" data-remote="true">)
|
||||
|
||||
expected = whole_form("http://www.example.com", :remote => true)
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_form_tag_with_remote_false
|
||||
actual = form_tag({}, :remote => false)
|
||||
expected = %(<form action="http://www.example.com" method="post">)
|
||||
|
||||
expected = whole_form
|
||||
assert_dom_equal expected, actual
|
||||
end
|
||||
|
||||
def test_form_tag_with_block_in_erb
|
||||
output_buffer = form_tag("http://example.com") { concat "Hello world!" }
|
||||
output_buffer = form_tag("http://www.example.com") { concat "Hello world!" }
|
||||
|
||||
expected = %(<form action="http://example.com" method="post">Hello world!</form>)
|
||||
expected = whole_form { "Hello world!" }
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
def test_form_tag_with_block_and_method_in_erb
|
||||
output_buffer = form_tag("http://example.com", :method => :put) { concat "Hello world!" }
|
||||
output_buffer = form_tag("http://www.example.com", :method => :put) { concat "Hello world!" }
|
||||
|
||||
expected = whole_form("http://www.example.com", :method => "put") do
|
||||
"Hello world!"
|
||||
end
|
||||
|
||||
expected = %(<form action="http://example.com" method="post"><div style='margin:0;padding:0;display:inline'><input type="hidden" name="_method" value="put" /></div>Hello world!</form>)
|
||||
assert_dom_equal expected, output_buffer
|
||||
end
|
||||
|
||||
|
|
|
@ -257,6 +257,10 @@ class SanitizerTest < ActionController::TestCase
|
|||
assert_sanitized %{<a href=\"http://www.domain.com?var1=1&var2=2\">my link</a>}
|
||||
end
|
||||
|
||||
def test_should_sanitize_neverending_attribute
|
||||
assert_sanitized "<span class=\"\\", "<span class=\"\\\">"
|
||||
end
|
||||
|
||||
protected
|
||||
def assert_sanitized(input, expected = nil)
|
||||
@sanitizer ||= HTML::WhiteListSanitizer.new
|
||||
|
|
|
@ -95,9 +95,9 @@ class TagHelperTest < ActionView::TestCase
|
|||
assert_equal '1 < 2 & 3', escape_once('1 < 2 & 3')
|
||||
end
|
||||
|
||||
def test_double_escaping_attributes
|
||||
def test_tag_honors_html_safe_for_param_values
|
||||
['1&2', '1 < 2', '“test“'].each do |escaped|
|
||||
assert_equal %(<a href="#{escaped}" />), tag('a', :href => escaped)
|
||||
assert_equal %(<a href="#{escaped}" />), tag('a', :href => escaped.html_safe)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -40,19 +40,8 @@ class UrlHelperTest < ActiveSupport::TestCase
|
|||
end
|
||||
alias url_hash hash_for
|
||||
|
||||
def test_url_for_escapes_urls
|
||||
def test_url_for_does_not_escape_urls
|
||||
assert_equal "/?a=b&c=d", url_for(abcd)
|
||||
assert_equal "/?a=b&c=d", url_for(abcd(:escape => true))
|
||||
assert_equal "/?a=b&c=d", url_for(abcd(:escape => false))
|
||||
end
|
||||
|
||||
def test_url_for_escaping_is_safety_aware
|
||||
assert url_for(abcd(:escape => true)).html_safe?, "escaped urls should be html_safe?"
|
||||
assert !url_for(abcd(:escape => false)).html_safe?, "non-escaped urls should not be html_safe?"
|
||||
end
|
||||
|
||||
def test_url_for_escapes_url_once
|
||||
assert_equal "/?a=b&c=d", url_for("/?a=b&c=d")
|
||||
end
|
||||
|
||||
def test_url_for_with_back
|
||||
|
@ -67,11 +56,6 @@ class UrlHelperTest < ActiveSupport::TestCase
|
|||
assert_equal 'javascript:history.back()', url_for(:back)
|
||||
end
|
||||
|
||||
def test_url_for_from_hash_doesnt_escape_ampersand
|
||||
path = url_for(hash_for(:foo => :bar, :baz => :quux))
|
||||
assert_equal '/?baz=quux&foo=bar', sort_query_string_params(path)
|
||||
end
|
||||
|
||||
# todo: missing test cases
|
||||
def test_button_to_with_straight_url
|
||||
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
|
||||
|
@ -81,8 +65,8 @@ class UrlHelperTest < ActiveSupport::TestCase
|
|||
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
|
||||
end
|
||||
|
||||
def test_button_to_with_escaped_query
|
||||
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
|
||||
def test_button_to_with_html_safe_URL
|
||||
assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2".html_safe)
|
||||
end
|
||||
|
||||
def test_button_to_with_query_and_no_name
|
||||
|
@ -151,13 +135,12 @@ class UrlHelperTest < ActiveSupport::TestCase
|
|||
|
||||
def test_link_tag_with_query
|
||||
expected = %{<a href="http://www.example.com?q1=v1&q2=v2">Hello</a>}
|
||||
assert_dom_equal expected, link_to("Hello", "http://www.example.com?q1=v1&q2=v2")
|
||||
assert_dom_equal expected, link_to("Hello", "http://www.example.com?q1=v1&q2=v2")
|
||||
end
|
||||
|
||||
def test_link_tag_with_query_and_no_name
|
||||
link = link_to(nil, "http://www.example.com?q1=v1&q2=v2")
|
||||
expected = %{<a href="http://www.example.com?q1=v1&q2=v2">http://www.example.com?q1=v1&q2=v2</a>}
|
||||
assert_dom_equal expected, link
|
||||
assert_dom_equal expected, link_to(nil, "http://www.example.com?q1=v1&q2=v2")
|
||||
end
|
||||
|
||||
def test_link_tag_with_back
|
||||
|
@ -312,7 +295,7 @@ class UrlHelperTest < ActiveSupport::TestCase
|
|||
@request = request_for_url("/?order=desc&page=1")
|
||||
|
||||
assert current_page?(hash_for(:order => "desc", :page => "1"))
|
||||
assert current_page?("http://www.example.com/?order=desc&page=1")
|
||||
assert current_page?("http://www.example.com/?order=desc&page=1")
|
||||
end
|
||||
|
||||
def test_link_unless_current
|
||||
|
@ -345,7 +328,7 @@ class UrlHelperTest < ActiveSupport::TestCase
|
|||
link_to_unless_current("Showing", "http://www.example.com/?order=asc")
|
||||
|
||||
@request = request_for_url("/?order=desc")
|
||||
assert_equal %{<a href="/?order=desc&page=2\">Showing</a>},
|
||||
assert_equal %{<a href="/?order=desc&page=2\">Showing</a>},
|
||||
link_to_unless_current("Showing", hash_for(:order => "desc", :page => 2))
|
||||
assert_equal %{<a href="http://www.example.com/?order=desc&page=2">Showing</a>},
|
||||
link_to_unless_current("Showing", "http://www.example.com/?order=desc&page=2")
|
||||
|
@ -415,7 +398,7 @@ class UrlHelperTest < ActiveSupport::TestCase
|
|||
private
|
||||
def sort_query_string_params(uri)
|
||||
path, qs = uri.split('?')
|
||||
qs = qs.split('&').sort.join('&') if qs
|
||||
qs = qs.split('&').sort.join('&') if qs
|
||||
qs ? "#{path}?#{qs}" : path
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@ require 'cases/helper'
|
|||
require 'models/topic'
|
||||
require 'models/person'
|
||||
|
||||
require 'bigdecimal'
|
||||
|
||||
class NumericalityValidationTest < ActiveModel::TestCase
|
||||
|
||||
def teardown
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
*Rails 3.0.0 [RC1] (unreleased)*
|
||||
|
||||
* PostgreSQL: ensure the database time zone matches Ruby's time zone. #4895 [Aaron Patterson]
|
||||
* Add scoping and unscoped as the syntax to replace the old with_scope and with_exclusive_scope [José Valim]
|
||||
|
||||
* New rake task, db:migrate:status, displays status of migrations #4947 [Kevin Skoglund]
|
||||
|
||||
* select and order for ActiveRecord now always concatenate nested calls. Use reorder if you want the original order to be overwritten [Santiago Pastorino]
|
||||
|
||||
* PostgreSQL: ensure the database time zone matches Ruby's time zone #4895 [Aaron Patterson]
|
||||
|
||||
|
||||
*Rails 3.0.0 [beta 4] (June 8th, 2010)*
|
||||
|
|
|
@ -62,7 +62,9 @@ end
|
|||
adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z]+/]
|
||||
puts [adapter, adapter_short, connection_path].inspect
|
||||
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
|
||||
Dir["test/cases/**/*_test{,_#{adapter_short}}.rb"].all? do |file|
|
||||
(Dir["test/cases/**/*_test.rb"].reject {
|
||||
|x| x =~ /\/adapters\//
|
||||
} + Dir["test/cases/adapters/#{adapter_short}/**/*_test.rb"]).all? do |file|
|
||||
system(ruby, "-Ilib:test:#{connection_path}", file)
|
||||
end or raise "Failures"
|
||||
end
|
||||
|
|
|
@ -409,6 +409,17 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def method_missing(method, *args)
|
||||
case method.to_s
|
||||
when 'find_or_create'
|
||||
return find(:first, :conditions => args.first) || create(args.first)
|
||||
when /^find_or_create_by_(.*)$/
|
||||
rest = $1
|
||||
return send("find_by_#{rest}", *args) ||
|
||||
method_missing("create_by_#{rest}", *args)
|
||||
when /^create_by_(.*)$/
|
||||
return create Hash[$1.split('_and_').zip(args)]
|
||||
end
|
||||
|
||||
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
|
||||
if block_given?
|
||||
super { |*block_args| yield(*block_args) }
|
||||
|
|
|
@ -110,7 +110,11 @@ module ActiveRecord
|
|||
create_scoping = {}
|
||||
set_belongs_to_association_for(create_scoping)
|
||||
{
|
||||
:find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]},
|
||||
:find => { :conditions => @finder_sql,
|
||||
:readonly => false,
|
||||
:order => @reflection.options[:order],
|
||||
:limit => @reflection.options[:limit],
|
||||
:include => @reflection.options[:include]},
|
||||
:create => create_scoping
|
||||
}
|
||||
end
|
||||
|
|
|
@ -15,6 +15,7 @@ require 'active_support/core_ext/hash/slice'
|
|||
require 'active_support/core_ext/string/behavior'
|
||||
require 'active_support/core_ext/kernel/singleton_class'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/module/deprecation'
|
||||
require 'active_support/core_ext/module/introspection'
|
||||
require 'active_support/core_ext/object/duplicable'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
@ -278,6 +279,18 @@ module ActiveRecord #:nodoc:
|
|||
# on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
|
||||
cattr_accessor :logger, :instance_writer => false
|
||||
|
||||
class << self
|
||||
def reset_subclasses #:nodoc:
|
||||
ActiveSupport::Deprecation.warn 'ActiveRecord::Base.reset_subclasses no longer does anything in Rails 3. It will be removed in the final release; please update your apps and plugins.', caller
|
||||
end
|
||||
|
||||
def subclasses
|
||||
descendants
|
||||
end
|
||||
|
||||
deprecate :subclasses => :descendants
|
||||
end
|
||||
|
||||
##
|
||||
# :singleton-method:
|
||||
# Contains the database configuration - as is typically stored in config/database.yml -
|
||||
|
@ -385,7 +398,7 @@ module ActiveRecord #:nodoc:
|
|||
|
||||
delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
|
||||
delegate :find_each, :find_in_batches, :to => :scoped
|
||||
delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
|
||||
delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
|
||||
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
|
||||
|
||||
# Executes a custom SQL query against your database and returns all the results. The results will
|
||||
|
@ -707,14 +720,6 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
alias :sequence_name= :set_sequence_name
|
||||
|
||||
# Turns the +table_name+ back into a class name following the reverse rules of +table_name+.
|
||||
def class_name(table_name = table_name) # :nodoc:
|
||||
# remove any prefix and/or suffix from the table name
|
||||
class_name = table_name[table_name_prefix.length..-(table_name_suffix.length + 1)].camelize
|
||||
class_name = class_name.singularize if pluralize_table_names
|
||||
class_name
|
||||
end
|
||||
|
||||
# Indicates whether the table associated with this class exists
|
||||
def table_exists?
|
||||
connection.table_exists?(table_name)
|
||||
|
@ -788,7 +793,7 @@ module ActiveRecord #:nodoc:
|
|||
def reset_column_information
|
||||
undefine_attribute_methods
|
||||
@column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil
|
||||
@arel_engine = @unscoped = @arel_table = nil
|
||||
@arel_engine = @relation = @arel_table = nil
|
||||
end
|
||||
|
||||
def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
|
||||
|
@ -891,9 +896,9 @@ module ActiveRecord #:nodoc:
|
|||
store_full_sti_class ? name : name.demodulize
|
||||
end
|
||||
|
||||
def unscoped
|
||||
@unscoped ||= Relation.new(self, arel_table)
|
||||
finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped
|
||||
def relation
|
||||
@relation ||= Relation.new(self, arel_table)
|
||||
finder_needs_type_condition? ? @relation.where(type_condition) : @relation
|
||||
end
|
||||
|
||||
def arel_table
|
||||
|
@ -910,6 +915,31 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
end
|
||||
|
||||
# Returns a scope for this class without taking into account the default_scope.
|
||||
#
|
||||
# class Post < ActiveRecord::Base
|
||||
# default_scope :published => true
|
||||
# end
|
||||
#
|
||||
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
|
||||
# Post.unscoped.all # Fires "SELECT * FROM posts"
|
||||
#
|
||||
# This method also accepts a block meaning that all queries inside the block will
|
||||
# not use the default_scope:
|
||||
#
|
||||
# Post.unscoped {
|
||||
# limit(10) # Fires "SELECT * FROM posts LIMIT 10"
|
||||
# }
|
||||
#
|
||||
def unscoped
|
||||
block_given? ? relation.scoping { yield } : relation
|
||||
end
|
||||
|
||||
def scoped_methods #:nodoc:
|
||||
key = :"#{self}_scoped_methods"
|
||||
Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
|
||||
end
|
||||
|
||||
private
|
||||
# Finder methods must instantiate through this method to work with the
|
||||
# single-table inheritance model that makes it possible to create
|
||||
|
@ -1144,6 +1174,20 @@ module ActiveRecord #:nodoc:
|
|||
|
||||
# Works like with_scope, but discards any nested properties.
|
||||
def with_exclusive_scope(method_scoping = {}, &block)
|
||||
if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
|
||||
raise ArgumentError, <<-MSG
|
||||
New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
|
||||
|
||||
User.unscoped.where(:active => true)
|
||||
|
||||
Or call unscoped with a block:
|
||||
|
||||
User.unscoped do
|
||||
User.where(:active => true).all
|
||||
end
|
||||
|
||||
MSG
|
||||
end
|
||||
with_scope(method_scoping, :overwrite, &block)
|
||||
end
|
||||
|
||||
|
@ -1157,11 +1201,6 @@ module ActiveRecord #:nodoc:
|
|||
self.default_scoping << construct_finder_arel(options, default_scoping.pop)
|
||||
end
|
||||
|
||||
def scoped_methods #:nodoc:
|
||||
key = :"#{self}_scoped_methods"
|
||||
Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
|
||||
end
|
||||
|
||||
def current_scoped_methods #:nodoc:
|
||||
scoped_methods.last
|
||||
end
|
||||
|
|
|
@ -365,7 +365,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def remove_index!(table_name, index_name) #:nodoc:
|
||||
execute "DROP INDEX #{quote_column_name(index_name)} ON #{table_name}"
|
||||
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
||||
end
|
||||
|
||||
# Rename an index.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'active_support/core_ext/kernel/singleton_class'
|
||||
require 'active_support/core_ext/module/aliasing'
|
||||
|
||||
module ActiveRecord
|
||||
# Exception that can be raised to stop migrations from going backwards.
|
||||
|
|
|
@ -25,10 +25,9 @@ module ActiveRecord
|
|||
#
|
||||
# You can define a \scope that applies to all finders using
|
||||
# ActiveRecord::Base.default_scope.
|
||||
def scoped(options = {}, &block)
|
||||
def scoped(options = nil)
|
||||
if options.present?
|
||||
relation = scoped.apply_finder_options(options)
|
||||
block_given? ? relation.extending(Module.new(&block)) : relation
|
||||
scoped.apply_finder_options(options)
|
||||
else
|
||||
current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone
|
||||
end
|
||||
|
@ -88,18 +87,22 @@ module ActiveRecord
|
|||
# end
|
||||
def scope(name, scope_options = {}, &block)
|
||||
name = name.to_sym
|
||||
valid_scope_name?(name)
|
||||
|
||||
if !scopes[name] && respond_to?(name, true)
|
||||
logger.warn "Creating scope :#{name}. " \
|
||||
"Overwriting existing method #{self.name}.#{name}."
|
||||
end
|
||||
extension = Module.new(&block) if block_given?
|
||||
|
||||
scopes[name] = lambda do |*args|
|
||||
options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options
|
||||
|
||||
relation = scoped
|
||||
relation = options.is_a?(Hash) ? relation.apply_finder_options(options) : scoped.merge(options) if options
|
||||
block_given? ? relation.extending(Module.new(&block)) : relation
|
||||
relation = if options.is_a?(Hash)
|
||||
scoped.apply_finder_options(options)
|
||||
elsif options
|
||||
scoped.merge(options)
|
||||
else
|
||||
scoped
|
||||
end
|
||||
|
||||
extension ? relation.extending(extension) : relation
|
||||
end
|
||||
|
||||
singleton_class.send :define_method, name, &scopes[name]
|
||||
|
@ -109,7 +112,15 @@ module ActiveRecord
|
|||
ActiveSupport::Deprecation.warn("Base.named_scope has been deprecated, please use Base.scope instead", caller)
|
||||
scope(*args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def valid_scope_name?(name)
|
||||
if !scopes[name] && respond_to?(name, true)
|
||||
logger.warn "Creating scope :#{name}. " \
|
||||
"Overwriting existing method #{self.name}.#{name}."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -182,7 +182,7 @@ module ActiveRecord
|
|||
def reload(options = nil)
|
||||
clear_aggregation_cache
|
||||
clear_association_cache
|
||||
@attributes.update(self.class.send(:with_exclusive_scope) { self.class.find(self.id, options) }.instance_variable_get('@attributes'))
|
||||
@attributes.update(self.class.unscoped { self.class.find(self.id, options) }.instance_variable_get('@attributes'))
|
||||
@attributes_cache = {}
|
||||
self
|
||||
end
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace :db do
|
|||
end
|
||||
rescue
|
||||
case config['adapter']
|
||||
when 'mysql'
|
||||
when /mysql/
|
||||
@charset = ENV['CHARSET'] || 'utf8'
|
||||
@collation = ENV['COLLATION'] || 'utf8_unicode_ci'
|
||||
creation_options = {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
|
||||
|
@ -171,6 +171,36 @@ namespace :db do
|
|||
ActiveRecord::Migrator.run(:down, "db/migrate/", version)
|
||||
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
|
||||
end
|
||||
|
||||
desc "Display status of migrations"
|
||||
task :status => :environment do
|
||||
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
||||
ActiveRecord::Base.establish_connection(config)
|
||||
unless ActiveRecord::Base.connection.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
|
||||
puts 'Schema migrations table does not exist yet.'
|
||||
next # means "return" for rake task
|
||||
end
|
||||
db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
|
||||
file_list = []
|
||||
Dir.foreach(File.join(Rails.root, 'db', 'migrate')) do |file|
|
||||
# only files matching "20091231235959_some_name.rb" pattern
|
||||
if match_data = /(\d{14})_(.+)\.rb/.match(file)
|
||||
status = db_list.delete(match_data[1]) ? 'up' : 'down'
|
||||
file_list << [status, match_data[1], match_data[2]]
|
||||
end
|
||||
end
|
||||
# output
|
||||
puts "\ndatabase: #{config['database']}\n\n"
|
||||
puts "#{"Status".center(8)} #{"Migration ID".ljust(14)} Migration Name"
|
||||
puts "-" * 50
|
||||
file_list.each do |file|
|
||||
puts "#{file[0].center(8)} #{file[1].ljust(14)} #{file[2].humanize}"
|
||||
end
|
||||
db_list.each do |version|
|
||||
puts "#{'up'.center(8)} #{version.ljust(14)} *** NO FILE ***"
|
||||
end
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
|
||||
|
@ -194,7 +224,7 @@ namespace :db do
|
|||
task :charset => :environment do
|
||||
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
||||
case config['adapter']
|
||||
when 'mysql'
|
||||
when /mysql/
|
||||
ActiveRecord::Base.establish_connection(config)
|
||||
puts ActiveRecord::Base.connection.charset
|
||||
when 'postgresql'
|
||||
|
@ -212,7 +242,7 @@ namespace :db do
|
|||
task :collation => :environment do
|
||||
config = ActiveRecord::Base.configurations[Rails.env || 'development']
|
||||
case config['adapter']
|
||||
when 'mysql'
|
||||
when /mysql/
|
||||
ActiveRecord::Base.establish_connection(config)
|
||||
puts ActiveRecord::Base.connection.collation
|
||||
else
|
||||
|
@ -313,7 +343,7 @@ namespace :db do
|
|||
task :dump => :environment do
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
case abcs[Rails.env]["adapter"]
|
||||
when "mysql", "oci", "oracle"
|
||||
when /mysql/, "oci", "oracle"
|
||||
ActiveRecord::Base.establish_connection(abcs[Rails.env])
|
||||
File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
|
||||
when "postgresql"
|
||||
|
@ -361,7 +391,7 @@ namespace :db do
|
|||
task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
case abcs["test"]["adapter"]
|
||||
when "mysql"
|
||||
when /mysql/
|
||||
ActiveRecord::Base.establish_connection(:test)
|
||||
ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
|
||||
IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split("\n\n").each do |table|
|
||||
|
@ -395,7 +425,7 @@ namespace :db do
|
|||
task :purge => :environment do
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
case abcs["test"]["adapter"]
|
||||
when "mysql"
|
||||
when /mysql/
|
||||
ActiveRecord::Base.establish_connection(:test)
|
||||
ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], abcs["test"])
|
||||
when "postgresql"
|
||||
|
@ -451,7 +481,7 @@ task 'test:prepare' => 'db:test:prepare'
|
|||
|
||||
def drop_database(config)
|
||||
case config['adapter']
|
||||
when 'mysql'
|
||||
when /mysql/
|
||||
ActiveRecord::Base.establish_connection(config)
|
||||
ActiveRecord::Base.connection.drop_database config['database']
|
||||
when /^sqlite/
|
||||
|
|
|
@ -16,7 +16,7 @@ module ActiveRecord
|
|||
attr_reader :table, :klass
|
||||
attr_accessor :extensions
|
||||
|
||||
def initialize(klass, table, &block)
|
||||
def initialize(klass, table)
|
||||
@klass, @table = klass, table
|
||||
|
||||
@implicit_readonly = nil
|
||||
|
@ -25,12 +25,10 @@ module ActiveRecord
|
|||
SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
|
||||
(ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
|
||||
@extensions = []
|
||||
|
||||
apply_modules(Module.new(&block)) if block_given?
|
||||
end
|
||||
|
||||
def new(*args, &block)
|
||||
with_create_scope { @klass.new(*args, &block) }
|
||||
scoping { @klass.new(*args, &block) }
|
||||
end
|
||||
|
||||
def initialize_copy(other)
|
||||
|
@ -40,11 +38,11 @@ module ActiveRecord
|
|||
alias build new
|
||||
|
||||
def create(*args, &block)
|
||||
with_create_scope { @klass.create(*args, &block) }
|
||||
scoping { @klass.create(*args, &block) }
|
||||
end
|
||||
|
||||
def create!(*args, &block)
|
||||
with_create_scope { @klass.create!(*args, &block) }
|
||||
scoping { @klass.create!(*args, &block) }
|
||||
end
|
||||
|
||||
def respond_to?(method, include_private = false)
|
||||
|
@ -102,6 +100,25 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
# Scope all queries to the current scope.
|
||||
#
|
||||
# ==== Example
|
||||
#
|
||||
# Comment.where(:post_id => 1).scoping do
|
||||
# Comment.first #=> SELECT * FROM comments WHERE post_id = 1
|
||||
# end
|
||||
#
|
||||
# Please check unscoped if you want to remove all previous scopes (including
|
||||
# the default_scope) during the execution of a block.
|
||||
def scoping
|
||||
@klass.scoped_methods << self
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
@klass.scoped_methods.pop
|
||||
end
|
||||
end
|
||||
|
||||
# Updates all records with details given if they match a set of conditions supplied, limits and order can
|
||||
# also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
|
||||
# database. It does not instantiate the involved models and it does not trigger Active Record callbacks
|
||||
|
@ -305,7 +322,6 @@ module ActiveRecord
|
|||
if where.is_a?(Arel::Predicates::Equality)
|
||||
hash[where.operand1.name] = where.operand2.respond_to?(:value) ? where.operand2.value : where.operand2
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
@ -328,15 +344,6 @@ module ActiveRecord
|
|||
to_a.inspect
|
||||
end
|
||||
|
||||
def extend(*args, &block)
|
||||
if block_given?
|
||||
apply_modules Module.new(&block)
|
||||
self
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def method_missing(method, *args, &block)
|
||||
|
@ -345,7 +352,7 @@ module ActiveRecord
|
|||
elsif @klass.scopes[method]
|
||||
merge(@klass.send(method, *args, &block))
|
||||
elsif @klass.respond_to?(method)
|
||||
@klass.send(:with_scope, self) { @klass.send(method, *args, &block) }
|
||||
scoping { @klass.send(method, *args, &block) }
|
||||
elsif arel.respond_to?(method)
|
||||
arel.send(method, *args, &block)
|
||||
elsif match = DynamicFinderMatch.match(method)
|
||||
|
@ -364,10 +371,6 @@ module ActiveRecord
|
|||
|
||||
private
|
||||
|
||||
def with_create_scope
|
||||
@klass.send(:with_scope, :create => scope_for_create, :find => {}) { yield }
|
||||
end
|
||||
|
||||
def references_eager_loaded_tables?
|
||||
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
||||
joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map(&:downcase).uniq
|
||||
|
|
|
@ -20,9 +20,7 @@ module ActiveRecord
|
|||
table = Arel::Table.new(table_name, :engine => @engine)
|
||||
end
|
||||
|
||||
unless attribute = table[column]
|
||||
raise StatementInvalid, "No attribute named `#{column}` exists for table `#{table.name}`"
|
||||
end
|
||||
attribute = table[column] || Arel::Attribute.new(table, column)
|
||||
|
||||
case value
|
||||
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation
|
||||
|
|
|
@ -15,12 +15,10 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def eager_load(*args)
|
||||
args.reject! { |a| a.blank? }
|
||||
clone.tap { |r| r.eager_load_values += args if args.present? }
|
||||
end
|
||||
|
||||
def preload(*args)
|
||||
args.reject! { |a| a.blank? }
|
||||
clone.tap { |r| r.preload_values += args if args.present? }
|
||||
end
|
||||
|
||||
|
@ -28,29 +26,24 @@ module ActiveRecord
|
|||
if block_given?
|
||||
to_a.select { |*block_args| yield(*block_args) }
|
||||
else
|
||||
args.reject! { |a| a.blank? }
|
||||
clone.tap { |r| r.select_values += args if args.present? }
|
||||
end
|
||||
end
|
||||
|
||||
def group(*args)
|
||||
args.reject! { |a| a.blank? }
|
||||
clone.tap { |r| r.group_values += args if args.present? }
|
||||
end
|
||||
|
||||
def order(*args)
|
||||
args.reject! { |a| a.blank? }
|
||||
clone.tap { |r| r.order_values += args if args.present? }
|
||||
end
|
||||
|
||||
def reorder(*args)
|
||||
args.reject! { |a| a.blank? }
|
||||
clone.tap { |r| r.order_values = args if args.present? }
|
||||
end
|
||||
|
||||
def joins(*args)
|
||||
args.flatten!
|
||||
args.reject! { |a| a.blank? }
|
||||
clone.tap { |r| r.joins_values += args if args.present? }
|
||||
end
|
||||
|
||||
|
@ -93,8 +86,9 @@ module ActiveRecord
|
|||
clone.tap { |r| r.from_value = value }
|
||||
end
|
||||
|
||||
def extending(*modules)
|
||||
clone.tap { |r| r.send :apply_modules, *modules }
|
||||
def extending(*modules, &block)
|
||||
modules << Module.new(&block) if block_given?
|
||||
clone.tap { |r| r.send(:apply_modules, *modules) }
|
||||
end
|
||||
|
||||
def reverse_order
|
||||
|
@ -201,7 +195,7 @@ module ActiveRecord
|
|||
|
||||
stashed_association_joins = joins.select {|j| j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)}
|
||||
|
||||
non_association_joins = (joins - association_joins - stashed_association_joins).reject {|j| j.blank?}
|
||||
non_association_joins = (joins - association_joins - stashed_association_joins)
|
||||
custom_joins = custom_join_sql(*non_association_joins)
|
||||
|
||||
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins)
|
||||
|
|
|
@ -6,7 +6,7 @@ module ActiveRecord
|
|||
merged_relation = clone
|
||||
return merged_relation unless r
|
||||
|
||||
(Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).reject {|m| [:joins, :where].include?(m)}.each do |method|
|
||||
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - [:joins, :where]).each do |method|
|
||||
value = r.send(:"#{method}_values")
|
||||
merged_relation.send(:"#{method}_values=", value) if value.present?
|
||||
end
|
||||
|
|
|
@ -21,6 +21,68 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|||
Client.destroyed_client_ids.clear
|
||||
end
|
||||
|
||||
def test_create_by
|
||||
person = Person.create! :first_name => 'tenderlove'
|
||||
post = Post.find :first
|
||||
|
||||
assert_equal [], person.readers
|
||||
assert_nil person.readers.find_by_post_id post.id
|
||||
|
||||
reader = person.readers.create_by_post_id post.id
|
||||
|
||||
assert_equal 1, person.readers.count
|
||||
assert_equal 1, person.readers.length
|
||||
assert_equal post, person.readers.first.post
|
||||
assert_equal person, person.readers.first.person
|
||||
end
|
||||
|
||||
def test_create_by_multi
|
||||
person = Person.create! :first_name => 'tenderlove'
|
||||
post = Post.find :first
|
||||
|
||||
assert_equal [], person.readers
|
||||
|
||||
reader = person.readers.create_by_post_id_and_skimmer post.id, false
|
||||
|
||||
assert_equal 1, person.readers.count
|
||||
assert_equal 1, person.readers.length
|
||||
assert_equal post, person.readers.first.post
|
||||
assert_equal person, person.readers.first.person
|
||||
end
|
||||
|
||||
def test_find_or_create_by
|
||||
person = Person.create! :first_name => 'tenderlove'
|
||||
post = Post.find :first
|
||||
|
||||
assert_equal [], person.readers
|
||||
assert_nil person.readers.find_by_post_id post.id
|
||||
|
||||
reader = person.readers.find_or_create_by_post_id post.id
|
||||
|
||||
assert_equal 1, person.readers.count
|
||||
assert_equal 1, person.readers.length
|
||||
assert_equal post, person.readers.first.post
|
||||
assert_equal person, person.readers.first.person
|
||||
end
|
||||
|
||||
def test_find_or_create
|
||||
person = Person.create! :first_name => 'tenderlove'
|
||||
post = Post.find :first
|
||||
|
||||
assert_equal [], person.readers
|
||||
assert_nil person.readers.find(:first, :conditions => {
|
||||
:post_id => post.id
|
||||
})
|
||||
|
||||
reader = person.readers.find_or_create :post_id => post.id
|
||||
|
||||
assert_equal 1, person.readers.count
|
||||
assert_equal 1, person.readers.length
|
||||
assert_equal post, person.readers.first.post
|
||||
assert_equal person, person.readers.first.person
|
||||
end
|
||||
|
||||
|
||||
def force_signal37_to_load_all_clients_of_firm
|
||||
companies(:first_firm).clients_of_firm.each {|f| }
|
||||
end
|
||||
|
|
|
@ -798,25 +798,6 @@ class BasicsTest < ActiveRecord::TestCase
|
|||
assert_raise(NoMethodError) { t.title2 }
|
||||
end
|
||||
|
||||
def test_class_name
|
||||
assert_equal "Firm", ActiveRecord::Base.class_name("firms")
|
||||
assert_equal "Category", ActiveRecord::Base.class_name("categories")
|
||||
assert_equal "AccountHolder", ActiveRecord::Base.class_name("account_holder")
|
||||
|
||||
ActiveRecord::Base.pluralize_table_names = false
|
||||
assert_equal "Firms", ActiveRecord::Base.class_name( "firms" )
|
||||
ActiveRecord::Base.pluralize_table_names = true
|
||||
|
||||
ActiveRecord::Base.table_name_prefix = "test_"
|
||||
assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms" )
|
||||
ActiveRecord::Base.table_name_suffix = "_tests"
|
||||
assert_equal "Firm", ActiveRecord::Base.class_name( "test_firms_tests" )
|
||||
ActiveRecord::Base.table_name_prefix = ""
|
||||
assert_equal "Firm", ActiveRecord::Base.class_name( "firms_tests" )
|
||||
ActiveRecord::Base.table_name_suffix = ""
|
||||
assert_equal "Firm", ActiveRecord::Base.class_name( "firms" )
|
||||
end
|
||||
|
||||
def test_null_fields
|
||||
assert_nil Topic.find(1).parent_id
|
||||
assert_nil Topic.create("title" => "Hey you").parent_id
|
||||
|
|
|
@ -173,7 +173,7 @@ class InheritanceTest < ActiveRecord::TestCase
|
|||
|
||||
def test_complex_inheritance
|
||||
very_special_client = VerySpecialClient.create("name" => "veryspecial")
|
||||
assert_equal very_special_client, VerySpecialClient.find(:first, :conditions => "name = 'veryspecial'")
|
||||
assert_equal very_special_client, VerySpecialClient.where("name = 'veryspecial'").first
|
||||
assert_equal very_special_client, SpecialClient.find(:first, :conditions => "name = 'veryspecial'")
|
||||
assert_equal very_special_client, Company.find(:first, :conditions => "name = 'veryspecial'")
|
||||
assert_equal very_special_client, Client.find(:first, :conditions => "name = 'veryspecial'")
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
# This file can be removed once with_exclusive_scope and with_scope are removed.
|
||||
# All the tests were already ported to relation_scoping_test.rb when the new
|
||||
# relation scoping API was added.
|
||||
|
||||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
|
@ -283,6 +287,12 @@ class NestedScopingTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_with_exclusive_scope_with_relation
|
||||
assert_raise(ArgumentError) do
|
||||
Developer.all_johns
|
||||
end
|
||||
end
|
||||
|
||||
def test_append_conditions
|
||||
Developer.send(:with_scope, :find => { :conditions => "name = 'David'" }) do
|
||||
Developer.send(:with_scope, :find => { :conditions => 'salary = 80000' }) do
|
||||
|
@ -534,204 +544,3 @@ class NestedScopingTest < ActiveRecord::TestCase
|
|||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
end
|
||||
|
||||
class HasManyScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
def setup
|
||||
@welcome = Post.find(1)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_methods
|
||||
assert_equal 'a comment...', Comment.what_are_you
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_scoped
|
||||
assert_equal 4, Comment.search_by_type('Comment').size
|
||||
assert_equal 2, @welcome.comments.search_by_type('Comment').size
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 4, Comment.find_all_by_type('Comment').size
|
||||
assert_equal 2, @welcome.comments.find_all_by_type('Comment').size
|
||||
end
|
||||
|
||||
def test_nested_scope
|
||||
Comment.send(:with_scope, :find => { :conditions => '1=1' }) do
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
|
||||
fixtures :posts, :categories, :categories_posts
|
||||
|
||||
def setup
|
||||
@welcome = Post.find(1)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_methods
|
||||
assert_equal 'a category...', Category.what_are_you
|
||||
assert_equal 'a category...', @welcome.categories.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 4, Category.find_all_by_type('SpecialCategory').size
|
||||
assert_equal 0, @welcome.categories.find_all_by_type('SpecialCategory').size
|
||||
assert_equal 2, @welcome.categories.find_all_by_type('Category').size
|
||||
end
|
||||
|
||||
def test_nested_scope
|
||||
Category.send(:with_scope, :find => { :conditions => '1=1' }) do
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultScopingTest < ActiveRecord::TestCase
|
||||
fixtures :developers, :posts
|
||||
|
||||
def test_default_scope
|
||||
expected = Developer.find(:all, :order => 'salary DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_string
|
||||
assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.find(:all).map(&:id).sort
|
||||
assert_equal nil, DeveloperCalledDavid.create!.name
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_hash
|
||||
assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.find(:all).map(&:id).sort
|
||||
assert_equal 'Jamis', DeveloperCalledJamis.create!.name
|
||||
end
|
||||
|
||||
def test_default_scoping_with_threads
|
||||
2.times do
|
||||
Thread.new { assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values }.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_scoping_with_inheritance
|
||||
# Inherit a class having a default scope and define a new default scope
|
||||
klass = Class.new(DeveloperOrderedBySalary)
|
||||
klass.send :default_scope, :limit => 1
|
||||
|
||||
# Scopes added on children should append to parent scope
|
||||
assert_equal 1, klass.scoped.limit_value
|
||||
assert_equal ['salary DESC'], klass.scoped.order_values
|
||||
|
||||
# Parent should still have the original scope
|
||||
assert_nil DeveloperOrderedBySalary.scoped.limit_value
|
||||
assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values
|
||||
end
|
||||
|
||||
def test_default_scope_called_twice_merges_conditions
|
||||
Developer.destroy_all
|
||||
Developer.create!(:name => "David", :salary => 80000)
|
||||
Developer.create!(:name => "David", :salary => 100000)
|
||||
Developer.create!(:name => "Brian", :salary => 100000)
|
||||
|
||||
klass = Class.new(Developer)
|
||||
klass.__send__ :default_scope, :conditions => { :name => "David" }
|
||||
klass.__send__ :default_scope, :conditions => { :salary => 100000 }
|
||||
assert_equal 1, klass.count
|
||||
assert_equal "David", klass.first.name
|
||||
assert_equal 100000, klass.first.salary
|
||||
end
|
||||
def test_method_scope
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_nested_scope
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.send(:with_scope, :find => { :order => 'name DESC'}) do
|
||||
DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
end
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_named_scope_overwrites_default
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name }
|
||||
received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_nested_exclusive_scope
|
||||
expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do
|
||||
DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
end
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_overwriting_default_scope
|
||||
expected = Developer.find(:all, :order => 'salary').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.find(:all, :order => 'salary').collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_using_relation
|
||||
posts = PostWithComment.scoped
|
||||
assert_equal 2, posts.count
|
||||
assert_equal posts(:thinking), posts.first
|
||||
end
|
||||
|
||||
def test_create_attribute_overwrites_default_scoping
|
||||
assert_equal 'David', PoorDeveloperCalledJamis.create!(:name => 'David').name
|
||||
assert_equal 200000, PoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary
|
||||
end
|
||||
|
||||
def test_create_attribute_overwrites_default_values
|
||||
assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary
|
||||
assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
# We disabled the scoping for has_one and belongs_to as we can't think of a proper use case
|
||||
|
||||
class BelongsToScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
def setup
|
||||
@greetings = Comment.find(1)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_method
|
||||
assert_equal 'a post...', Post.what_are_you
|
||||
assert_equal 'a post...', @greetings.post.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 4, Post.find_all_by_type('Post').size
|
||||
assert_equal 1, @greetings.post.find_all_by_type('Post').size
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class HasOneScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
def setup
|
||||
@sti_comments = Post.find(4)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_methods
|
||||
assert_equal 'a comment...', Comment.what_are_you
|
||||
assert_equal 'a very special comment...', @sti_comments.very_special_comment.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 1, Comment.find_all_by_type('VerySpecialComment').size
|
||||
assert_equal 1, @sti_comments.very_special_comment.find_all_by_type('VerySpecialComment').size
|
||||
assert_equal 0, @sti_comments.very_special_comment.find_all_by_type('Comment').size
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
=end
|
||||
|
|
|
@ -1621,6 +1621,23 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|||
end
|
||||
end
|
||||
|
||||
class ReservedWordsMigrationTest < ActiveRecord::TestCase
|
||||
def test_drop_index_from_table_named_values
|
||||
connection = Person.connection
|
||||
connection.create_table :values, :force => true do |t|
|
||||
t.integer :value
|
||||
end
|
||||
|
||||
assert_nothing_raised do
|
||||
connection.add_index :values, :value
|
||||
connection.remove_index :values, :column => :value
|
||||
end
|
||||
|
||||
connection.drop_table :values rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class ChangeTableMigrationsTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@connection = Person.connection
|
||||
|
|
|
@ -54,8 +54,8 @@ class NamedScopeTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_respond_to_respects_include_private_parameter
|
||||
assert !Topic.approved.respond_to?(:with_create_scope)
|
||||
assert Topic.approved.respond_to?(:with_create_scope, true)
|
||||
assert !Topic.approved.respond_to?(:tables_in_string)
|
||||
assert Topic.approved.respond_to?(:tables_in_string, true)
|
||||
end
|
||||
|
||||
def test_subclasses_inherit_scopes
|
||||
|
@ -450,6 +450,12 @@ class NamedScopeTest < ActiveRecord::TestCase
|
|||
assert before.object_id != post.comments.containing_the_letter_e.object_id, "AssociationCollection##{method} should reset the named scopes cache"
|
||||
end
|
||||
end
|
||||
|
||||
def test_named_scoped_are_lazy_loaded_if_table_still_does_not_exist
|
||||
assert_nothing_raised do
|
||||
require "models/without_table"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DynamicScopeMatchTest < ActiveRecord::TestCase
|
||||
|
|
|
@ -0,0 +1,396 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
require 'models/developer'
|
||||
require 'models/project'
|
||||
require 'models/comment'
|
||||
require 'models/category'
|
||||
|
||||
class RelationScopingTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects
|
||||
|
||||
def test_scoped_find
|
||||
Developer.where("name = 'David'").scoping do
|
||||
assert_nothing_raised { Developer.find(1) }
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_first
|
||||
developer = Developer.find(10)
|
||||
Developer.where("salary = 100000").scoping do
|
||||
assert_equal developer, Developer.order("name").first
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_last
|
||||
highest_salary = Developer.order("salary DESC").first
|
||||
|
||||
Developer.order("salary").scoping do
|
||||
assert_equal highest_salary, Developer.last
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_last_preserves_scope
|
||||
lowest_salary = Developer.first :order => "salary ASC"
|
||||
highest_salary = Developer.first :order => "salary DESC"
|
||||
|
||||
Developer.order("salary").scoping do
|
||||
assert_equal highest_salary, Developer.last
|
||||
assert_equal lowest_salary, Developer.first
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_combines_and_sanitizes_conditions
|
||||
Developer.where("salary = 9000").scoping do
|
||||
assert_equal developers(:poor_jamis), Developer.where("name = 'Jamis'").first
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_all
|
||||
Developer.where("name = 'David'").scoping do
|
||||
assert_equal [developers(:david)], Developer.all
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_select
|
||||
Developer.select("id, name").scoping do
|
||||
developer = Developer.where("name = 'David'").first
|
||||
assert_equal "David", developer.name
|
||||
assert !developer.has_attribute?(:salary)
|
||||
end
|
||||
end
|
||||
|
||||
def test_scope_select_concatenates
|
||||
Developer.select("id, name").scoping do
|
||||
developer = Developer.select('id, salary').where("name = 'David'").first
|
||||
assert_equal 80000, developer.salary
|
||||
assert developer.has_attribute?(:id)
|
||||
assert developer.has_attribute?(:name)
|
||||
assert developer.has_attribute?(:salary)
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_count
|
||||
Developer.where("name = 'David'").scoping do
|
||||
assert_equal 1, Developer.count
|
||||
end
|
||||
|
||||
Developer.where('salary = 100000').scoping do
|
||||
assert_equal 8, Developer.count
|
||||
assert_equal 1, Developer.where("name LIKE 'fixture_1%'").count
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_include
|
||||
# with the include, will retrieve only developers for the given project
|
||||
scoped_developers = Developer.includes(:projects).scoping do
|
||||
Developer.where('projects.id = 2').all
|
||||
end
|
||||
assert scoped_developers.include?(developers(:david))
|
||||
assert !scoped_developers.include?(developers(:jamis))
|
||||
assert_equal 1, scoped_developers.size
|
||||
end
|
||||
|
||||
def test_scoped_find_joins
|
||||
scoped_developers = Developer.joins('JOIN developers_projects ON id = developer_id').scoping do
|
||||
Developer.where('developers_projects.project_id = 2').all
|
||||
end
|
||||
|
||||
assert scoped_developers.include?(developers(:david))
|
||||
assert !scoped_developers.include?(developers(:jamis))
|
||||
assert_equal 1, scoped_developers.size
|
||||
assert_equal developers(:david).attributes, scoped_developers.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_create_with_where
|
||||
new_comment = VerySpecialComment.where(:post_id => 1).scoping do
|
||||
VerySpecialComment.create :body => "Wonderful world"
|
||||
end
|
||||
|
||||
assert_equal 1, new_comment.post_id
|
||||
assert Post.find(1).comments.include?(new_comment)
|
||||
end
|
||||
|
||||
def test_scoped_create_with_create_with
|
||||
new_comment = VerySpecialComment.create_with(:post_id => 1).scoping do
|
||||
VerySpecialComment.create :body => "Wonderful world"
|
||||
end
|
||||
|
||||
assert_equal 1, new_comment.post_id
|
||||
assert Post.find(1).comments.include?(new_comment)
|
||||
end
|
||||
|
||||
def test_scoped_create_with_create_with_has_higher_priority
|
||||
new_comment = VerySpecialComment.where(:post_id => 2).create_with(:post_id => 1).scoping do
|
||||
VerySpecialComment.create :body => "Wonderful world"
|
||||
end
|
||||
|
||||
assert_equal 1, new_comment.post_id
|
||||
assert Post.find(1).comments.include?(new_comment)
|
||||
end
|
||||
|
||||
def test_ensure_that_method_scoping_is_correctly_restored
|
||||
scoped_methods = Developer.send(:current_scoped_methods)
|
||||
|
||||
begin
|
||||
Developer.where("name = 'Jamis'").scoping do
|
||||
raise "an exception"
|
||||
end
|
||||
rescue
|
||||
end
|
||||
|
||||
assert_equal scoped_methods, Developer.send(:current_scoped_methods)
|
||||
end
|
||||
end
|
||||
|
||||
class NestedRelationScopingTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :developers, :projects, :comments, :posts
|
||||
|
||||
def test_merge_options
|
||||
Developer.where('salary = 80000').scoping do
|
||||
Developer.limit(10).scoping do
|
||||
devs = Developer.scoped
|
||||
assert_equal '(salary = 80000)', devs.arel.send(:where_clauses).join(' AND ')
|
||||
assert_equal 10, devs.taken
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_merge_inner_scope_has_priority
|
||||
Developer.limit(5).scoping do
|
||||
Developer.limit(10).scoping do
|
||||
assert_equal 10, Developer.count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_replace_options
|
||||
Developer.where(:name => 'David').scoping do
|
||||
Developer.unscoped do
|
||||
assert_equal 'Jamis', Developer.where(:name => 'Jamis').first[:name]
|
||||
end
|
||||
|
||||
assert_equal 'David', Developer.first[:name]
|
||||
end
|
||||
end
|
||||
|
||||
def test_three_level_nested_exclusive_scoped_find
|
||||
Developer.where("name = 'Jamis'").scoping do
|
||||
assert_equal 'Jamis', Developer.first.name
|
||||
|
||||
Developer.unscoped.where("name = 'David'") do
|
||||
assert_equal 'David', Developer.first.name
|
||||
|
||||
Developer.unscoped.where("name = 'Maiha'") do
|
||||
assert_equal nil, Developer.first
|
||||
end
|
||||
|
||||
# ensure that scoping is restored
|
||||
assert_equal 'David', Developer.first.name
|
||||
end
|
||||
|
||||
# ensure that scoping is restored
|
||||
assert_equal 'Jamis', Developer.first.name
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_create
|
||||
comment = Comment.create_with(:post_id => 1).scoping do
|
||||
Comment.create_with(:post_id => 2).scoping do
|
||||
Comment.create :body => "Hey guys, nested scopes are broken. Please fix!"
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal 2, comment.post_id
|
||||
end
|
||||
|
||||
def test_nested_exclusive_scope_for_create
|
||||
comment = Comment.create_with(:body => "Hey guys, nested scopes are broken. Please fix!").scoping do
|
||||
Comment.unscoped.create_with(:post_id => 1).scoping do
|
||||
assert Comment.new.body.blank?
|
||||
Comment.create :body => "Hey guys"
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal 1, comment.post_id
|
||||
assert_equal 'Hey guys', comment.body
|
||||
end
|
||||
end
|
||||
|
||||
class HasManyScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
def setup
|
||||
@welcome = Post.find(1)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_methods
|
||||
assert_equal 'a comment...', Comment.what_are_you
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_scoped
|
||||
assert_equal 4, Comment.search_by_type('Comment').size
|
||||
assert_equal 2, @welcome.comments.search_by_type('Comment').size
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 4, Comment.find_all_by_type('Comment').size
|
||||
assert_equal 2, @welcome.comments.find_all_by_type('Comment').size
|
||||
end
|
||||
|
||||
def test_nested_scope_finder
|
||||
Comment.where('1=0').scoping do
|
||||
assert_equal 0, @welcome.comments.count
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
|
||||
Comment.where('1=1').scoping do
|
||||
assert_equal 2, @welcome.comments.count
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
|
||||
fixtures :posts, :categories, :categories_posts
|
||||
|
||||
def setup
|
||||
@welcome = Post.find(1)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_methods
|
||||
assert_equal 'a category...', Category.what_are_you
|
||||
assert_equal 'a category...', @welcome.categories.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 4, Category.find_all_by_type('SpecialCategory').size
|
||||
assert_equal 0, @welcome.categories.find_all_by_type('SpecialCategory').size
|
||||
assert_equal 2, @welcome.categories.find_all_by_type('Category').size
|
||||
end
|
||||
|
||||
def test_nested_scope_finder
|
||||
Category.where('1=0').scoping do
|
||||
assert_equal 0, @welcome.categories.count
|
||||
assert_equal 'a category...', @welcome.categories.what_are_you
|
||||
end
|
||||
|
||||
Category.where('1=1').scoping do
|
||||
assert_equal 2, @welcome.categories.count
|
||||
assert_equal 'a category...', @welcome.categories.what_are_you
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultScopingTest < ActiveRecord::TestCase
|
||||
fixtures :developers, :posts
|
||||
|
||||
def test_default_scope
|
||||
expected = Developer.find(:all, :order => 'salary DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_is_unscoped_on_find
|
||||
assert_equal 1, DeveloperCalledDavid.count
|
||||
assert_equal 11, DeveloperCalledDavid.unscoped.count
|
||||
end
|
||||
|
||||
def test_default_scope_is_unscoped_on_create
|
||||
assert_nil DeveloperCalledJamis.unscoped.create!.name
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_string
|
||||
assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.find(:all).map(&:id).sort
|
||||
assert_equal nil, DeveloperCalledDavid.create!.name
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_hash
|
||||
assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.find(:all).map(&:id).sort
|
||||
assert_equal 'Jamis', DeveloperCalledJamis.create!.name
|
||||
end
|
||||
|
||||
def test_default_scoping_with_threads
|
||||
2.times do
|
||||
Thread.new { assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values }.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_scoping_with_inheritance
|
||||
# Inherit a class having a default scope and define a new default scope
|
||||
klass = Class.new(DeveloperOrderedBySalary)
|
||||
klass.send :default_scope, :limit => 1
|
||||
|
||||
# Scopes added on children should append to parent scope
|
||||
assert_equal 1, klass.scoped.limit_value
|
||||
assert_equal ['salary DESC'], klass.scoped.order_values
|
||||
|
||||
# Parent should still have the original scope
|
||||
assert_nil DeveloperOrderedBySalary.scoped.limit_value
|
||||
assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values
|
||||
end
|
||||
|
||||
def test_default_scope_called_twice_merges_conditions
|
||||
Developer.destroy_all
|
||||
Developer.create!(:name => "David", :salary => 80000)
|
||||
Developer.create!(:name => "David", :salary => 100000)
|
||||
Developer.create!(:name => "Brian", :salary => 100000)
|
||||
|
||||
klass = Class.new(Developer)
|
||||
klass.__send__ :default_scope, :conditions => { :name => "David" }
|
||||
klass.__send__ :default_scope, :conditions => { :salary => 100000 }
|
||||
assert_equal 1, klass.count
|
||||
assert_equal "David", klass.first.name
|
||||
assert_equal 100000, klass.first.salary
|
||||
end
|
||||
def test_method_scope
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_nested_scope
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.send(:with_scope, :find => { :order => 'name DESC'}) do
|
||||
DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
end
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_named_scope_overwrites_default
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name }
|
||||
received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_nested_exclusive_scope
|
||||
expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do
|
||||
DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
end
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_overwriting_default_scope
|
||||
expected = Developer.find(:all, :order => 'salary').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.find(:all, :order => 'salary').collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_using_relation
|
||||
posts = PostWithComment.scoped
|
||||
assert_equal 2, posts.count
|
||||
assert_equal posts(:thinking), posts.first
|
||||
end
|
||||
|
||||
def test_create_attribute_overwrites_default_scoping
|
||||
assert_equal 'David', PoorDeveloperCalledJamis.create!(:name => 'David').name
|
||||
assert_equal 200000, PoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary
|
||||
end
|
||||
|
||||
def test_create_attribute_overwrites_default_values
|
||||
assert_equal nil, PoorDeveloperCalledJamis.create!(:salary => nil).salary
|
||||
assert_equal 50000, PoorDeveloperCalledJamis.create!(:name => 'David').salary
|
||||
end
|
||||
end
|
|
@ -618,7 +618,7 @@ class RelationTest < ActiveRecord::TestCase
|
|||
end
|
||||
|
||||
def test_anonymous_extension
|
||||
relation = Post.where(:author_id => 1).order('id ASC').extend do
|
||||
relation = Post.where(:author_id => 1).order('id ASC').extending do
|
||||
def author
|
||||
'lifo'
|
||||
end
|
||||
|
|
|
@ -55,6 +55,12 @@ class Developer < ActiveRecord::Base
|
|||
def log=(message)
|
||||
audit_logs.build :message => message
|
||||
end
|
||||
|
||||
def self.all_johns
|
||||
self.with_exclusive_scope :find => where(:name => 'John') do
|
||||
self.all
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class AuditLog < ActiveRecord::Base
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
class WithoutTable < ActiveRecord::Base
|
||||
default_scope where(:published => true)
|
||||
end
|
|
@ -1,5 +1,11 @@
|
|||
*Rails 3.0.0 [Release Candidate] (unreleased)*
|
||||
|
||||
* Deprecation behavior is no longer hardcoded to the name of the environment.
|
||||
Instead, it is set via config.active_support.deprecation and can be one
|
||||
of :log, :stderr or :notify. :notify is a new style that sends the warning
|
||||
via ActiveSupport::Notifications, and is the new default for production
|
||||
[Yehuda Katz]
|
||||
|
||||
* Renamed ActiveSupport::Dependecies.load_(once_)paths to autoload_(once_)paths. [fxn]
|
||||
|
||||
* Added ActiveSupport::FileUpdateChecker to execute a block only if a set of files changed, used by Router and I18n locale files. [José Valim]
|
||||
|
|
|
@ -28,9 +28,8 @@ class String
|
|||
self[0]
|
||||
end unless method_defined?(:ord)
|
||||
|
||||
def getbyte(index)
|
||||
self[index]
|
||||
end if RUBY_VERSION < '1.9'
|
||||
# +getbyte+ backport from Ruby 1.9
|
||||
alias_method :getbyte, :[] unless method_defined?(:getbyte)
|
||||
|
||||
# Form can be either :utc (default) or :local.
|
||||
def to_time(form = :utc)
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
require "active_support/notifications"
|
||||
|
||||
module ActiveSupport
|
||||
module Deprecation
|
||||
class << self
|
||||
# Behavior is a block that takes a message argument.
|
||||
attr_writer :behavior
|
||||
|
||||
# Whether to print a backtrace along with the warning.
|
||||
attr_accessor :debug
|
||||
|
||||
def behavior
|
||||
@behavior ||= default_behavior
|
||||
@behavior ||= DEFAULT_BEHAVIORS[:stderr]
|
||||
end
|
||||
|
||||
def default_behavior
|
||||
Deprecation::DEFAULT_BEHAVIORS[defined?(Rails.env) ? Rails.env.to_s : 'test']
|
||||
def behavior=(behavior)
|
||||
@behavior = DEFAULT_BEHAVIORS[behavior] || behavior
|
||||
end
|
||||
end
|
||||
|
||||
# Default warning behaviors per Rails.env. Ignored in production.
|
||||
# Default warning behaviors per Rails.env.
|
||||
DEFAULT_BEHAVIORS = {
|
||||
'test' => Proc.new { |message, callstack|
|
||||
:stderr => Proc.new { |message, callstack|
|
||||
$stderr.puts(message)
|
||||
$stderr.puts callstack.join("\n ") if debug
|
||||
},
|
||||
'development' => Proc.new { |message, callstack|
|
||||
:log => Proc.new { |message, callstack|
|
||||
logger =
|
||||
if defined?(Rails) && Rails.logger
|
||||
Rails.logger
|
||||
|
@ -32,6 +31,10 @@ module ActiveSupport
|
|||
end
|
||||
logger.warn message
|
||||
logger.debug callstack.join("\n ") if debug
|
||||
},
|
||||
:notify => Proc.new { |message, callstack|
|
||||
ActiveSupport::Notifications.instrument("deprecation.rails",
|
||||
:message => message, :callstack => callstack)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -105,7 +105,7 @@ module ActiveSupport #:nodoc:
|
|||
# Example:
|
||||
# ('Café'.mb_chars + ' périferôl').to_s #=> "Café périferôl"
|
||||
def +(other)
|
||||
self << other
|
||||
chars(@wrapped_string + other)
|
||||
end
|
||||
|
||||
# Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
|
||||
|
@ -316,11 +316,12 @@ module ActiveSupport #:nodoc:
|
|||
result = @wrapped_string.slice(*args)
|
||||
elsif args.size == 1 && args[0].kind_of?(Numeric)
|
||||
character = Unicode.u_unpack(@wrapped_string)[args[0]]
|
||||
result = character.nil? ? nil : [character].pack('U')
|
||||
result = character && [character].pack('U')
|
||||
else
|
||||
result = Unicode.u_unpack(@wrapped_string).slice(*args).pack('U*')
|
||||
cps = Unicode.u_unpack(@wrapped_string).slice(*args)
|
||||
result = cps && cps.pack('U*')
|
||||
end
|
||||
result.nil? ? nil : chars(result)
|
||||
result && chars(result)
|
||||
end
|
||||
alias_method :[], :slice
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ module ActiveSupport
|
|||
NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
|
||||
|
||||
# The Unicode version that is supported by the implementation
|
||||
UNICODE_VERSION = '5.1.0'
|
||||
UNICODE_VERSION = '5.2.0'
|
||||
|
||||
# The default normalization used for operations that require normalization. It can be set to any of the
|
||||
# normalizations in NORMALIZATION_FORMS.
|
||||
|
|
|
@ -12,6 +12,36 @@ module ActiveSupport
|
|||
require 'active_support/whiny_nil' if app.config.whiny_nils
|
||||
end
|
||||
|
||||
initializer "active_support.deprecation_behavior" do |app|
|
||||
if deprecation = app.config.active_support.deprecation
|
||||
ActiveSupport::Deprecation.behavior = deprecation
|
||||
else
|
||||
defaults = {"development" => :log,
|
||||
"production" => :notify,
|
||||
"test" => :stderr}
|
||||
|
||||
env = Rails.env
|
||||
|
||||
if defaults.key?(env)
|
||||
msg = "You did not specify how you would like Rails to report " \
|
||||
"deprecation notices for your #{env} environment, please " \
|
||||
"set config.active_support.deprecation to :#{defaults[env]} " \
|
||||
"at config/environments/#{env}.rb"
|
||||
|
||||
warn msg
|
||||
ActiveSupport::Deprecation.behavior = defaults[env]
|
||||
else
|
||||
msg = "You did not specify how you would like Rails to report " \
|
||||
"deprecation notices for your #{env} environment, please " \
|
||||
"set config.active_support.deprecation to :log, :notify or " \
|
||||
":stderr at config/environments/#{env}.rb"
|
||||
|
||||
warn msg
|
||||
ActiveSupport::Deprecation.behavior = :stderr
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the default value for Time.zone
|
||||
# If assigned value cannot be matched to a TimeZone, an exception will be raised.
|
||||
initializer "active_support.initialize_time_zone" do |app|
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue