aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--actionpack/CHANGELOG10
-rw-r--r--actionpack/actionpack.gemspec4
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/node.rb1
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb31
-rw-r--r--actionpack/lib/action_dispatch/routing/deprecated_mapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb205
-rw-r--r--actionpack/lib/action_dispatch/routing/route.rb13
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb12
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount.rb32
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb60
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb74
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb159
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb113
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb210
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb53
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb36
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/regexp_with_named_groups.rb69
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb130
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb409
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb68
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb160
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y34
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb83
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex12
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb148
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb569
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb185
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/nested_multimap.rb158
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb45
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/alternation.rb40
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/anchor.rb4
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb59
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character.rb56
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character_class.rb55
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/collection.rb83
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/expression.rb126
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/group.rb90
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/options.rb55
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb415
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/tokenizer.rb213
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/version.rb3
-rw-r--r--actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb5
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb17
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb50
-rw-r--r--actionpack/lib/action_view/log_subscriber.rb5
-rw-r--r--actionpack/lib/action_view/template.rb19
-rw-r--r--actionpack/lib/action_view/test_case.rb2
-rw-r--r--actionpack/test/abstract_unit.rb11
-rw-r--r--actionpack/test/activerecord/polymorphic_routes_test.rb30
-rw-r--r--actionpack/test/controller/base_test.rb3
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb22
-rw-r--r--actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb23
-rw-r--r--actionpack/test/dispatch/routing_test.rb82
-rw-r--r--actionpack/test/template/erb/form_for_test.rb2
-rw-r--r--actionpack/test/template/erb/tag_helper_test.rb4
-rw-r--r--actionpack/test/template/form_helper_test.rb385
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb56
-rw-r--r--actionpack/test/template/html-scanner/sanitizer_test.rb4
-rw-r--r--actionpack/test/template/tag_helper_test.rb4
-rw-r--r--actionpack/test/template/url_helper_test.rb33
-rw-r--r--activemodel/test/cases/validations/numericality_validation_test.rb2
-rw-r--r--activerecord/CHANGELOG8
-rw-r--r--activerecord/Rakefile4
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb11
-rw-r--r--activerecord/lib/active_record/associations/has_many_association.rb6
-rw-r--r--activerecord/lib/active_record/base.rb75
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb1
-rw-r--r--activerecord/lib/active_record/named_scope.rb33
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/railties/databases.rake44
-rw-r--r--activerecord/lib/active_record/relation.rb45
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb14
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb (renamed from activerecord/test/cases/adapters/sqlite/copy_table_test.rb)0
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb (renamed from activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb)0
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb62
-rw-r--r--activerecord/test/cases/base_test.rb19
-rw-r--r--activerecord/test/cases/inheritance_test.rb2
-rw-r--r--activerecord/test/cases/method_scoping_test.rb213
-rw-r--r--activerecord/test/cases/migration_test.rb17
-rw-r--r--activerecord/test/cases/named_scope_test.rb10
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb396
-rw-r--r--activerecord/test/cases/relations_test.rb2
-rw-r--r--activerecord/test/models/developer.rb8
-rw-r--r--activerecord/test/models/without_table.rb3
-rw-r--r--activesupport/CHANGELOG6
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb5
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb21
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb9
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb2
-rw-r--r--activesupport/lib/active_support/railtie.rb32
-rw-r--r--activesupport/lib/active_support/testing/performance.rb54
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb6
-rw-r--r--activesupport/lib/active_support/values/unicode_tables.datbin710743 -> 813343 bytes
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb4
-rw-r--r--activesupport/test/multibyte_chars_test.rb15
-rw-r--r--activesupport/test/time_zone_test.rb7
-rw-r--r--railties/CHANGELOG4
-rw-r--r--railties/lib/rails/application.rb7
-rw-r--r--railties/lib/rails/application/configuration.rb4
-rw-r--r--railties/lib/rails/engine.rb21
-rw-r--r--railties/lib/rails/engine/configuration.rb4
-rw-r--r--railties/lib/rails/generators/actions.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js171
-rw-r--r--railties/lib/rails/paths.rb51
-rw-r--r--railties/lib/rails/plugin.rb16
-rw-r--r--railties/test/application/loading_test.rb15
-rw-r--r--railties/test/application/url_generation_test.rb1
-rw-r--r--railties/test/isolation/abstract_unit.rb3
-rw-r--r--railties/test/paths_test.rb56
-rw-r--r--railties/test/railties/engine_test.rb4
-rw-r--r--railties/test/railties/plugin_test.rb30
-rw-r--r--railties/test/railties/shared_tests.rb45
126 files changed, 5690 insertions, 970 deletions
diff --git a/Gemfile b/Gemfile
index d0b11b04e4..827c67e522 100644
--- a/Gemfile
+++ b/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"
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index b17a48003c..32aba2091a 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -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]
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 1dede257f9..b4311599a9 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -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
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index b6d4fb1284..89201fb5ee 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -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
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
index 6c0331636c..a874519978 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
@@ -177,6 +177,7 @@ module HTML #:nodoc:
case text
when "\\" then
value << text
+ break if scanner.eos?
value << scanner.getch
when delim
break
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
index 064ff3724d..240dc1890f 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 9b9e81440b..e9fdf75cc8 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -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
@@ -137,4 +137,4 @@ module ActionDispatch
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 0a37bd7fc1..add8cab2ab 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
index 61c2253763..bc3f62fb48 100644
--- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 388f695187..0b4ba8c9f5 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -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,12 +682,8 @@ 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
- yield
- end
- end
+ new_scope do
+ yield
end
end
@@ -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,48 +883,30 @@ 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)"
- 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
+ prefix = shallow_scoping? ?
+ "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path]
+
+ if canonical_action?(action, path.blank?)
+ "#{prefix}(.:format)"
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]
+ if shallow_scoping?
+ "#{@scope[:shallow_path]}/#{parent_resource.path}/:id"
else
- if parent_resource.shallow?
- "#{@scope[:shallow_path]}/#{parent_resource.path}/:id"
- else
- @scope[:path]
- end
+ @scope[:path]
end
end
@@ -917,50 +914,40 @@ module ActionDispatch
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}"
- end
- when :edit
- if parent_resource.shallow?
- "edit_#{shallow_prefix}#{parent_resource.member_name}"
- else
- "edit_#{name_prefix}#{parent_resource.member_name}"
- end
+ if parent_resource
+ collection_name = parent_resource.collection_name
+ member_name = parent_resource.member_name
+ name_prefix = "#{name_prefix}_" if name_prefix.present?
+ end
+
+ case @scope[:scope_level]
+ when :collection
+ "#{prefix}#{name_prefix}#{collection_name}"
when :new
- "new_#{name_prefix}#{parent_resource.member_name}"
+ "#{prefix}new_#{name_prefix}#{member_name}"
else
- case @scope[:scope_level]
- when :collection
- "#{action}_#{name_prefix}#{parent_resource.collection_name}"
- when :new
- "#{action}_new_#{name_prefix}#{parent_resource.member_name}"
+ if shallow_scoping?
+ shallow_prefix = "#{@scope[:shallow_prefix]}_" if @scope[:shallow_prefix].present?
+ "#{prefix}#{shallow_prefix}#{member_name}"
else
- if parent_resource.shallow?
- "#{action}_#{shallow_prefix}#{parent_resource.member_name}"
- else
- "#{action}_#{name_prefix}#{parent_resource.member_name}"
- end
+ "#{prefix}#{name_prefix}#{member_name}"
end
end
end
-
end
include Base
diff --git a/actionpack/lib/action_dispatch/routing/route.rb b/actionpack/lib/action_dispatch/routing/route.rb
index 6f37eadd6b..aefebf8f80 100644
--- a/actionpack/lib/action_dispatch/routing/route.rb
+++ b/actionpack/lib/action_dispatch/routing/route.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 7be79d3200..5ecad6bc04 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount.rb
new file mode 100644
index 0000000000..9fbf707724
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb
new file mode 100644
index 0000000000..671258f807
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb
new file mode 100644
index 0000000000..20aaa132f9
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb
new file mode 100644
index 0000000000..8a8c551302
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb
new file mode 100644
index 0000000000..903c79fdc6
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb
new file mode 100644
index 0000000000..47bbab3784
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb
new file mode 100644
index 0000000000..0f8eaaec67
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb
new file mode 100644
index 0000000000..58892e4c4f
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/regexp_with_named_groups.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/regexp_with_named_groups.rb
new file mode 100644
index 0000000000..c11292b2a2
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/regexp_with_named_groups.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb
new file mode 100644
index 0000000000..680c40f147
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb
new file mode 100644
index 0000000000..0e5a65a640
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb
new file mode 100644
index 0000000000..d0d8797008
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb
new file mode 100644
index 0000000000..cfe05afc61
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y
new file mode 100644
index 0000000000..ffbd9fae11
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb
new file mode 100644
index 0000000000..0ff7f67661
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex
new file mode 100644
index 0000000000..473bd096e1
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb
new file mode 100644
index 0000000000..aa23b1162f
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb
new file mode 100644
index 0000000000..0b49b49280
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb
new file mode 100644
index 0000000000..119bf12646
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/nested_multimap.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/nested_multimap.rb
new file mode 100644
index 0000000000..4eb088b91a
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/nested_multimap.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb
new file mode 100644
index 0000000000..d38922bcc6
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/alternation.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/alternation.rb
new file mode 100644
index 0000000000..ce4f52bfdb
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/alternation.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/anchor.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/anchor.rb
new file mode 100644
index 0000000000..05520dd5e0
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/anchor.rb
@@ -0,0 +1,4 @@
+module Regin
+ class Anchor < Atom
+ end
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb
new file mode 100644
index 0000000000..eb1923a5a1
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character.rb
new file mode 100644
index 0000000000..12a9199d2a
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character_class.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character_class.rb
new file mode 100644
index 0000000000..caed5ef9d0
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character_class.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/collection.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/collection.rb
new file mode 100644
index 0000000000..b60353268a
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/collection.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/expression.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/expression.rb
new file mode 100644
index 0000000000..18e4965097
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/expression.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/group.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/group.rb
new file mode 100644
index 0000000000..d682148bd9
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/group.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/options.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/options.rb
new file mode 100644
index 0000000000..03ba29d9a5
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/options.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb
new file mode 100644
index 0000000000..0bb9b87e9c
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb
@@ -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'
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/tokenizer.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/tokenizer.rb
new file mode 100644
index 0000000000..59e4ffb611
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/tokenizer.rb
@@ -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
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/version.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/version.rb
new file mode 100644
index 0000000000..7ad2a5a25e
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/version.rb
@@ -0,0 +1,3 @@
+module Regin
+ Version = '0.3.3'
+end
diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb
new file mode 100644
index 0000000000..a3688b102e
--- /dev/null
+++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb
@@ -0,0 +1,5 @@
+module Rack
+ module Mount
+ Version = '0.6.6.pre'
+ end
+end
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index ea1bf14c96..f9105ca364 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -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
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index ea491b2db8..4c1b751160 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -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 => "&#9731;".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)
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index bfb8f74a00..5c0ff5d59c 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -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
- self << line
- end
+ line = "#{line.to_s.chomp.gsub(/\;\z/, '')};"
+ self << line
+ line
end
def render(*options)
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index d4e8b3d587..5d032b32a7 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -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
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 6af11e632f..b8d6dc22f2 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -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&amp;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?
-
- email_address_obfuscated = html_escape(email_address)
+ 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 = 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
diff --git a/actionpack/lib/action_view/log_subscriber.rb b/actionpack/lib/action_view/log_subscriber.rb
index 4a52937c58..443a0eafd1 100644
--- a/actionpack/lib/action_view/log_subscriber.rb
+++ b/actionpack/lib/action_view/log_subscriber.rb
@@ -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
@@ -24,4 +25,4 @@ module ActionView
end
end
-ActionView::LogSubscriber.attach_to :action_view \ No newline at end of file
+ActionView::LogSubscriber.attach_to :action_view
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index c00557209b..40ff1f2182 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -156,11 +156,12 @@ module ActionView
end
def inspect
- if defined?(Rails.root)
- identifier.sub("#{Rails.root}/", '')
- else
- identifier
- end
+ @inspect ||=
+ if defined?(Rails.root)
+ identifier.sub("#{Rails.root}/", '')
+ else
+ identifier
+ end
end
private
@@ -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
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 757e4cf77c..e5614c9e3b 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -181,7 +181,7 @@ module ActionView
}
def _instance_variables
- instance_variables - EXCLUDE_IVARS
+ instance_variables.map(&:to_s) - EXCLUDE_IVARS
end
def _assigns
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 2640e96453..c8477fb83f 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -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.
diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb
index 6e1e6cdd20..90a1ef982c 100644
--- a/actionpack/test/activerecord/polymorphic_routes_test.rb
+++ b/actionpack/test/activerecord/polymorphic_routes_test.rb
@@ -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
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 4f58b5d968..ae270b751e 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -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
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index 40c5ac2d09..e3ec5cf182 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -89,15 +89,21 @@ 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
- params = parse_multipart('mixed_files')
- assert_equal %w(files foo), params.keys.sort
- assert_equal 'bar', params['foo']
-
- # Rack doesn't handle multipart/mixed for us.
- files = params['files']
- files.force_encoding('ASCII-8BIT') if files.respond_to?(:force_encoding)
- assert_equal 19756, files.size
+ 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']
+
+ # Rack doesn't handle multipart/mixed for us.
+ files = params['files']
+ 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
diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
index 69dbd7f528..0bcef81534 100644
--- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
@@ -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
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index cf92b039e3..26bd641cd6 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -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
diff --git a/actionpack/test/template/erb/form_for_test.rb b/actionpack/test/template/erb/form_for_test.rb
index ec6e872735..e722b40a9a 100644
--- a/actionpack/test/template/erb/form_for_test.rb
+++ b/actionpack/test/template/erb/form_for_test.rb
@@ -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
diff --git a/actionpack/test/template/erb/tag_helper_test.rb b/actionpack/test/template/erb/tag_helper_test.rb
index 64a88bde1d..d073100986 100644
--- a/actionpack/test/template/erb/tag_helper_test.rb
+++ b/actionpack/test/template/erb/tag_helper_test.rb
@@ -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
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 6ba407e230..4b9e41803f 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -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'>" +
- "<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>"
+ 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' />"
+ 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'>" +
- "<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>"
+ 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' />"
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ 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">' +
- '<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>'
+ 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" />'
+ end
assert_dom_equal expected, output_buffer
end
@@ -1273,20 +1268,20 @@ class FormHelperTest < ActionView::TestCase
end
end
- expected = '<form action="http://www.example.com" method="post">' +
- '<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" />' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
- '<input id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" size="30" type="text" value="tag #123" />' +
- '<input id="post_tags_attributes_0_relevances_attributes_0_value" name="post[tags_attributes][0][relevances_attributes][0][value]" size="30" type="text" value="tagrelevance #3141" />' +
- '<input id="post_tags_attributes_0_relevances_attributes_0_id" name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' +
- '<input id="post_tags_attributes_0_id" name="post[tags_attributes][0][id]" type="hidden" value="123" />' +
- '<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>'
+ 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" />' +
+ '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
+ '<input id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" size="30" type="text" value="tag #123" />' +
+ '<input id="post_tags_attributes_0_relevances_attributes_0_value" name="post[tags_attributes][0][relevances_attributes][0][value]" size="30" type="text" value="tagrelevance #3141" />' +
+ '<input id="post_tags_attributes_0_relevances_attributes_0_id" name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' +
+ '<input id="post_tags_attributes_0_id" name="post[tags_attributes][0][id]" type="hidden" value="123" />' +
+ '<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" />'
+ 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'>" +
- "<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>"
+ 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' />"
+ 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'>" +
- "<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>"
+ 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/>"
+ 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="&#9731;" />}
+ 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
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index b75863bb6a..8a0f352bc0 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -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="&#9731;" />}
+ 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
diff --git a/actionpack/test/template/html-scanner/sanitizer_test.rb b/actionpack/test/template/html-scanner/sanitizer_test.rb
index a6e760b0b6..c9edde8892 100644
--- a/actionpack/test/template/html-scanner/sanitizer_test.rb
+++ b/actionpack/test/template/html-scanner/sanitizer_test.rb
@@ -257,6 +257,10 @@ class SanitizerTest < ActionController::TestCase
assert_sanitized %{<a href=\"http://www.domain.com?var1=1&amp;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
diff --git a/actionpack/test/template/tag_helper_test.rb b/actionpack/test/template/tag_helper_test.rb
index ec5fe3d1d7..507cdca8d0 100644
--- a/actionpack/test/template/tag_helper_test.rb
+++ b/actionpack/test/template/tag_helper_test.rb
@@ -95,9 +95,9 @@ class TagHelperTest < ActionView::TestCase
assert_equal '1 &lt; 2 &amp; 3', escape_once('1 < 2 &amp; 3')
end
- def test_double_escaping_attributes
+ def test_tag_honors_html_safe_for_param_values
['1&amp;2', '1 &lt; 2', '&#8220;test&#8220;'].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
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 72d4897630..765beebfa3 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -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&amp;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&amp;c=d", url_for("/?a=b&amp;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&amp;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&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2")
+ def test_button_to_with_html_safe_URL
+ assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;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&amp;q2=v2">Hello</a>}
- assert_dom_equal expected, link_to("Hello", "http://www.example.com?q1=v1&amp;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&amp;q2=v2")
expected = %{<a href="http://www.example.com?q1=v1&amp;q2=v2">http://www.example.com?q1=v1&amp;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&amp;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&amp;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&amp;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('&amp;').sort.join('&amp;') if qs
qs ? "#{path}?#{qs}" : path
end
end
diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb
index 8e77a0222e..e1d7d40c47 100644
--- a/activemodel/test/cases/validations/numericality_validation_test.rb
+++ b/activemodel/test/cases/validations/numericality_validation_test.rb
@@ -4,6 +4,8 @@ require 'cases/helper'
require 'models/topic'
require 'models/person'
+require 'bigdecimal'
+
class NumericalityValidationTest < ActiveModel::TestCase
def teardown
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 09f897eaac..a1a82fdff5 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -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)*
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 22a17a62af..392b717e0a 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -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
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 186b531ffb..ddf4ce4058 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -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) }
diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb
index 92c6b3e770..d74fb7c702 100644
--- a/activerecord/lib/active_record/associations/has_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_association.rb
@@ -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
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index e7b52287a5..c8795e4496 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -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
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 0216a8f4ac..76b65bf219 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -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.
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index b273c33e50..4c5e1ae218 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -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.
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index ec0a98c6df..c010dac64e 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -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
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 9e28aa2a05..50166c4385 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -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
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 006e64b115..7882f05d07 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -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/
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index fd0660a138..bc708b573f 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -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
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index d853fd63d1..5cea2328e8 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -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
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 015ca8c24c..4692271266 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -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)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index bb1f138f5b..7712ad2569 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -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
diff --git a/activerecord/test/cases/adapters/sqlite/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index 575b4806c1..575b4806c1 100644
--- a/activerecord/test/cases/adapters/sqlite/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
diff --git a/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 934cf72f72..934cf72f72 100644
--- a/activerecord/test/cases/adapters/sqlite/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 45c7498013..5e3ba778f3 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -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
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index f8b90d8877..ba7db838ca 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -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
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index c1c8f01e46..8c09fc4d59 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -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'")
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 6cd42ff936..4e8ce1dac1 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -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
@@ -533,205 +543,4 @@ class NestedScopingTest < ActiveRecord::TestCase
assert_equal 1, scoped_authors.size
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
+end \ No newline at end of file
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 99a3a12a8b..4ce9bdb46d 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -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
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 33ffb041c1..dc85b395d3 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -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
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
new file mode 100644
index 0000000000..41dcdbcd37
--- /dev/null
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -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 \ No newline at end of file
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index d27a69ea1c..ffde8daa07 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -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
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index e35de3b9b0..de68fd7f24 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -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
@@ -103,4 +109,4 @@ end
class PoorDeveloperCalledJamis < ActiveRecord::Base
self.table_name = 'developers'
default_scope :conditions => { :name => 'Jamis', :salary => 50000 }
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/without_table.rb b/activerecord/test/models/without_table.rb
new file mode 100644
index 0000000000..87f80911e1
--- /dev/null
+++ b/activerecord/test/models/without_table.rb
@@ -0,0 +1,3 @@
+class WithoutTable < ActiveRecord::Base
+ default_scope where(:published => true)
+end \ No newline at end of file
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 59ad57ede8..60586cf99a 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -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]
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index af277f9995..5b2cb6e331 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -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)
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 578c025fcf..feb1508586 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -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
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index c107aad6bb..8823e4a5ed 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -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
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index f91e50c755..11c72d873b 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -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.
diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb
index 1f32f8718f..c2deba3b1b 100644
--- a/activesupport/lib/active_support/railtie.rb
+++ b/activesupport/lib/active_support/railtie.rb
@@ -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|
@@ -27,4 +57,4 @@ module ActiveSupport
Time.zone_default = zone_default
end
end
-end \ No newline at end of file
+end
diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb
index cd628a956d..a124c6e0ca 100644
--- a/activesupport/lib/active_support/testing/performance.rb
+++ b/activesupport/lib/active_support/testing/performance.rb
@@ -260,16 +260,14 @@ begin
end
protected
- # Ruby 1.9 + extented GC profiler patch
- if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
+ # Ruby 1.9 with GC::Profiler
+ if defined?(GC::Profiler)
def with_gc_stats
- GC.start
- GC.disable
GC::Profiler.enable
+ GC.start
yield
ensure
GC::Profiler.disable
- GC.enable
end
# Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker)
@@ -294,7 +292,7 @@ begin
end
def format(measurement)
- if measurement < 2
+ if measurement < 1
'%d ms' % (measurement * 1000)
else
'%.2f sec' % measurement
@@ -335,14 +333,10 @@ begin
class Memory < Base
Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY)
- # Ruby 1.9 + extended GC profiler patch
- if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
+ # Ruby 1.9 + GCdata patch
+ if GC.respond_to?(:malloc_allocated_size)
def measure
- GC.enable
- GC.start
- kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0
- GC.disable
- kb
+ GC.malloc_allocated_size / 1024.0
end
# Ruby 1.8 + ruby-prof wrapper
@@ -360,14 +354,10 @@ begin
class Objects < Base
Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS)
- # Ruby 1.9 + extented GC profiler patch
- if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
+ # Ruby 1.9 + GCdata patch
+ if GC.respond_to?(:malloc_allocations)
def measure
- GC.enable
- GC.start
- count = GC::Profiler.data.last[:HEAP_TOTAL_OBJECTS]
- GC.disable
- count
+ GC.malloc_allocations
end
# Ruby 1.8 + ruby-prof wrapper
@@ -385,14 +375,10 @@ begin
class GcRuns < Base
Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS)
- # Ruby 1.9 + extented GC profiler patch
- if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
+ # Ruby 1.9
+ if GC.respond_to?(:count)
def measure
- GC.enable
- GC.start
- count = GC::Profiler.data.last[:GC_RUNS]
- GC.disable
- count
+ GC.count
end
# Ruby 1.8 + ruby-prof wrapper
@@ -410,25 +396,21 @@ begin
class GcTime < Base
Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME)
- # Ruby 1.9 + extented GC profiler patch
- if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data)
+ # Ruby 1.9 with GC::Profiler
+ if GC.respond_to?(:total_time)
def measure
- GC.enable
- GC.start
- sec = GC::Profiler.data.inject(0) { |total, run| total += run[:GC_TIME] }
- GC.disable
- sec
+ GC::Profiler.total_time
end
# Ruby 1.8 + ruby-prof wrapper
elsif RubyProf.respond_to?(:measure_gc_time)
def measure
- RubyProf.measure_gc_time
+ RubyProf.measure_gc_time / 1000
end
end
def format(measurement)
- '%d ms' % (measurement / 1000)
+ '%.2f ms' % measurement
end
end
end
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index 0a08016522..05b40298d3 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -350,7 +350,11 @@ module ActiveSupport
def [](arg)
case arg
when String
- zones_map[arg] ||= lookup(arg)
+ begin
+ zones_map[arg] ||= lookup(arg).tap { |tz| tz.utc_offset }
+ rescue TZInfo::InvalidTimezoneIdentifier
+ nil
+ end
when Numeric, ActiveSupport::Duration
arg *= 3600 if arg.abs <= 13
all.find { |z| z.utc_offset == arg.to_i }
diff --git a/activesupport/lib/active_support/values/unicode_tables.dat b/activesupport/lib/active_support/values/unicode_tables.dat
index 10f2cae465..4fe0268cca 100644
--- a/activesupport/lib/active_support/values/unicode_tables.dat
+++ b/activesupport/lib/active_support/values/unicode_tables.dat
Binary files differ
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index cf11f4d28f..5ce4277672 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -832,9 +832,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase
def test_time_zone_setter_with_invalid_zone
Time.zone = 'foo'
- assert_not_nil Time.zone
- assert_equal 'foo', Time.zone.name
- assert_raise(TZInfo::InvalidTimezoneIdentifier) { Time.zone.utc_offset }
+ assert_nil Time.zone
Time.zone = -15.hours
assert_nil Time.zone
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 602828ef5f..610295fa89 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -49,13 +49,15 @@ class MultibyteCharsTest < Test::Unit::TestCase
end
def test_should_concatenate
- assert_equal 'ab', 'a'.mb_chars + 'b'
- assert_equal 'ab', 'a' + 'b'.mb_chars
- assert_equal 'ab', 'a'.mb_chars + 'b'.mb_chars
+ mb_a = 'a'.mb_chars
+ mb_b = 'b'.mb_chars
+ assert_equal 'ab', mb_a + 'b'
+ assert_equal 'ab', 'a' + mb_b
+ assert_equal 'ab', mb_a + mb_b
- assert_equal 'ab', 'a'.mb_chars << 'b'
- assert_equal 'ab', 'a' << 'b'.mb_chars
- assert_equal 'ab', 'a'.mb_chars << 'b'.mb_chars
+ assert_equal 'ab', mb_a << 'b'
+ assert_equal 'ab', 'a' << mb_b
+ assert_equal 'abb', mb_a << mb_b
end
def test_consumes_utf8_strings
@@ -395,6 +397,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
assert_equal 'こ', @chars.slice(0)
assert_equal 'わ', @chars.slice(3)
assert_equal nil, ''.mb_chars.slice(-1..1)
+ assert_equal nil, ''.mb_chars.slice(-1, 1)
assert_equal '', ''.mb_chars.slice(0..10)
assert_equal 'にちわ', @chars.slice(1..3)
assert_equal 'にちわ', @chars.slice(1, 3)
diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb
index 620623b389..af6eee69e5 100644
--- a/activesupport/test/time_zone_test.rb
+++ b/activesupport/test/time_zone_test.rb
@@ -268,7 +268,7 @@ class TimeZoneTest < Test::Unit::TestCase
end
def test_index
- assert_not_nil ActiveSupport::TimeZone["bogus"]
+ assert_nil ActiveSupport::TimeZone["bogus"]
assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone["Central Time (US & Canada)"]
assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[8]
assert_raise(ArgumentError) { ActiveSupport::TimeZone[false] }
@@ -285,6 +285,11 @@ class TimeZoneTest < Test::Unit::TestCase
assert_equal(-21_600, zone.utc_offset)
end
+ def test_unknown_zones_dont_store_mapping_keys
+ ActiveSupport::TimeZone["bogus"]
+ assert !ActiveSupport::TimeZone.zones_map.key?("bogus")
+ end
+
def test_new
assert_equal ActiveSupport::TimeZone["Central Time (US & Canada)"], ActiveSupport::TimeZone.new("Central Time (US & Canada)")
end
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 61d84e104a..8e2bac7f00 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,9 @@
*Rails 3.0.0 [Release Candidate] (unreleased)*
+* Rails no longer autoload code in lib for application. You need to explicitly require it. [José Valim]
+
+* Rails::LogSubscriber was renamed to ActiveSupport::LogSubscriber [José Valim]
+
* config.load_(once_)paths in config/application.rb got renamed to config.autoload_(once_)paths. [fxn]
* Abort generation/booting on Ruby 1.9.1. [fxn]
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index aabe86715c..4a7ed2d028 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -28,7 +28,7 @@ module Rails
# Besides providing the same configuration as Rails::Engine and Rails::Railtie,
# the application object has several specific configurations, for example
# "allow_concurrency", "cache_classes", "consider_all_requests_local", "filter_parameters",
- # "logger", "reload_engines", "reload_plugins" and so forth.
+ # "logger", "reload_plugins" and so forth.
#
# Check Rails::Application::Configuration to see them all.
#
@@ -217,10 +217,5 @@ module Rails
def initialize_generators
require "rails/generators"
end
-
- # Application is always reloadable when config.cache_classes is false.
- def reloadable?(app)
- true
- end
end
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index e3165b2d4c..465851c0e6 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -10,7 +10,7 @@ module Rails
attr_accessor :allow_concurrency, :cache_classes, :cache_store,
:encoding, :consider_all_requests_local, :dependency_loading,
:filter_parameters, :log_level, :logger, :middleware,
- :plugins, :preload_frameworks, :reload_engines, :reload_plugins,
+ :plugins, :preload_frameworks, :reload_plugins,
:secret_token, :serve_static_assets, :session_options,
:time_zone, :whiny_nils
@@ -59,7 +59,7 @@ module Rails
if File.exists?("#{root}/test/mocks/#{Rails.env}")
ActiveSupport::Deprecation.warn "\"Rails.root/test/mocks/#{Rails.env}\" won't be added " <<
"automatically to load paths anymore in future releases"
- paths.mocks_path "test/mocks", :load_path => true, :glob => Rails.env
+ paths.mocks_path "test/mocks", :autoload => true, :glob => Rails.env
end
paths
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 0a3f21fb1b..ee3e3ba040 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -142,7 +142,7 @@ module Rails
# Add configured load paths to ruby load paths and remove duplicates.
initializer :set_load_path, :before => :bootstrap_hook do
- config.autoload_paths.reverse_each do |path|
+ _all_load_paths.reverse_each do |path|
$LOAD_PATH.unshift(path) if File.directory?(path)
end
$LOAD_PATH.uniq!
@@ -154,16 +154,12 @@ module Rails
# This needs to be an initializer, since it needs to run once
# per engine and get the engine as a block parameter
initializer :set_autoload_paths, :before => :bootstrap_hook do |app|
- ActiveSupport::Dependencies.autoload_paths.unshift(*config.autoload_paths)
-
- if reloadable?(app)
- ActiveSupport::Dependencies.autoload_once_paths.unshift(*config.autoload_once_paths)
- else
- ActiveSupport::Dependencies.autoload_once_paths.unshift(*config.autoload_paths)
- end
+ ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
+ ActiveSupport::Dependencies.autoload_once_paths.unshift(*config.autoload_once_paths)
# Freeze so future modifications will fail rather than do nothing mysteriously
config.autoload_paths.freeze
+ config.eager_load_paths.freeze
config.autoload_once_paths.freeze
end
@@ -195,7 +191,6 @@ module Rails
ActiveSupport.on_load(:action_controller) do
prepend_view_path(views)
end
-
ActiveSupport.on_load(:action_mailer) do
prepend_view_path(views)
end
@@ -214,8 +209,12 @@ module Rails
protected
- def reloadable?(app)
- app.config.reload_engines
+ def _all_autoload_paths
+ @_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
+ end
+
+ def _all_load_paths
+ @_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
end
end
end
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 4e27180f1e..2f465670cf 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -42,11 +42,11 @@ module Rails
end
def autoload_once_paths
- @autoload_once_paths ||= paths.load_once
+ @autoload_once_paths ||= paths.autoload_once
end
def autoload_paths
- @autoload_paths ||= paths.load_paths
+ @autoload_paths ||= paths.autoload_paths
end
end
end
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 7af329bbf0..199afbdc30 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -275,7 +275,7 @@ module Rails
#
def route(routing_code)
log :route, routing_code
- sentinel = "routes.draw do |map|"
+ sentinel = /\.routes\.draw do(\s*\|map\|)?\s*$/
in_root do
inject_into_file 'config/routes.rb', "\n #{routing_code}\n", { :after => sentinel, :verbose => false }
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index f0e917dd96..99758dfcf7 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -16,4 +16,7 @@
# Don't care if the mailer can't send
config.action_mailer.raise_delivery_errors = false
+
+ # Print deprecation notices to the Rails logger
+ config.active_support.deprecation = :log
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index b9fb13b640..500fc4d860 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -43,4 +43,7 @@
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners
+ config.active_support.deprecation = :notify
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
index beb28e2229..26cdef071a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt
@@ -29,4 +29,7 @@
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.schema_format = :sql
+
+ # Print deprecation notices to the stderr
+ config.active_support.deprecation = :stderr
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
index d6c0365c04..d42cf3bfdf 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
@@ -1,4 +1,4 @@
-<%= app_const %>.routes.draw do |map|
+<%= app_const %>.routes.draw do
# The priority is based upon order of creation:
# first created -> highest priority.
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js
index c5fa02ae35..4283ed8982 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js
+++ b/railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js
@@ -1,87 +1,148 @@
-document.observe("dom:loaded", function() {
+(function() {
+ // Technique from Juriy Zaytsev
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
+ function isEventSupported(eventName) {
+ var el = document.createElement('div');
+ eventName = 'on' + eventName;
+ var isSupported = (eventName in el);
+ if (!isSupported) {
+ el.setAttribute(eventName, 'return;');
+ isSupported = typeof el[eventName] == 'function';
+ }
+ el = null;
+ return isSupported;
+ }
+
+ function isForm(element) {
+ return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
+ }
+
+ function isInput(element) {
+ if (Object.isElement(element)) {
+ var name = element.nodeName.toUpperCase()
+ return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
+ }
+ else return false
+ }
+
+ var submitBubbles = isEventSupported('submit'),
+ changeBubbles = isEventSupported('change')
+
+ if (!submitBubbles || !changeBubbles) {
+ // augment the Event.Handler class to observe custom events when needed
+ Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
+ function(init, element, eventName, selector, callback) {
+ init(element, eventName, selector, callback)
+ // is the handler being attached to an element that doesn't support this event?
+ if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
+ (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
+ // "submit" => "emulated:submit"
+ this.eventName = 'emulated:' + this.eventName
+ }
+ }
+ )
+ }
+
+ if (!submitBubbles) {
+ // discover forms on the page by observing focus events which always bubble
+ document.on('focusin', 'form', function(focusEvent, form) {
+ // special handler for the real "submit" event (one-time operation)
+ if (!form.retrieve('emulated:submit')) {
+ form.on('submit', function(submitEvent) {
+ var emulated = form.fire('emulated:submit', submitEvent, true)
+ // if custom event received preventDefault, cancel the real one too
+ if (emulated.returnValue === false) submitEvent.preventDefault()
+ })
+ form.store('emulated:submit', true)
+ }
+ })
+ }
+
+ if (!changeBubbles) {
+ // discover form inputs on the page
+ document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
+ // special handler for real "change" events
+ if (!input.retrieve('emulated:change')) {
+ input.on('change', function(changeEvent) {
+ input.fire('emulated:change', changeEvent, true)
+ })
+ input.store('emulated:change', true)
+ }
+ })
+ }
+
function handleRemote(element) {
var method, url, params;
+ var event = element.fire("ajax:before");
+ if (event.stopped) return false;
+
if (element.tagName.toLowerCase() === 'form') {
method = element.readAttribute('method') || 'post';
url = element.readAttribute('action');
- params = element.serialize(true);
+ params = element.serialize();
} else {
method = element.readAttribute('data-method') || 'get';
url = element.readAttribute('href');
params = {};
}
- var event = element.fire("ajax:before");
- if (event.stopped) return false;
-
new Ajax.Request(url, {
method: method,
parameters: params,
- asynchronous: true,
evalScripts: true,
- onLoading: function(request) { element.fire("ajax:loading", {request: request}); },
- onLoaded: function(request) { element.fire("ajax:loaded", {request: request}); },
- onInteractive: function(request) { element.fire("ajax:interactive", {request: request}); },
- onComplete: function(request) { element.fire("ajax:complete", {request: request}); },
- onSuccess: function(request) { element.fire("ajax:success", {request: request}); },
- onFailure: function(request) { element.fire("ajax:failure", {request: request}); }
+ onComplete: function(request) { element.fire("ajax:complete", request); },
+ onSuccess: function(request) { element.fire("ajax:success", request); },
+ onFailure: function(request) { element.fire("ajax:failure", request); }
});
element.fire("ajax:after");
}
function handleMethod(element) {
- var method, url, token_name, token;
-
- method = element.readAttribute('data-method');
- url = element.readAttribute('href');
- csrf_param = $$('meta[name=csrf-param]').first();
- csrf_token = $$('meta[name=csrf-token]').first();
+ var method = element.readAttribute('data-method'),
+ url = element.readAttribute('href'),
+ csrf_param = $$('meta[name=csrf-param]')[0],
+ csrf_token = $$('meta[name=csrf-token]')[0];
var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
- element.parentNode.appendChild(form);
+ element.parentNode.insert(form);
- if (method != 'post') {
+ if (method !== 'post') {
var field = new Element('input', { type: 'hidden', name: '_method', value: method });
- form.appendChild(field);
+ form.insert(field);
}
if (csrf_param) {
- var param = csrf_param.readAttribute('content');
- var token = csrf_token.readAttribute('content');
- var field = new Element('input', { type: 'hidden', name: param, value: token });
- form.appendChild(field);
+ var param = csrf_param.readAttribute('content'),
+ token = csrf_token.readAttribute('content'),
+ field = new Element('input', { type: 'hidden', name: param, value: token });
+ form.insert(field);
}
form.submit();
}
- $(document.body).observe("click", function(event) {
- var message = event.findElement().readAttribute('data-confirm');
- if (message && !confirm(message)) {
- event.stop();
- return false;
- }
- var element = event.findElement("a[data-remote]");
- if (element) {
- handleRemote(element);
- event.stop();
- return true;
- }
+ document.on("click", "*[data-confirm]", function(event, element) {
+ var message = element.readAttribute('data-confirm');
+ if (!confirm(message)) event.stop();
+ });
- var element = event.findElement("a[data-method]");
- if (element) {
- handleMethod(element);
- event.stop();
- return true;
- }
+ document.on("click", "a[data-remote]", function(event, element) {
+ if (event.stopped) return;
+ handleRemote(element);
+ event.stop();
+ });
+
+ document.on("click", "a[data-method]", function(event, element) {
+ if (event.stopped) return;
+ handleMethod(element);
+ event.stop();
});
- // TODO: I don't think submit bubbles in IE
- $(document.body).observe("submit", function(event) {
+ document.on("submit", function(event) {
var element = event.findElement(),
message = element.readAttribute('data-confirm');
if (message && !confirm(message)) {
@@ -103,16 +164,12 @@ document.observe("dom:loaded", function() {
}
});
- $(document.body).observe("ajax:after", function(event) {
- var element = event.findElement();
-
- if (element.tagName.toLowerCase() === 'form') {
- var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
- inputs.each(function(input) {
- input.value = input.readAttribute('data-original-value');
- input.writeAttribute('data-original-value', null);
- input.disabled = false;
- });
- }
+ document.on("ajax:after", "form", function(event, element) {
+ var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
+ inputs.each(function(input) {
+ input.value = input.readAttribute('data-original-value');
+ input.removeAttribute('data-original-value');
+ input.disabled = false;
+ });
});
-}); \ No newline at end of file
+})();
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 1c9e308631..7a65188a9a 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -25,9 +25,7 @@ module Rails
def initialize(path)
raise if path.is_a?(Array)
-
@children = {}
-
@path = path
@root = self
@all_paths = []
@@ -38,14 +36,18 @@ module Rails
@all_paths
end
- def load_once
- filter_by(:load_once?)
+ def autoload_once
+ filter_by(:autoload_once?)
end
def eager_load
filter_by(:eager_load?)
end
+ def autoload_paths
+ filter_by(:autoload?)
+ end
+
def load_paths
filter_by(:load_path?)
end
@@ -61,15 +63,17 @@ module Rails
protected
def filter_by(constraint)
- all_paths.map do |path|
+ all = []
+ all_paths.each do |path|
if path.send(constraint)
paths = path.paths
paths -= path.children.values.map { |p| p.send(constraint) ? [] : p.paths }.flatten
- paths
- else
- []
+ all.concat(paths)
end
- end.flatten.uniq.select { |p| File.exists?(p) }
+ end
+ all.uniq!
+ all.reject! { |p| !File.exists?(p) }
+ all
end
end
@@ -80,15 +84,16 @@ module Rails
attr_accessor :glob
def initialize(root, *paths)
- @options = paths.last.is_a?(::Hash) ? paths.pop : {}
+ options = paths.last.is_a?(::Hash) ? paths.pop : {}
@children = {}
@root = root
@paths = paths.flatten
- @glob = @options.delete(:glob)
+ @glob = options[:glob]
- @load_once = @options[:load_once]
- @eager_load = @options[:eager_load]
- @load_path = @options[:load_path] || @eager_load || @load_once
+ autoload_once! if options[:autoload_once]
+ eager_load! if options[:eager_load]
+ autoload! if options[:autoload]
+ load_path! if options[:load_path]
@root.all_paths << self
end
@@ -111,24 +116,30 @@ module Rails
@paths.concat paths
end
- def load_once!
- @load_once = true
- @load_path = true
+ def autoload_once!
+ @autoload_once = true
end
- def load_once?
- @load_once
+ def autoload_once?
+ @autoload_once
end
def eager_load!
@eager_load = true
- @load_path = true
end
def eager_load?
@eager_load
end
+ def autoload!
+ @autoload = true
+ end
+
+ def autoload?
+ @autoload
+ end
+
def load_path!
@load_path = true
end
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index fcdd099135..be229cc9a2 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -61,6 +61,16 @@ module Rails
@config ||= Engine::Configuration.new
end
+ initializer :handle_lib_autoload, :before => :set_load_path do |app|
+ paths = if app.config.reload_plugins
+ config.autoload_paths
+ else
+ config.autoload_once_paths
+ end
+
+ paths.concat config.paths.lib.to_a
+ end
+
initializer :load_init_rb, :before => :load_config_initializers do |app|
files = %w(rails/init.rb init.rb).map { |path| File.expand_path path, root }
if initrb = files.find { |path| File.file? path }
@@ -77,11 +87,5 @@ module Rails
raise "\"#{name}\" is a Railtie/Engine and cannot be installed as plugin"
end
end
-
- protected
-
- def reloadable?(app)
- app.config.reload_plugins
- end
end
end
diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb
index b337d3fc6e..340ce67511 100644
--- a/railties/test/application/loading_test.rb
+++ b/railties/test/application/loading_test.rb
@@ -12,7 +12,7 @@ class LoadingTest < Test::Unit::TestCase
@app ||= Rails.application
end
- def test_load_should_load_constants
+ def test_constants_in_app_are_autoloaded
app_file "app/models/post.rb", <<-MODEL
class Post < ActiveRecord::Base
validates_acceptance_of :title, :accept => "omg"
@@ -29,6 +29,19 @@ class LoadingTest < Test::Unit::TestCase
assert_equal 'omg', p.title
end
+ def test_models_without_table_do_not_panic_on_scope_definitions_when_loaded
+ app_file "app/models/user.rb", <<-MODEL
+ class User < ActiveRecord::Base
+ default_scope where(:published => true)
+ end
+ MODEL
+
+ require "#{rails_root}/config/environment"
+ setup_ar!
+
+ User
+ end
+
def test_descendants_are_cleaned_on_each_request_without_cache_classes
add_to_config <<-RUBY
config.cache_classes = false
diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb
index 72cae23985..2b6ec26cd0 100644
--- a/railties/test/application/url_generation_test.rb
+++ b/railties/test/application/url_generation_test.rb
@@ -16,6 +16,7 @@ module ApplicationTests
class MyApp < Rails::Application
config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
config.session_store :cookie_store, :key => "_myapp_session"
+ config.active_support.deprecation = :log
end
MyApp.initialize!
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index b46ac0efaf..390c0ab543 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -100,7 +100,7 @@ module TestHelpers
end
end
- add_to_config 'config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"; config.session_store :cookie_store, :key => "_myapp_session"'
+ add_to_config 'config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"; config.session_store :cookie_store, :key => "_myapp_session"; config.active_support.deprecation = :log'
end
def make_basic_app
@@ -110,6 +110,7 @@ module TestHelpers
app = Class.new(Rails::Application)
app.config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
app.config.session_store :cookie_store, :key => "_myapp_session"
+ app.config.active_support.deprecation = :log
yield app if block_given?
app.initialize!
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index 92c7b2ba0e..008247cb1a 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -120,36 +120,36 @@ class PathsTest < ActiveSupport::TestCase
test "it is possible to add a path that should be loaded only once" do
@root.app = "/app"
- @root.app.load_once!
- assert @root.app.load_once?
- assert @root.load_once.include?(@root.app.paths.first)
+ @root.app.autoload_once!
+ assert @root.app.autoload_once?
+ assert @root.autoload_once.include?(@root.app.paths.first)
end
test "it is possible to add a path without assignment and specify it should be loaded only once" do
- @root.app "/app", :load_once => true
- assert @root.app.load_once?
- assert @root.load_once.include?("/app")
+ @root.app "/app", :autoload_once => true
+ assert @root.app.autoload_once?
+ assert @root.autoload_once.include?("/app")
end
test "it is possible to add multiple paths without assignment and specify it should be loaded only once" do
- @root.app "/app", "/app2", :load_once => true
- assert @root.app.load_once?
- assert @root.load_once.include?("/app")
- assert @root.load_once.include?("/app2")
+ @root.app "/app", "/app2", :autoload_once => true
+ assert @root.app.autoload_once?
+ assert @root.autoload_once.include?("/app")
+ assert @root.autoload_once.include?("/app2")
end
- test "making a path load_once more than once only includes it once in @root.load_once" do
+ test "making a path autoload_once more than once only includes it once in @root.load_once" do
@root.app = "/app"
- @root.app.load_once!
- @root.app.load_once!
- assert_equal 1, @root.load_once.select {|p| p == @root.app.paths.first }.size
+ @root.app.autoload_once!
+ @root.app.autoload_once!
+ assert_equal 1, @root.autoload_once.select {|p| p == @root.app.paths.first }.size
end
- test "paths added to a load_once path should be added to the load_once collection" do
+ test "paths added to a load_once path should be added to the autoload_once collection" do
@root.app = "/app"
- @root.app.load_once!
+ @root.app.autoload_once!
@root.app << "/app2"
- assert_equal 2, @root.load_once.size
+ assert_equal 2, @root.autoload_once.size
end
test "it is possible to mark a path as eager" do
@@ -173,11 +173,11 @@ class PathsTest < ActiveSupport::TestCase
end
test "it is possible to create a path without assignment and mark it both as eager and load once" do
- @root.app "/app", :eager_load => true, :load_once => true
+ @root.app "/app", :eager_load => true, :autoload_once => true
assert @root.app.eager_load?
- assert @root.app.load_once?
+ assert @root.app.autoload_once?
assert @root.eager_load.include?("/app")
- assert @root.load_once.include?("/app")
+ assert @root.autoload_once.include?("/app")
end
test "making a path eager more than once only includes it once in @root.eager_paths" do
@@ -218,16 +218,16 @@ class PathsTest < ActiveSupport::TestCase
assert_equal ["/app"], @root.load_paths
end
- test "adding a path to the eager paths also adds it to the load path" do
+ test "a path can be marked as autoload path" do
@root.app = "app"
- @root.app.eager_load!
- assert_equal ["/foo/bar/app"], @root.load_paths
+ @root.app.autoload!
+ @root.app.models = "app/models"
+ assert_equal ["/foo/bar/app"], @root.autoload_paths
end
- test "adding a path to the load once paths also adds it to the load path" do
- @root.app = "app"
- @root.app.load_once!
- assert_equal ["/foo/bar/app"], @root.load_paths
+ test "a path can be marked as autoload on creation" do
+ @root.app "/app", :autoload => true
+ assert @root.app.autoload?
+ assert_equal ["/app"], @root.autoload_paths
end
-
end
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 3fe01e543c..7410a10712 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -20,10 +20,6 @@ module RailtiesTest
end
end
- def reload_config
- :reload_engines
- end
-
test "Rails::Engine itself does not respond to config" do
boot_rails
assert !Rails::Engine.respond_to?(:config)
diff --git a/railties/test/railties/plugin_test.rb b/railties/test/railties/plugin_test.rb
index 0f5f29468c..b143f56484 100644
--- a/railties/test/railties/plugin_test.rb
+++ b/railties/test/railties/plugin_test.rb
@@ -15,10 +15,6 @@ module RailtiesTest
end
end
- def reload_config
- :reload_plugins
- end
-
test "Rails::Plugin itself does not respond to config" do
boot_rails
assert !Rails::Plugin.respond_to?(:config)
@@ -37,6 +33,32 @@ module RailtiesTest
assert_equal "Bukkits", Bukkits.name
end
+ test "plugin gets added to dependency list" do
+ boot_rails
+ assert_equal "Another", Another.name
+ end
+
+ test "plugin constants get reloaded if config.reload_plugins is set to true" do
+ add_to_config <<-RUBY
+ config.reload_plugins = true
+ RUBY
+
+ boot_rails
+
+ assert_equal "Another", Another.name
+ ActiveSupport::Dependencies.clear
+ @plugin.delete("lib/another.rb")
+ assert_raises(NameError) { Another }
+ end
+
+ test "plugin constants are not reloaded by default" do
+ boot_rails
+ assert_equal "Another", Another.name
+ ActiveSupport::Dependencies.clear
+ @plugin.delete("lib/another.rb")
+ assert_nothing_raised { Another }
+ end
+
test "it loads the plugin's init.rb file" do
boot_rails
assert_equal "loaded", BUKKITS
diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb
index 3f78d7d3fe..ce7c55c11c 100644
--- a/railties/test/railties/shared_tests.rb
+++ b/railties/test/railties/shared_tests.rb
@@ -10,51 +10,25 @@ module RailtiesTest
@app ||= Rails.application
end
- def test_plugin_puts_its_lib_directory_on_load_path
+ def test_puts_its_lib_directory_on_load_path
boot_rails
require "another"
assert_equal "Another", Another.name
end
- def test_plugin_paths_get_added_to_as_dependency_list
- boot_rails
- assert_equal "Another", Another.name
- end
-
- def test_plugins_constants_are_not_reloaded_by_default
- boot_rails
- assert_equal "Another", Another.name
- ActiveSupport::Dependencies.clear
- @plugin.delete("lib/another.rb")
- assert_nothing_raised { Another }
- end
-
- def test_plugin_constants_get_reloaded_if_config_reload_plugins
- add_to_config <<-RUBY
- config.#{reload_config} = true
- RUBY
-
- boot_rails
-
- assert_equal "Another", Another.name
- ActiveSupport::Dependencies.clear
- @plugin.delete("lib/another.rb")
- assert_raises(NameError) { Another }
- end
-
- def test_plugin_puts_its_models_directory_on_load_path
+ def test_puts_its_models_directory_on_autoload_path
@plugin.write "app/models/my_bukkit.rb", "class MyBukkit ; end"
boot_rails
assert_nothing_raised { MyBukkit }
end
- def test_plugin_puts_its_controllers_directory_on_the_load_path
+ def test_puts_its_controllers_directory_on_autoload_path
@plugin.write "app/controllers/bukkit_controller.rb", "class BukkitController ; end"
boot_rails
assert_nothing_raised { BukkitController }
end
- def test_plugin_adds_its_views_to_view_paths
+ def test_adds_its_views_to_view_paths
@plugin.write "app/controllers/bukkit_controller.rb", <<-RUBY
class BukkitController < ActionController::Base
def index
@@ -72,7 +46,7 @@ module RailtiesTest
assert_equal "Hello bukkits\n", response[2].body
end
- def test_plugin_adds_its_views_to_view_paths_with_lower_proriority
+ def test_adds_its_views_to_view_paths_with_lower_proriority_than_app_ones
@plugin.write "app/controllers/bukkit_controller.rb", <<-RUBY
class BukkitController < ActionController::Base
def index
@@ -91,7 +65,7 @@ module RailtiesTest
assert_equal "Hi bukkits\n", response[2].body
end
- def test_plugin_adds_helpers_to_controller_views
+ def test_adds_helpers_to_controller_views
@plugin.write "app/controllers/bukkit_controller.rb", <<-RUBY
class BukkitController < ActionController::Base
def index
@@ -116,11 +90,10 @@ module RailtiesTest
assert_equal "Hello bukkits\n", response[2].body
end
- def test_plugin_eager_load_any_path_under_app
+ def test_autoload_any_path_under_app
@plugin.write "app/anything/foo.rb", <<-RUBY
module Foo; end
RUBY
-
boot_rails
assert Foo
end
@@ -269,7 +242,7 @@ YAML
assert_equal "Rendered from namespace", last_response.body
end
- def test_plugin_initializers
+ def test_initializers
$plugin_initializer = false
@plugin.write "config/initializers/foo.rb", <<-RUBY
$plugin_initializer = true
@@ -279,7 +252,7 @@ YAML
assert $plugin_initializer
end
- def test_plugin_midleware_referenced_in_configuration
+ def test_midleware_referenced_in_configuration
@plugin.write "lib/bukkits.rb", <<-RUBY
class Bukkits
def initialize(app)