aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2009-11-23 22:04:18 -0200
committerJosé Valim <jose.valim@gmail.com>2009-11-23 22:04:18 -0200
commit4ff66b6b85d1351e447f18c027f1dba6bcf23a7b (patch)
treea46d6c65c5c67964bc8658bf991157f0f8056f55
parent01ae99c681d31803f3a29f8305c9a041aa456660 (diff)
parent934bb012ba3f1da5cd181ae5c2d84f697a3c58a1 (diff)
downloadrails-4ff66b6b85d1351e447f18c027f1dba6bcf23a7b.tar.gz
rails-4ff66b6b85d1351e447f18c027f1dba6bcf23a7b.tar.bz2
rails-4ff66b6b85d1351e447f18c027f1dba6bcf23a7b.zip
Merge branch 'master' of git://github.com/rails/rails
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules6
-rw-r--r--Gemfile10
-rw-r--r--actionpack/actionpack.gemspec4
-rw-r--r--actionpack/lib/action_controller/caching.rb1
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb5
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb44
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb30
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb67
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb13
-rw-r--r--actionpack/test/controller/mime_responds_test.rb22
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb20
-rw-r--r--actionpack/test/controller/routing_test.rb25
-rw-r--r--actionpack/test/dispatch/routing_test.rb52
-rw-r--r--actionpack/test/template/form_helper_test.rb66
-rw-r--r--activemodel/lib/active_model/translation.rb2
-rw-r--r--activerecord/lib/active_record/association_preload.rb20
-rwxr-xr-xactiverecord/lib/active_record/associations.rb31
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb14
-rw-r--r--activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb14
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb7
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb34
-rw-r--r--activerecord/test/cases/associations/habtm_join_table_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb22
-rw-r--r--activerecord/test/cases/copy_table_test_sqlite.rb4
-rw-r--r--activerecord/test/cases/locking_test.rb31
-rw-r--r--activerecord/test/fixtures/edges.yml3
-rw-r--r--activerecord/test/schema/schema.rb2
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/array/wrap.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/date.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/date/conversions.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/date_time.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/logger.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/name_error.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/interpolation.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time.rb10
-rw-r--r--activesupport/lib/active_support/json/encoding.rb2
-rw-r--r--activesupport/lib/active_support/notifications.rb100
-rw-r--r--activesupport/lib/active_support/rescuable.rb2
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb15
-rw-r--r--activesupport/lib/active_support/time.rb26
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb8
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb3
-rw-r--r--activesupport/lib/active_support/vendor.rb2
-rw-r--r--activesupport/lib/active_support/whiny_nil.rb10
-rw-r--r--activesupport/test/caching_test.rb4
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb20
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb2
-rw-r--r--activesupport/test/core_ext/date_time_ext_test.rb2
-rw-r--r--activesupport/test/core_ext/name_error_test.rb26
-rw-r--r--activesupport/test/core_ext/range_ext_test.rb2
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb2
-rw-r--r--activesupport/test/i18n_test.rb4
-rw-r--r--activesupport/test/json/decoding_test.rb1
-rw-r--r--activesupport/test/message_encryptor_test.rb1
-rw-r--r--activesupport/test/message_verifier_test.rb2
-rw-r--r--activesupport/test/notifications_test.rb40
-rw-r--r--activesupport/test/whiny_nil_test.rb14
m---------arel6
m---------rack-mount0
-rw-r--r--railties/CHANGELOG2
-rw-r--r--railties/lib/rails.rb12
-rw-r--r--railties/lib/rails/application.rb55
-rw-r--r--railties/lib/rails/commands/server.rb4
-rw-r--r--railties/lib/rails/configuration.rb33
-rw-r--r--railties/lib/rails/generators.rb34
-rw-r--r--railties/lib/rails/initializable.rb3
-rw-r--r--railties/lib/rails/initializer.rb14
-rw-r--r--railties/lib/rails/paths.rb6
-rw-r--r--railties/lib/rails/plugin.rb209
-rw-r--r--railties/lib/rails/plugin/loader.rb200
-rw-r--r--railties/lib/rails/plugin/locator.rb100
-rw-r--r--railties/lib/rails/rack/debugger.rb2
-rw-r--r--railties/lib/rails/rack/static.rb2
-rw-r--r--railties/test/application/plugins_test.rb117
-rw-r--r--railties/test/fixtures/vendor/plugins/mspec/lib/rails_generators/mspec_generator.rb (renamed from railties/test/fixtures/vendor/plugins/mspec/lib/generators/mspec_generator.rb)0
-rw-r--r--railties/test/generators_test.rb7
-rw-r--r--railties/test/initializable_test.rb8
-rw-r--r--railties/test/initializer/initialize_i18n_test.rb42
-rw-r--r--railties/test/isolation/abstract_unit.rb30
-rw-r--r--railties/test/plugin_loader_test.rb176
-rw-r--r--railties/test/plugin_locator_test.rb73
-rw-r--r--railties/test/plugin_test.rb174
-rw-r--r--railties/test/plugin_test_helper.rb29
-rw-r--r--railties/test/plugins/vendored_test.rb190
-rw-r--r--railties/test/rails_info_test.rb13
88 files changed, 907 insertions, 1537 deletions
diff --git a/.gitignore b/.gitignore
index 3b922f29f7..70b7c0057a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,7 @@ railties/guides/output
*.rbc
*.swp
*.swo
+*.tmproj
bin
vendor/gems/
railties/tmp
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000..20ed3ed9c9
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "arel"]
+ path = arel
+ url = git://github.com/rails/arel.git
+[submodule "rack-mount"]
+ path = rack-mount
+ url = git://github.com/rails/rack-mount.git
diff --git a/Gemfile b/Gemfile
index 20d5263c07..87bf43b786 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,3 @@
-clear_sources
-source 'http://gemcutter.org'
-
gem "rake", ">= 0.8.7"
gem "mocha", ">= 0.9.8"
@@ -12,12 +9,11 @@ end
# AR
gem "arel", "0.2.pre", :git => "git://github.com/rails/arel.git"
gem "sqlite3-ruby", ">= 1.2.5"
+gem "pg", ">= 0.8.0"
+gem "mysql", ">= 2.8.1"
# AP
gem "rack", "1.0.1", :git => "git://github.com/rails/rack.git"
-gem "rack-mount", :git => "git://github.com/rails/rack-mount.git"
-gem "rack-test", ">= 0.5.0"
-gem "erubis", ">= 2.6.5"
gem "RedCloth", ">= 4.2.2"
if ENV['CI']
@@ -25,8 +21,6 @@ if ENV['CI']
gem "nokogiri", ">= 1.4.0"
gem "memcache-client", ">= 1.7.6"
- gem "pg", ">= 0.8.0"
- gem "mysql", ">= 2.8.1"
# fcgi gem doesn't compile on 1.9
# avoid minitest strangeness on 1.9
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index ae5985120e..78286f6747 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -18,8 +18,8 @@ Gem::Specification.new do |s|
s.add_dependency('activemodel', '= 3.0.pre')
s.add_dependency('rack', '~> 1.0.1')
s.add_dependency('rack-test', '~> 0.5.0')
- s.add_dependency('rack-mount', '~> 0.0.1')
- s.add_dependency('erubis', '~> 2.6.0')
+ s.add_dependency('rack-mount', '~> 0.2')
+ s.add_dependency('erubis', '~> 2.6.5')
s.require_path = 'lib'
s.autorequire = 'action_controller'
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 083d6328af..3caf759032 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -26,6 +26,7 @@ module ActionController #:nodoc:
# config.action_controller.cache_store = :file_store, "/path/to/cache/directory"
# config.action_controller.cache_store = :drb_store, "druby://localhost:9192"
# config.action_controller.cache_store = :mem_cache_store, "localhost"
+ # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211")
# config.action_controller.cache_store = MyOwnStore.new("parameter")
module Caching
extend ActiveSupport::Concern
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 113c20a758..173df79ee7 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -101,6 +101,11 @@ module ActionController #:nodoc:
session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
end
+ # The form's authenticity parameter. Override to provide your own.
+ def form_authenticity_param
+ params[request_forgery_protection_token]
+ end
+
def protect_against_forgery?
allow_forgery_protection
end
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index c6e847ba0f..e8e88e7479 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -14,12 +14,11 @@ module ActionController #:nodoc:
#
# When a request comes, for example with format :xml, three steps happen:
#
- # 1) respond_with searches for a template at people/index.xml;
+ # 1) responder searches for a template at people/index.xml;
#
- # 2) if the template is not available, it will create a responder, passing
- # the controller and the resource and invoke :to_xml on it;
+ # 2) if the template is not available, it will invoke :to_xml in the given resource;
#
- # 3) if the responder does not respond_to :to_xml, call to_format on it.
+ # 3) if the responder does not respond_to :to_xml, call :to_format on it.
#
# === Builtin HTTP verb semantics
#
@@ -88,14 +87,16 @@ module ActionController #:nodoc:
@resource = resources.is_a?(Array) ? resources.last : resources
@resources = resources
@options = options
+ @action = options.delete(:action)
@default_response = options.delete(:default_response)
end
delegate :head, :render, :redirect_to, :to => :controller
delegate :get?, :post?, :put?, :delete?, :to => :request
- # Undefine :to_json since it's defined on Object
+ # Undefine :to_json and :to_yaml since it's defined on Object
undef_method(:to_json) if method_defined?(:to_json)
+ undef_method(:to_yaml) if method_defined?(:to_yaml)
# Initializes a new responder an invoke the proper format. If the format is
# not defined, call to_format.
@@ -111,14 +112,8 @@ module ActionController #:nodoc:
#
def to_html
default_render
- rescue ActionView::MissingTemplate
- if get?
- raise
- elsif has_errors?
- render :action => default_action
- else
- redirect_to resource_location
- end
+ rescue ActionView::MissingTemplate => e
+ navigation_behavior(e)
end
# All others formats follow the procedure below. First we try to render a
@@ -127,9 +122,26 @@ module ActionController #:nodoc:
#
def to_format
default_render
- rescue ActionView::MissingTemplate
+ rescue ActionView::MissingTemplate => e
raise unless resourceful?
+ api_behavior(e)
+ end
+ protected
+
+ # This is the common behavior for "navigation" requests, like :html, :iphone and so forth.
+ def navigation_behavior(error)
+ if get?
+ raise error
+ elsif has_errors?
+ render :action => default_action
+ else
+ redirect_to resource_location
+ end
+ end
+
+ # This is the common behavior for "API" requests, like :xml and :json.
+ def api_behavior(error)
if get?
display resource
elsif has_errors?
@@ -141,8 +153,6 @@ module ActionController #:nodoc:
end
end
- protected
-
# Checks whether the resource responds to the current format or not.
#
def resourceful?
@@ -194,7 +204,7 @@ module ActionController #:nodoc:
# the verb is post.
#
def default_action
- request.post? ? :new : :edit
+ @action || (request.post? ? :new : :edit)
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index b3bb8c623f..6a52854961 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -489,7 +489,7 @@ EOM
def self.extended(object)
object.class_eval do
attr_accessor :original_path, :content_type
- alias_method :local_path, :path
+ alias_method :local_path, :path if method_defined?(:path)
end
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index ebfb4c9be2..6e112c9b54 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -52,30 +52,38 @@ module ActionDispatch
resource = resources.pop
+ plural = resource.to_s
+ singular = plural.singularize
+
if @scope[:scope_level] == :resources
- member do
- resources(resource, options, &block)
+ parent_resource = @scope[:scope_level_options][:name]
+ with_scope_level(:member) do
+ scope(":#{parent_resource}_id", :name_prefix => parent_resource) do
+ resources(resource, options, &block)
+ end
end
return self
end
- plural = resource.to_s
- singular = plural.singularize
+ if @scope[:options] && (prefix = @scope[:options][:name_prefix])
+ plural = "#{prefix}_#{plural}"
+ singular = "#{prefix}_#{singular}"
+ end
controller(resource) do
namespace(resource) do
- with_scope_level(:resources) do
+ with_scope_level(:resources, :name => singular) do
yield if block_given?
member do
- get "", :to => :show, :as => "#{singular}"
+ get "", :to => :show, :as => singular
put "", :to => :update
delete "", :to => :destroy
get "edit", :to => :edit, :as => "edit_#{singular}"
end
collection do
- get "", :to => :index, :as => "#{plural}"
+ get "", :to => :index, :as => plural
post "", :to => :create
get "new", :to => :new, :as => "new_#{singular}"
end
@@ -127,11 +135,13 @@ module ActionDispatch
end
private
- def with_scope_level(kind)
+ def with_scope_level(kind, options = {})
old, @scope[:scope_level] = @scope[:scope_level], kind
+ old_options, @scope[:scope_level_options] = @scope[:scope_level_options], options
yield
ensure
@scope[:scope_level] = old
+ @scope[:scope_level_options] = old_options
end
end
@@ -195,9 +205,9 @@ module ActionDispatch
@constraints.each { |constraint|
if constraint.respond_to?(:matches?) && !constraint.matches?(req)
- return Rack::Mount::Const::EXPECTATION_FAILED_RESPONSE
+ return [417, {}, []]
elsif constraint.respond_to?(:call) && !constraint.call(req)
- return Rack::Mount::Const::EXPECTATION_FAILED_RESPONSE
+ return [417, {}, []]
end
}
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 28e5b806da..c15aaceb5b 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -5,7 +5,7 @@ module ActionDispatch
module Routing
class RouteSet #:nodoc:
NotFound = lambda { |env|
- raise ActionController::RoutingError, "No route matches #{env[::Rack::Mount::Const::PATH_INFO].inspect} with #{env.inspect}"
+ raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect} with #{env.inspect}"
}
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
@@ -372,7 +372,17 @@ module ActionDispatch
end
recall[:action] = options.delete(:action) if options[:action] == 'index'
- path = _uri(named_route, options, recall)
+ parameterize = lambda { |name, value|
+ if name == :controller
+ value
+ elsif value.is_a?(Array)
+ value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
+ else
+ Rack::Mount::Utils.escape_uri(value.to_param)
+ end
+ }
+
+ path = @set.url(named_route, options, recall, :parameterize => parameterize)
if path && method == :generate_extras
uri = URI(path)
extras = uri.query ?
@@ -439,59 +449,6 @@ module ActionDispatch
def extract_request_environment(request)
{ :method => request.method }
end
-
- private
- def _uri(named_route, params, recall)
- params = URISegment.wrap_values(params)
- recall = URISegment.wrap_values(recall)
-
- unless result = @set.generate(:path_info, named_route, params, recall)
- return
- end
-
- uri, params = result
- params.each do |k, v|
- if v._value
- params[k] = v._value
- else
- params.delete(k)
- end
- end
-
- uri << "?#{Rack::Mount::Utils.build_nested_query(params)}" if uri && params.any?
- uri
- end
-
- class URISegment < Struct.new(:_value, :_escape)
- EXCLUDED = [:controller]
-
- def self.wrap_values(hash)
- hash.inject({}) { |h, (k, v)|
- h[k] = new(v, !EXCLUDED.include?(k.to_sym))
- h
- }
- end
-
- extend Forwardable
- def_delegators :_value, :==, :eql?, :hash
-
- def to_param
- @to_param ||= begin
- if _value.is_a?(Array)
- _value.map { |v| _escaped(v) }.join('/')
- else
- _escaped(_value)
- end
- end
- end
- alias_method :to_s, :to_param
-
- private
- def _escaped(value)
- v = value.respond_to?(:to_param) ? value.to_param : value
- _escape ? Rack::Mount::Utils.escape_uri(v) : v.to_s
- end
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index c46b39fc23..d0c66eda60 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -963,7 +963,7 @@ module ActionView
end
end
- (field_helpers - %w(label check_box radio_button fields_for)).each do |selector|
+ (field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector|
src = <<-end_src
def #{selector}(method, options = {}) # def text_field(method, options = {})
@template.send( # @template.send(
@@ -1022,6 +1022,11 @@ module ActionView
def radio_button(method, tag_value, options = {})
@template.radio_button(@object_name, method, tag_value, objectify_options(options))
end
+
+ def hidden_field(method, options = {})
+ @emitted_hidden_id = true if method == :id
+ @template.hidden_field(@object_name, method, objectify_options(options))
+ end
def error_message_on(method, *args)
@template.error_message_on(@object, method, *args)
@@ -1035,6 +1040,10 @@ module ActionView
@template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
end
+ def emitted_hidden_id?
+ @emitted_hidden_id
+ end
+
private
def objectify_options(options)
@default_options.merge(options.merge(:object => @object))
@@ -1069,8 +1078,8 @@ module ActionView
@template.fields_for(name, object, *args, &block)
else
@template.fields_for(name, object, *args) do |builder|
- @template.concat builder.hidden_field(:id)
block.call(builder)
+ @template.concat builder.hidden_field(:id) unless builder.emitted_hidden_id?
end
end
end
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index b070f925d4..fee9cf46f9 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -501,6 +501,12 @@ class RespondWithController < ActionController::Base
respond_with(Customer.new("david", 13), :responder => responder)
end
+ def using_resource_with_action
+ respond_with(Customer.new("david", 13), :action => :foo) do |format|
+ format.html { raise ActionView::MissingTemplate.new([], "method") }
+ end
+ end
+
protected
def _render_js(js, options)
@@ -715,6 +721,20 @@ class RespondWithControllerTest < ActionController::TestCase
assert_match /<name>jamis<\/name>/, @response.body
end
+ def test_using_resource_with_action
+ @controller.instance_eval do
+ def render(params={})
+ self.response_body = "#{params[:action]} - #{formats}"
+ end
+ end
+
+ errors = { :name => :invalid }
+ Customer.any_instance.stubs(:errors).returns(errors)
+
+ post :using_resource_with_action
+ assert_equal "foo - #{[:html].to_s}", @controller.response_body
+ end
+
def test_clear_respond_to
@controller = InheritedRespondWithController.new
@request.accept = "text/html"
@@ -760,7 +780,7 @@ class RespondWithControllerTest < ActionController::TestCase
assert_equal "Resource name is david", @response.body
end
- def test_using_resource_with_responder
+ def test_using_resource_with_set_responder
RespondWithController.responder = proc { |c, r, o| c.render :text => "Resource name is #{r.first.name}" }
get :using_resource
assert_equal "Resource name is david", @response.body
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 7111796f8d..09003adf73 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -18,7 +18,7 @@ module RequestForgeryProtectionActions
def unsafe
render :text => 'pwn'
end
-
+
def rescue_action(e) raise e end
end
@@ -40,6 +40,13 @@ class FreeCookieController < RequestForgeryProtectionController
end
end
+class CustomAuthenticityParamController < RequestForgeryProtectionController
+ def form_authenticity_param
+ 'foobar'
+ end
+end
+
+
# common test methods
module RequestForgeryProtectionTests
@@ -241,3 +248,14 @@ class FreeCookieControllerTest < ActionController::TestCase
end
end
end
+
+class CustomAuthenticityParamControllerTest < ActionController::TestCase
+ def setup
+ ActionController::Base.request_forgery_protection_token = :authenticity_token
+ end
+
+ def test_should_allow_custom_token
+ post :index, :authenticity_token => 'foobar'
+ assert_response :ok
+ end
+end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 3971aacadb..4eaf309c41 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -1418,6 +1418,7 @@ class RouteSetTest < ActiveSupport::TestCase
:action => 'show',
:requirements => {:name => /(david|jamis)/i}
end
+
url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'})
assert_equal "/page/david", url
assert_raise ActionController::RoutingError do
@@ -1459,13 +1460,16 @@ class RouteSetTest < ActiveSupport::TestCase
jamis #The Deployer
)/x}
end
- url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'})
- assert_equal "/page/david", url
- assert_raise ActionController::RoutingError do
- url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'})
- end
- assert_raise ActionController::RoutingError do
- url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
+
+ pending do
+ url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'})
+ assert_equal "/page/david", url
+ assert_raise ActionController::RoutingError do
+ url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'})
+ end
+ assert_raise ActionController::RoutingError do
+ url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
+ end
end
end
@@ -1480,8 +1484,11 @@ class RouteSetTest < ActiveSupport::TestCase
jamis #The Deployer
)/xi}
end
- url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
- assert_equal "/page/JAMIS", url
+
+ pending do
+ url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'})
+ assert_equal "/page/JAMIS", url
+ end
end
def test_route_requirement_recognize_with_xi_modifiers
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index ca07bc7a28..496445fc34 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -212,9 +212,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
with_test_routes do
get '/projects/1/involvements'
assert_equal 'involvements#index', @response.body
+ assert_equal '/projects/1/involvements', project_involvements_path(:project_id => '1')
get '/projects/1/involvements/1'
assert_equal 'involvements#show', @response.body
+ assert_equal '/projects/1/involvements/1', project_involvement_path(:project_id => '1', :id => '1')
end
end
@@ -222,6 +224,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
with_test_routes do
get '/projects/1/attachments'
assert_equal 'attachments#index', @response.body
+ assert_equal '/projects/1/attachments', project_attachments_path(:project_id => '1')
end
end
@@ -229,9 +232,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
with_test_routes do
get '/projects/1/participants'
assert_equal 'participants#index', @response.body
+ assert_equal '/projects/1/participants', project_participants_path(:project_id => '1')
put '/projects/1/participants/update_all'
assert_equal 'participants#update_all', @response.body
+
+ pending do
+ assert_equal '/projects/1/participants/update_all', update_all_project_participants_path(:project_id => '1')
+ end
end
end
@@ -239,12 +247,19 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
with_test_routes do
get '/projects/1/companies'
assert_equal 'companies#index', @response.body
+ assert_equal '/projects/1/companies', project_companies_path(:project_id => '1')
get '/projects/1/companies/1/people'
assert_equal 'people#index', @response.body
+ pending do
+ assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1')
+ end
get '/projects/1/companies/1/avatar'
assert_equal 'avatars#show', @response.body
+ pending do
+ assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1')
+ end
end
end
@@ -252,9 +267,13 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
with_test_routes do
get '/projects/1/images'
assert_equal 'images#index', @response.body
+ assert_equal '/projects/1/images', project_images_path(:project_id => '1')
post '/projects/1/images/1/revise'
assert_equal 'images#revise', @response.body
+ pending do
+ assert_equal '/projects/1/images/1/revise', revise_project_image_path(:project_id => '1', :id => '1')
+ end
end
end
@@ -262,21 +281,35 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
with_test_routes do
get '/projects/1/people'
assert_equal 'people#index', @response.body
+ assert_equal '/projects/1/people', project_people_path(:project_id => '1')
get '/projects/1/people/1'
assert_equal 'people#show', @response.body
+ assert_equal '/projects/1/people/1', project_person_path(:project_id => '1', :id => '1')
get '/projects/1/people/1/7a2dec8/avatar'
assert_equal 'avatars#show', @response.body
+ pending do
+ assert_equal '/projects/1/people/1/7a2dec8/avatar', project_person_avatar_path(:project_id => '1', :person_id => '1', :access_token => '7a2dec8')
+ end
put '/projects/1/people/1/accessible_projects'
assert_equal 'people#accessible_projects', @response.body
+ pending do
+ assert_equal '/projects/1/people/1/accessible_projects', accessible_projects_project_person_path(:project_id => '1', :id => '1')
+ end
post '/projects/1/people/1/resend'
assert_equal 'people#resend', @response.body
+ pending do
+ assert_equal '/projects/1/people/1/resend', resend_project_person_path(:project_id => '1', :id => '1')
+ end
post '/projects/1/people/1/generate_new_password'
assert_equal 'people#generate_new_password', @response.body
+ pending do
+ assert_equal '/projects/1/people/1/generate_new_password', generate_new_password_project_person_path(:project_id => '1', :id => '1')
+ end
end
end
@@ -284,24 +317,43 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
with_test_routes do
get '/projects/1/posts'
assert_equal 'posts#index', @response.body
+ assert_equal '/projects/1/posts', project_posts_path(:project_id => '1')
get '/projects/1/posts/archive'
assert_equal 'posts#archive', @response.body
+ pending do
+ assert_equal '/projects/1/posts/archive', archive_project_posts_path(:project_id => '1')
+ end
get '/projects/1/posts/toggle_view'
assert_equal 'posts#toggle_view', @response.body
+ pending do
+ assert_equal '/projects/1/posts/toggle_view', toggle_view_project_posts_path(:project_id => '1')
+ end
post '/projects/1/posts/1/preview'
assert_equal 'posts#preview', @response.body
+ pending do
+ assert_equal '/projects/1/posts/1/preview', preview_project_post_path(:project_id => '1', :id => '1')
+ end
get '/projects/1/posts/1/subscription'
assert_equal 'subscriptions#show', @response.body
+ pending do
+ assert_equal '/projects/1/posts/1/subscription', project_post_subscription_path(:project_id => '1', :post_id => '1')
+ end
get '/projects/1/posts/1/comments'
assert_equal 'comments#index', @response.body
+ pending do
+ assert_equal '/projects/1/posts/1/comments', project_post_comments_path(:project_id => '1', :post_id => '1')
+ end
post '/projects/1/posts/1/comments/preview'
assert_equal 'comments#preview', @response.body
+ pending do
+ assert_equal '/projects/1/posts/1/comments/preview', preview_project_post_comments_path(:project_id => '1', :post_id => '1')
+ end
end
end
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 04c635e770..44734abb18 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -613,6 +613,26 @@ class FormHelperTest < ActionView::TestCase
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>'
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_for_with_existing_records_on_a_nested_attributes_one_to_one_association_with_explicit_hidden_field_placement
+ @post.author = Author.new(321)
+
+ form_for(:post, @post) do |f|
+ concat f.text_field(:title)
+ f.fields_for(:author) do |af|
+ concat af.hidden_field(:id)
+ concat af.text_field(:name)
+ 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>'
@@ -634,6 +654,30 @@ class FormHelperTest < ActionView::TestCase
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>'
+
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collection_association_with_explicit_hidden_field_placement
+ @post.comments = Array.new(2) { |id| Comment.new(id + 1) }
+
+ form_for(:post, @post) do |f|
+ concat f.text_field(:title)
+ @post.comments.each do |comment|
+ f.fields_for(:comments, comment) do |cf|
+ concat cf.hidden_field(:id)
+ concat cf.text_field(:name)
+ end
+ 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" />' +
@@ -678,8 +722,8 @@ class FormHelperTest < ActionView::TestCase
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="321" />' +
'<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>'
@@ -713,10 +757,10 @@ class FormHelperTest < ActionView::TestCase
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_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>'
assert_dom_equal expected, output_buffer
@@ -736,8 +780,8 @@ class FormHelperTest < ActionView::TestCase
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="321" />' +
'<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>'
@@ -755,8 +799,8 @@ class FormHelperTest < ActionView::TestCase
end
expected = '<form action="http://www.example.com" method="post">' +
- '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />' +
'<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>'
assert_dom_equal expected, output_buffer
@@ -790,18 +834,18 @@ class FormHelperTest < ActionView::TestCase
end
expected = '<form action="http://www.example.com" method="post">' +
- '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' +
'<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_id" name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' +
'<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_tags_attributes_0_id" name="post[tags_attributes][0][id]" type="hidden" value="123" />' +
+ '<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_id" name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' +
'<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_1_id" name="post[tags_attributes][1][id]" type="hidden" value="456" />' +
+ '<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_id" name="post[tags_attributes][1][relevances_attributes][0][id]" type="hidden" value="31415" />' +
'<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>'
assert_dom_equal expected, output_buffer
diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb
index 2ad8ca9dea..42ca463f82 100644
--- a/activemodel/lib/active_model/translation.rb
+++ b/activemodel/lib/active_model/translation.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/reverse_merge'
+
module ActiveModel
module Translation
include ActiveModel::Naming
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index e41fda7a4b..9f7b2a60b2 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/array/wrap'
+require 'active_support/core_ext/enumerable'
+
module ActiveRecord
# See ActiveRecord::AssociationPreload::ClassMethods for documentation.
module AssociationPreload #:nodoc:
@@ -82,7 +85,7 @@ module ActiveRecord
# only one level deep in the +associations+ argument, i.e. it's not passed
# to the child associations when +associations+ is a Hash.
def preload_associations(records, associations, preload_options={})
- records = [records].flatten.compact.uniq
+ records = Array.wrap(records).compact.uniq
return if records.empty?
case associations
when Array then associations.each {|association| preload_associations(records, association, preload_options)}
@@ -92,7 +95,7 @@ module ActiveRecord
raise "parent must be an association name" unless parent.is_a?(String) || parent.is_a?(Symbol)
preload_associations(records, parent, preload_options)
reflection = reflections[parent]
- parents = records.map {|record| record.send(reflection.name)}.flatten.compact
+ parents = records.sum { |record| Array.wrap(record.send(reflection.name)) }
unless parents.empty?
parents.first.class.preload_associations(parents, child)
end
@@ -123,7 +126,8 @@ module ActiveRecord
parent_records.each do |parent_record|
association_proxy = parent_record.send(reflection_name)
association_proxy.loaded
- association_proxy.target.push(*[associated_record].flatten)
+ association_proxy.target.push *Array.wrap(associated_record)
+
association_proxy.__send__(:set_inverse_instance, associated_record, parent_record)
end
end
@@ -254,6 +258,7 @@ module ActiveRecord
through_reflection = reflections[through_association]
through_primary_key = through_reflection.primary_key_name
+ through_records = []
if reflection.options[:source_type]
interface = reflection.source_reflection.options[:foreign_type]
preload_options = {:conditions => ["#{connection.quote_column_name interface} = ?", reflection.options[:source_type]]}
@@ -262,23 +267,22 @@ module ActiveRecord
records.first.class.preload_associations(records, through_association, preload_options)
# Dont cache the association - we would only be caching a subset
- through_records = []
records.each do |record|
proxy = record.send(through_association)
if proxy.respond_to?(:target)
- through_records << proxy.target
+ through_records.concat Array.wrap(proxy.target)
proxy.reset
else # this is a has_one :through reflection
through_records << proxy if proxy
end
end
- through_records.flatten!
else
records.first.class.preload_associations(records, through_association)
- through_records = records.map {|record| record.send(through_association)}.flatten
+ records.each do |record|
+ through_records.concat Array.wrap(record.send(through_association))
+ end
end
- through_records.compact!
through_records
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 3a5f3ed030..0fcd288fc5 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/enumerable'
module ActiveRecord
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
@@ -60,6 +61,12 @@ module ActiveRecord
end
end
+ class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc:
+ def initialize(reflection)
+ super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).")
+ end
+ end
+
class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
def initialize(reflection)
super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
@@ -1396,8 +1403,8 @@ module ActiveRecord
end
define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
- ids = (new_value || []).reject { |nid| nid.blank? }
- send("#{reflection.name}=", reflection.klass.find(ids))
+ ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i)
+ send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids))
end
end
end
@@ -1674,7 +1681,6 @@ module ActiveRecord
def create_has_and_belongs_to_many_reflection(association_id, options, &extension)
options.assert_valid_keys(valid_keys_for_has_and_belongs_to_many_association)
-
options[:extend] = create_extension_modules(association_id, extension, options[:extend])
reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self)
@@ -1684,6 +1690,9 @@ module ActiveRecord
end
reflection.options[:join_table] ||= join_table_name(undecorated_table_name(self.to_s), undecorated_table_name(reflection.class_name))
+ if connection.supports_primary_key? && (connection.primary_key(reflection.options[:join_table]) rescue false)
+ raise HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection)
+ end
reflection
end
@@ -1922,12 +1931,16 @@ module ActiveRecord
reflection = base.reflections[name]
is_collection = [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
- parent_records = records.map do |record|
- descendant = record.send(reflection.name)
- next unless descendant
- descendant.target.uniq! if is_collection
- descendant
- end.flatten.compact
+ parent_records = []
+ records.each do |record|
+ if descendant = record.send(reflection.name)
+ if is_collection
+ parent_records.concat descendant.target.uniq
+ else
+ parent_records << descendant
+ end
+ end
+ end
remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
end
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 75218c01d2..7d8f4670fa 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -256,10 +256,16 @@ module ActiveRecord
end
end
- # Array#flatten has problems with recursive arrays. Going one level
- # deeper solves the majority of the problems.
- def flatten_deeper(array)
- array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
+ if RUBY_VERSION < '1.9.2'
+ # Array#flatten has problems with recursive arrays before Ruby 1.9.2.
+ # Going one level deeper solves the majority of the problems.
+ def flatten_deeper(array)
+ array.collect { |element| (element.respond_to?(:flatten) && !element.is_a?(Hash)) ? element.flatten : element }.flatten
+ end
+ else
+ def flatten_deeper(array)
+ array.flatten
+ end
end
# Returns the ID of the owner, quoted if needed.
diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
index c646fe488b..b01faa5212 100644
--- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
+++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -1,11 +1,6 @@
module ActiveRecord
module Associations
class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
- def initialize(owner, reflection)
- super
- @primary_key_list = {}
- end
-
def create(attributes = {})
create_record(attributes) { |record| insert_record(record) }
end
@@ -23,9 +18,7 @@ module ActiveRecord
end
def has_primary_key?
- return @has_primary_key unless @has_primary_key.nil?
- @has_primary_key = (@owner.connection.supports_primary_key? &&
- @owner.connection.primary_key(@reflection.options[:join_table]))
+ @has_primary_key ||= @owner.connection.supports_primary_key? && @owner.connection.primary_key(@reflection.options[:join_table])
end
protected
@@ -40,11 +33,6 @@ module ActiveRecord
end
def insert_record(record, force = true, validate = true)
- if has_primary_key?
- raise ActiveRecord::ConfigurationError,
- "Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})."
- end
-
if record.new_record?
if force
record.save!
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index ab7ad34b9e..3a9a67e3a2 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -31,11 +31,10 @@ module ActiveRecord
self.class.define_attribute_methods
method_name = method_id.to_s
guard_private_attribute_method!(method_name, args)
- if self.class.generated_attribute_methods.instance_methods.include?(method_name)
- return self.send(method_id, *args, &block)
- end
+ send(method_id, *args, &block)
+ else
+ super
end
- super
end
def respond_to?(*args)
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index c8cd79a2b0..986bc7009b 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -23,16 +23,6 @@ module ActiveRecord
# p2.first_name = "should fail"
# p2.save # Raises a ActiveRecord::StaleObjectError
#
- # Optimistic locking will also check for stale data when objects are destroyed. Example:
- #
- # p1 = Person.find(1)
- # p2 = Person.find(1)
- #
- # p1.first_name = "Michael"
- # p1.save
- #
- # p2.destroy # Raises a ActiveRecord::StaleObjectError
- #
# You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
# or otherwise apply the business logic needed to resolve the conflict.
#
@@ -49,7 +39,6 @@ module ActiveRecord
self.lock_optimistically = true
alias_method_chain :update, :lock
- alias_method_chain :destroy, :lock
alias_method_chain :attributes_from_column_definition, :lock
class << self
@@ -111,29 +100,6 @@ module ActiveRecord
end
end
- def destroy_with_lock #:nodoc:
- return destroy_without_lock unless locking_enabled?
-
- unless new_record?
- lock_col = self.class.locking_column
- previous_value = send(lock_col).to_i
-
- arel_table = self.class.arel_table(self.class.table_name)
-
- affected_rows = arel_table.where(
- arel_table[self.class.primary_key].eq(quoted_id).and(
- arel_table[self.class.locking_column].eq(quote_value(previous_value))
- )
- ).delete
-
- unless affected_rows == 1
- raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object"
- end
- end
-
- freeze
- end
-
module ClassMethods
DEFAULT_LOCKING_COLUMN = 'lock_version'
diff --git a/activerecord/test/cases/associations/habtm_join_table_test.rb b/activerecord/test/cases/associations/habtm_join_table_test.rb
index bf3e04c3eb..745f169ad7 100644
--- a/activerecord/test/cases/associations/habtm_join_table_test.rb
+++ b/activerecord/test/cases/associations/habtm_join_table_test.rb
@@ -36,21 +36,9 @@ class HabtmJoinTableTest < ActiveRecord::TestCase
uses_transaction :test_should_raise_exception_when_join_table_has_a_primary_key
def test_should_raise_exception_when_join_table_has_a_primary_key
if ActiveRecord::Base.connection.supports_primary_key?
- assert_raise ActiveRecord::ConfigurationError do
- jaime = MyReader.create(:name=>"Jaime")
- jaime.my_books << MyBook.create(:name=>'Great Expectations')
+ assert_raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError do
+ MyReader.has_and_belongs_to_many :my_books
end
end
end
-
- uses_transaction :test_should_cache_result_of_primary_key_check
- def test_should_cache_result_of_primary_key_check
- if ActiveRecord::Base.connection.supports_primary_key?
- ActiveRecord::Base.connection.stubs(:primary_key).with('my_books_my_readers').returns(false).once
- weaz = MyReader.create(:name=>'Weaz')
-
- weaz.my_books << MyBook.create(:name=>'Great Expectations')
- weaz.my_books << MyBook.create(:name=>'Greater Expectations')
- end
- end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 5f13b66d11..fe68d03de2 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -137,6 +137,28 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert !posts(:welcome).reload.people(true).include?(people(:michael))
end
+ def test_replace_order_is_preserved
+ posts(:welcome).people.clear
+ posts(:welcome).people = [people(:david), people(:michael)]
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
+
+ # Test the inverse order in case the first success was a coincidence
+ posts(:welcome).people.clear
+ posts(:welcome).people = [people(:michael), people(:david)]
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
+ end
+
+ def test_replace_by_id_order_is_preserved
+ posts(:welcome).people.clear
+ posts(:welcome).person_ids = [people(:david).id, people(:michael).id]
+ assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
+
+ # Test the inverse order in case the first success was a coincidence
+ posts(:welcome).people.clear
+ posts(:welcome).person_ids = [people(:michael).id, people(:david).id]
+ assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
+ end
+
def test_associate_with_create
assert_queries(1) { posts(:thinking) }
diff --git a/activerecord/test/cases/copy_table_test_sqlite.rb b/activerecord/test/cases/copy_table_test_sqlite.rb
index de8af30997..575b4806c1 100644
--- a/activerecord/test/cases/copy_table_test_sqlite.rb
+++ b/activerecord/test/cases/copy_table_test_sqlite.rb
@@ -1,7 +1,7 @@
require "cases/helper"
class CopyTableTest < ActiveRecord::TestCase
- fixtures :companies, :comments
+ fixtures :customers, :companies, :comments
def setup
@connection = ActiveRecord::Base.connection
@@ -27,8 +27,8 @@ class CopyTableTest < ActiveRecord::TestCase
test_copy_table('customers', 'customers2',
:rename => {'name' => 'person_name'}) do |from, to, options|
expected = column_values(from, 'name')
- assert expected.any?, 'only nils in resultset; real values are needed'
assert_equal expected, column_values(to, 'person_name')
+ assert expected.any?, "No values in table: #{expected.inspect}"
end
end
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index e177235591..a64c01292f 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -38,24 +38,6 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
end
- def test_lock_destroy
- p1 = Person.find(1)
- p2 = Person.find(1)
- assert_equal 0, p1.lock_version
- assert_equal 0, p2.lock_version
-
- p1.first_name = 'stu'
- p1.save!
- assert_equal 1, p1.lock_version
- assert_equal 0, p2.lock_version
-
- assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
-
- assert p1.destroy
- assert_equal true, p1.frozen?
- assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
- end
-
def test_lock_repeating
p1 = Person.find(1)
p2 = Person.find(1)
@@ -282,11 +264,14 @@ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
assert first.end > second.end
end
- def test_second_lock_waits
- assert [0.2, 1, 5].any? { |zzz|
- first, second = duel(zzz) { Person.find 1, :lock => true }
- second.end > first.end
- }
+ # Hit by ruby deadlock detection since connection checkout is mutexed.
+ if RUBY_VERSION < '1.9.0'
+ def test_second_lock_waits
+ assert [0.2, 1, 5].any? { |zzz|
+ first, second = duel(zzz) { Person.find 1, :lock => true }
+ second.end > first.end
+ }
+ end
end
protected
diff --git a/activerecord/test/fixtures/edges.yml b/activerecord/test/fixtures/edges.yml
index c16c70dd2f..b804f7b6a6 100644
--- a/activerecord/test/fixtures/edges.yml
+++ b/activerecord/test/fixtures/edges.yml
@@ -1,6 +1,5 @@
<% (1..4).each do |id| %>
edge_<%= id %>:
- id: <%= id %>
source_id: <%= id %>
sink_id: <%= id + 1 %>
-<% end %> \ No newline at end of file
+<% end %>
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 15e5e12d03..0dd9da4c11 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -160,7 +160,7 @@ ActiveRecord::Schema.define do
t.integer :access_level, :default => 1
end
- create_table :edges, :force => true do |t|
+ create_table :edges, :force => true, :id => false do |t|
t.column :source_id, :integer, :null => false
t.column :sink_id, :integer, :null => false
end
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index bec9de86ed..1b6b820ca4 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -38,6 +38,11 @@ module ActiveSupport
#
# If no addresses are specified, then MemCacheStore will connect to
# localhost port 11211 (the default memcached port).
+ #
+ # Instead of addresses one can pass in a MemCache-like object. For example:
+ #
+ # require 'memcached' # gem install memcached; uses C bindings to libmemcached
+ # ActiveSupport::Cache::MemCacheStore.new(Memcached::Rails.new("localhost:11211"))
def initialize(*addresses)
if addresses.first.respond_to?(:get)
@data = addresses.first
diff --git a/activesupport/lib/active_support/core_ext/array/wrap.rb b/activesupport/lib/active_support/core_ext/array/wrap.rb
index 38bb68c1ec..f48d5ce500 100644
--- a/activesupport/lib/active_support/core_ext/array/wrap.rb
+++ b/activesupport/lib/active_support/core_ext/array/wrap.rb
@@ -4,11 +4,10 @@ class Array
def self.wrap(object)
if object.nil?
[]
- # to_a doesn't work correctly with Array() but to_ary always does
- elsif object.respond_to?(:to_a) && !object.respond_to?(:to_ary)
- [object]
+ elsif object.respond_to?(:to_ary)
+ object.to_ary
else
- Array(object)
+ [object]
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/date.rb b/activesupport/lib/active_support/core_ext/date.rb
deleted file mode 100644
index 6672129076..0000000000
--- a/activesupport/lib/active_support/core_ext/date.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require 'date'
-
-require 'active_support/core_ext/date/acts_like'
-require 'active_support/core_ext/date/freeze'
-
-require 'active_support/core_ext/date/calculations'
-require 'active_support/core_ext/date/conversions'
diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb
index b41ad5b686..f6c870035b 100644
--- a/activesupport/lib/active_support/core_ext/date/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/date/conversions.rb
@@ -1,7 +1,4 @@
require 'active_support/inflector'
-require 'active_support/core_ext/time/conversions'
-require 'active_support/core_ext/date_time/conversions'
-require 'active_support/values/time_zone'
class Date
DATE_FORMATS = {
diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb
deleted file mode 100644
index 004fd0ad29..0000000000
--- a/activesupport/lib/active_support/core_ext/date_time.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'active_support/core_ext/time'
-require 'active_support/core_ext/date_time/acts_like'
-require 'active_support/core_ext/date_time/calculations'
-require 'active_support/core_ext/date_time/conversions'
-require 'active_support/core_ext/date_time/zones'
diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb
index 22749229a3..e4df8fe338 100644
--- a/activesupport/lib/active_support/core_ext/logger.rb
+++ b/activesupport/lib/active_support/core_ext/logger.rb
@@ -137,10 +137,10 @@ class Logger
attr_writer :formatter
public :formatter=
- alias old_format_datetime format_datetime
+ alias old_format_datetime format_datetime if method_defined?(:format_datetime)
def format_datetime(datetime) datetime end
- alias old_msg2str msg2str
+ alias old_msg2str msg2str if method_defined?(:msg2str)
def msg2str(msg) msg end
end
end
diff --git a/activesupport/lib/active_support/core_ext/name_error.rb b/activesupport/lib/active_support/core_ext/name_error.rb
index cd165626c8..e1ebd4f91c 100644
--- a/activesupport/lib/active_support/core_ext/name_error.rb
+++ b/activesupport/lib/active_support/core_ext/name_error.rb
@@ -1,7 +1,9 @@
class NameError
# Extract the name of the missing constant from the exception message.
def missing_name
- $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
+ if /undefined local variable or method/ !~ message
+ $1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
+ end
end
# Was this exception raised because the given name was missing?
diff --git a/activesupport/lib/active_support/core_ext/string/interpolation.rb b/activesupport/lib/active_support/core_ext/string/interpolation.rb
index 41a061c1a4..2048d35091 100644
--- a/activesupport/lib/active_support/core_ext/string/interpolation.rb
+++ b/activesupport/lib/active_support/core_ext/string/interpolation.rb
@@ -5,7 +5,7 @@
You may redistribute it and/or modify it under the same license terms as Ruby.
=end
-if RUBY_VERSION < '1.9'
+if RUBY_VERSION < '1.9' && !"".respond_to?(:interpolate_without_ruby_19_syntax)
# KeyError is raised by String#% when the string contains a named placeholder
# that is not contained in the given arguments hash. Ruby 1.9 includes and
diff --git a/activesupport/lib/active_support/core_ext/time.rb b/activesupport/lib/active_support/core_ext/time.rb
deleted file mode 100644
index b28f7f1a32..0000000000
--- a/activesupport/lib/active_support/core_ext/time.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'date'
-require 'time'
-
-require 'active_support/core_ext/time/publicize_conversion_methods'
-require 'active_support/core_ext/time/marshal_with_utc_flag'
-
-require 'active_support/core_ext/time/acts_like'
-require 'active_support/core_ext/time/calculations'
-require 'active_support/core_ext/time/conversions'
-require 'active_support/core_ext/time/zones'
diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb
index 8e8f9022c1..3c15056c41 100644
--- a/activesupport/lib/active_support/json/encoding.rb
+++ b/activesupport/lib/active_support/json/encoding.rb
@@ -6,7 +6,7 @@ require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/instance_variables'
require 'active_support/deprecation'
-require 'active_support/time_with_zone'
+require 'active_support/time'
# Hack to load json gem first so we can overwrite its to_json.
begin
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 6304f496f5..e2540cd598 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -41,7 +41,7 @@ module ActiveSupport
# to subscribers in a thread. You can use any queue implementation you want.
#
module Notifications
- mattr_accessor :queue
+ mattr_accessor :queue, :listener
class << self
delegate :instrument, :transaction_id, :transaction, :to => :instrumenter
@@ -54,8 +54,13 @@ module ActiveSupport
@publisher ||= Publisher.new(queue)
end
- def subscribe(pattern=nil, &block)
- Subscriber.new(queue).bind(pattern).subscribe(&block)
+ def subscriber
+ @subscriber ||= Subscriber.new(queue)
+ end
+
+ def subscribe(pattern=nil, options={}, &block)
+ with = options[:with] || listener
+ subscriber.bind(with, pattern).subscribe(&block)
end
end
@@ -104,13 +109,14 @@ module ActiveSupport
@queue = queue
end
- def bind(pattern)
- @pattern = pattern
+ def bind(listener, pattern)
+ @listener = listener
+ @pattern = pattern
self
end
def subscribe
- @queue.subscribe(@pattern) do |*args|
+ @queue.subscribe(@listener, @pattern) do |*args|
yield(*args)
end
end
@@ -138,54 +144,70 @@ module ActiveSupport
end
end
- # This is a default queue implementation that ships with Notifications. It
- # consumes events in a thread and publish them to all registered subscribers.
- #
- class LittleFanout
- def initialize
- @listeners = []
- @stream = Queue.new
+ class AsyncListener
+ def initialize(pattern, &block)
+ @pattern = pattern
+ @subscriber = block
+ @queue = Queue.new
Thread.new { consume }
end
- def publish(*args)
- @stream.push(args)
- end
-
- def subscribe(pattern=nil, &block)
- @listeners << Listener.new(pattern, &block)
+ def publish(name, *args)
+ if !@pattern || @pattern === name.to_s
+ @queue << args.unshift(name)
+ end
end
def consume
- while args = @stream.shift
- @listeners.each { |l| l.publish(*args) }
+ while args = @queue.shift
+ @subscriber.call(*args)
end
end
- class Listener
- # attr_reader :thread
+ def drained?
+ @queue.size.zero?
+ end
+ end
- def initialize(pattern, &block)
- @pattern = pattern
- @subscriber = block
- @queue = Queue.new
- Thread.new { consume }
- end
+ class SyncListener
+ def initialize(pattern, &block)
+ @pattern = pattern
+ @subscriber = block
+ end
- def publish(name, *args)
- if !@pattern || @pattern === name.to_s
- @queue << args.unshift(name)
- end
+ def publish(name, *args)
+ if !@pattern || @pattern === name.to_s
+ @subscriber.call(*args.unshift(name))
end
+ end
- def consume
- while args = @queue.shift
- @subscriber.call(*args)
- end
- end
+ def drained?
+ true
+ end
+ end
+
+ # This is a default queue implementation that ships with Notifications. It
+ # consumes events in a thread and publish them to all registered subscribers.
+ #
+ class LittleFanout
+ def initialize
+ @listeners = []
+ end
+
+ def publish(*args)
+ @listeners.each { |l| l.publish(*args) }
+ end
+
+ def subscribe(listener, pattern=nil, &block)
+ @listeners << listener.new(pattern, &block)
+ end
+
+ def drained?
+ @listeners.all? &:drained?
end
end
end
- Notifications.queue = Notifications::LittleFanout.new
+ Notifications.queue = Notifications::LittleFanout.new
+ Notifications.listener = Notifications::AsyncListener
end
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index 879662c16c..f0119f5994 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -81,7 +81,7 @@ module ActiveSupport
def handler_for_rescue(exception)
# We go from right to left because pairs are pushed onto rescue_handlers
# as rescue_from declarations are found.
- _, rescuer = Array(rescue_handlers).reverse.detect do |klass_name, handler|
+ _, rescuer = rescue_handlers.reverse.detect do |klass_name, handler|
# The purpose of allowing strings in rescue_from is to support the
# declaration of handler associations for exception classes whose
# definition is yet unknown.
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index c75b59c284..81f7e3c9df 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -1,10 +1,25 @@
module ActiveSupport
module Testing
+ class RemoteError < StandardError
+
+ attr_reader :message, :backtrace
+
+ def initialize(exception)
+ @message = "caught #{exception.class.name}: #{exception.message}"
+ @backtrace = exception.backtrace
+ end
+ end
+
class ProxyTestResult
def initialize
@calls = []
end
+ def add_error(e)
+ e = Test::Unit::Error.new(e.test_name, RemoteError.new(e.exception))
+ @calls << [:add_error, e]
+ end
+
def __replay__(result)
@calls.each do |name, args|
result.send(name, *args)
diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb
index d36a683601..0f421421d0 100644
--- a/activesupport/lib/active_support/time.rb
+++ b/activesupport/lib/active_support/time.rb
@@ -1,7 +1,4 @@
require 'active_support'
-require 'active_support/core_ext/time'
-require 'active_support/core_ext/date'
-require 'active_support/core_ext/date_time'
module ActiveSupport
autoload :Duration, 'active_support/duration'
@@ -12,3 +9,26 @@ module ActiveSupport
[Duration, TimeWithZone, TimeZone]
end
end
+
+require 'date'
+require 'time'
+
+require 'active_support/core_ext/time/publicize_conversion_methods'
+require 'active_support/core_ext/time/marshal_with_utc_flag'
+require 'active_support/core_ext/time/acts_like'
+require 'active_support/core_ext/time/calculations'
+require 'active_support/core_ext/time/conversions'
+require 'active_support/core_ext/time/zones'
+
+require 'active_support/core_ext/date/acts_like'
+require 'active_support/core_ext/date/freeze'
+require 'active_support/core_ext/date/calculations'
+require 'active_support/core_ext/date/conversions'
+
+require 'active_support/core_ext/date_time/acts_like'
+require 'active_support/core_ext/date_time/calculations'
+require 'active_support/core_ext/date_time/conversions'
+require 'active_support/core_ext/date_time/zones'
+
+require 'active_support/core_ext/integer/time'
+require 'active_support/core_ext/numeric/time'
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index a64128e49e..8304f6c434 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -1,10 +1,4 @@
-require 'active_support/duration'
-require 'active_support/values/time_zone'
-require 'active_support/core_ext/numeric/time'
-require 'active_support/core_ext/integer/time'
-require 'active_support/core_ext/time/conversions'
-require 'active_support/core_ext/date/conversions'
-require 'active_support/core_ext/date_time/conversions'
+require "active_support/values/time_zone"
module ActiveSupport
# A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are
diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb
index e7583bef1b..cbb8e890ae 100644
--- a/activesupport/lib/active_support/values/time_zone.rb
+++ b/activesupport/lib/active_support/values/time_zone.rb
@@ -1,7 +1,4 @@
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/time'
-require 'active_support/core_ext/date'
-require 'active_support/core_ext/date_time'
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following:
#
diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb
index 4a711b7e25..eb5080888c 100644
--- a/activesupport/lib/active_support/vendor.rb
+++ b/activesupport/lib/active_support/vendor.rb
@@ -9,7 +9,7 @@ end
unless ActiveSupport.requirable? lib
# Try to activate a gem ~> satisfying the requested version first.
begin
- gem lib, "~> #{version}"
+ gem lib, ">= #{version}"
# Use the vendored lib if the gem's missing or we aren't using RubyGems.
rescue LoadError, NoMethodError
# There could be symlinks
diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb
index 36fe9510ba..c4aaba7ab3 100644
--- a/activesupport/lib/active_support/whiny_nil.rb
+++ b/activesupport/lib/active_support/whiny_nil.rb
@@ -43,7 +43,14 @@ class NilClass
private
def method_missing(method, *args, &block)
- raise_nil_warning_for METHOD_CLASS_MAP[method], method, caller
+ # Ruby 1.9.2: disallow explicit coercion via method_missing.
+ if method == :to_ary || method == :to_str
+ super
+ elsif klass = METHOD_CLASS_MAP[method]
+ raise_nil_warning_for klass, method, caller
+ else
+ super
+ end
end
# Raises a NoMethodError when you attempt to call a method on +nil+.
@@ -55,4 +62,3 @@ class NilClass
raise NoMethodError, message, with_caller || caller
end
end
-
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 6a51ce9402..00e05f76fe 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -30,7 +30,9 @@ class CacheStoreSettingTest < ActiveSupport::TestCase
def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object
MemCache.expects(:new).never
- store = ActiveSupport::Cache.lookup_store :mem_cache_store, stub("memcache", :get => true)
+ memcache = Object.new
+ def memcache.get() true end
+ store = ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
end
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index 8198b9bd2c..f5f91ddd80 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -339,6 +339,11 @@ class ArrayWrapperTests < Test::Unit::TestCase
end
end
+ class Proxy
+ def initialize(target) @target = target end
+ def method_missing(*a) @target.send(*a) end
+ end
+
def test_array
ary = %w(foo bar)
assert_same ary, Array.wrap(ary)
@@ -364,4 +369,19 @@ class ArrayWrapperTests < Test::Unit::TestCase
def test_object_with_to_ary
assert_equal ["foo", "bar"], Array.wrap(FakeCollection.new)
end
+
+ def test_proxy_object
+ p = Proxy.new(Object.new)
+ assert_equal [p], Array.wrap(p)
+ end
+
+ def test_proxy_to_object_with_to_ary
+ p = Proxy.new(FakeCollection.new)
+ assert_equal [p], Array.wrap(p)
+ end
+
+ def test_struct
+ o = Struct.new(:foo).new(123)
+ assert_equal [o], Array.wrap(o)
+ end
end
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index 18422d68bc..23c9bc7fb1 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -1,5 +1,5 @@
require 'abstract_unit'
-require 'active_support/core_ext/date'
+require 'active_support/time'
class DateExtCalculationsTest < Test::Unit::TestCase
def test_to_s
diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb
index a7b179b2be..4341ead488 100644
--- a/activesupport/test/core_ext/date_time_ext_test.rb
+++ b/activesupport/test/core_ext/date_time_ext_test.rb
@@ -1,5 +1,5 @@
require 'abstract_unit'
-require 'active_support/core_ext/date_time'
+require 'active_support/time'
class DateTimeExtCalculationsTest < Test::Unit::TestCase
def test_to_s
diff --git a/activesupport/test/core_ext/name_error_test.rb b/activesupport/test/core_ext/name_error_test.rb
index 10913e2ade..6352484d04 100644
--- a/activesupport/test/core_ext/name_error_test.rb
+++ b/activesupport/test/core_ext/name_error_test.rb
@@ -3,23 +3,19 @@ require 'active_support/core_ext/name_error'
class NameErrorTest < Test::Unit::TestCase
def test_name_error_should_set_missing_name
- begin
- SomeNameThatNobodyWillUse____Really ? 1 : 0
- flunk "?!?!"
- rescue NameError => exc
- assert_equal "NameErrorTest::SomeNameThatNobodyWillUse____Really", exc.missing_name
- assert exc.missing_name?(:SomeNameThatNobodyWillUse____Really)
- assert exc.missing_name?("NameErrorTest::SomeNameThatNobodyWillUse____Really")
- end
+ SomeNameThatNobodyWillUse____Really ? 1 : 0
+ flunk "?!?!"
+ rescue NameError => exc
+ assert_equal "NameErrorTest::SomeNameThatNobodyWillUse____Really", exc.missing_name
+ assert exc.missing_name?(:SomeNameThatNobodyWillUse____Really)
+ assert exc.missing_name?("NameErrorTest::SomeNameThatNobodyWillUse____Really")
end
def test_missing_method_should_ignore_missing_name
- begin
- some_method_that_does_not_exist
- flunk "?!?!"
- rescue NameError => exc
- assert_equal nil, exc.missing_name
- assert ! exc.missing_name?(:Foo)
- end
+ some_method_that_does_not_exist
+ flunk "?!?!"
+ rescue NameError => exc
+ assert !exc.missing_name?(:Foo)
+ assert_nil exc.missing_name
end
end
diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb
index 2565c56b8a..5701eeef28 100644
--- a/activesupport/test/core_ext/range_ext_test.rb
+++ b/activesupport/test/core_ext/range_ext_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
+require 'active_support/time'
require 'active_support/core_ext/range'
-require 'active_support/core_ext/date/conversions'
class RangeTest < Test::Unit::TestCase
def test_to_s_from_dates
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 1e8a1c4f08..56ed296dac 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -4,7 +4,7 @@ require 'abstract_unit'
require 'inflector_test_cases'
require 'active_support/core_ext/string'
-require 'active_support/core_ext/time'
+require 'active_support/time'
require 'active_support/core_ext/kernel/reporting'
class StringInflectionsTest < Test::Unit::TestCase
diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb
index 9868f1e87d..dfcd4f822d 100644
--- a/activesupport/test/i18n_test.rb
+++ b/activesupport/test/i18n_test.rb
@@ -9,8 +9,8 @@ class I18nTest < Test::Unit::TestCase
end
def test_time_zone_localization_with_default_format
- Time.zone.stubs(:now).returns Time.local(2000)
- assert_equal Time.zone.now.strftime("%a, %d %b %Y %H:%M:%S %z"), I18n.localize(Time.zone.now)
+ now = Time.local(2000)
+ assert_equal now.strftime("%a, %d %b %Y %H:%M:%S %z"), I18n.localize(now)
end
def test_date_localization_should_use_default_format
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index 7b5a4d0416..8fcb16abfb 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -1,6 +1,7 @@
# encoding: UTF-8
require 'abstract_unit'
require 'active_support/json'
+require 'active_support/time'
require 'active_support/core_ext/kernel/reporting'
class TestJSONDecoding < ActiveSupport::TestCase
diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index ed3461571a..5c2b44f188 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/time'
class MessageEncryptorTest < Test::Unit::TestCase
def setup
diff --git a/activesupport/test/message_verifier_test.rb b/activesupport/test/message_verifier_test.rb
index ef300e4e26..714a3b3a39 100644
--- a/activesupport/test/message_verifier_test.rb
+++ b/activesupport/test/message_verifier_test.rb
@@ -7,6 +7,8 @@ rescue LoadError, NameError
$stderr.puts "Skipping MessageVerifier test: broken OpenSSL install"
else
+require 'active_support/time'
+
class MessageVerifierTest < Test::Unit::TestCase
def setup
@verifier = ActiveSupport::MessageVerifier.new("Hey, I'm a secret!")
diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb
index 9175c8f26e..35d44367cf 100644
--- a/activesupport/test/notifications_test.rb
+++ b/activesupport/test/notifications_test.rb
@@ -75,7 +75,7 @@ class NotificationsMainTest < Test::Unit::TestCase
1 + 1
end
- sleep(0.1)
+ drain
assert_equal 1, @events.size
assert_equal :awesome, @events.last.name
@@ -88,19 +88,18 @@ class NotificationsMainTest < Test::Unit::TestCase
1 + 1
end
- sleep(0.1)
+ drain
assert_equal 1, @events.size
assert_equal :wot, @events.first.name
assert_equal Hash[:payload => "child"], @events.first.payload
end
- sleep(0.1)
+ drain
assert_equal 2, @events.size
assert_equal :awesome, @events.last.name
assert_equal Hash[:payload => "notifications"], @events.last.payload
- assert_in_delta 100, @events.last.duration, 70
end
def test_event_is_pushed_even_if_block_fails
@@ -108,7 +107,7 @@ class NotificationsMainTest < Test::Unit::TestCase
raise "OMG"
end rescue RuntimeError
- sleep(0.1)
+ drain
assert_equal 1, @events.size
assert_equal :awesome, @events.last.name
@@ -117,7 +116,7 @@ class NotificationsMainTest < Test::Unit::TestCase
def test_event_is_pushed_even_without_block
ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications")
- sleep(0.1)
+ drain
assert_equal 1, @events.size
assert_equal :awesome, @events.last.name
@@ -137,8 +136,9 @@ class NotificationsMainTest < Test::Unit::TestCase
end
ActiveSupport::Notifications.instrument(:cache){ 1 }
- sleep 0.1
+ drain
+ assert_equal 3, @another.size
before, during, after = @another.map {|e| e.transaction_id }
assert_equal before, after
assert_not_equal before, during
@@ -153,7 +153,7 @@ class NotificationsMainTest < Test::Unit::TestCase
ActiveSupport::Notifications.instrument(:cache){ 1 }
- sleep(0.1)
+ drain
assert_equal 1, @another.size
assert_equal :cache, @another.first.name
@@ -169,7 +169,22 @@ class NotificationsMainTest < Test::Unit::TestCase
ActiveSupport::Notifications.instrument(:something){ 0 }
ActiveSupport::Notifications.instrument(:cache){ 1 }
- sleep(0.1)
+ drain
+
+ assert_equal 1, @another.size
+ assert_equal :cache, @another.first.name
+ assert_equal 1, @another.first.result
+ end
+
+ def test_subscriber_allows_sync_listeners
+ @another = []
+ ActiveSupport::Notifications.subscribe(/cache/, :with => ActiveSupport::Notifications::SyncListener) do |*args|
+ @another << ActiveSupport::Notifications::Event.new(*args)
+ end
+
+ Thread.expects(:new).never
+ ActiveSupport::Notifications.instrument(:something){ 0 }
+ ActiveSupport::Notifications.instrument(:cache){ 1 }
assert_equal 1, @another.size
assert_equal :cache, @another.first.name
@@ -186,7 +201,7 @@ class NotificationsMainTest < Test::Unit::TestCase
ActiveSupport::Notifications.instrument(:value){ i }
end
- sleep 0.1
+ drain
assert_equal 100, @events.size
assert_equal :value, @events.first.name
@@ -198,4 +213,9 @@ class NotificationsMainTest < Test::Unit::TestCase
assert_equal 1, @another.first.result
assert_equal 100, @another.last.result
end
+
+ private
+ def drain
+ sleep(0.05) until ActiveSupport::Notifications.queue.drained?
+ end
end
diff --git a/activesupport/test/whiny_nil_test.rb b/activesupport/test/whiny_nil_test.rb
index 4cb22c41b2..009d97940f 100644
--- a/activesupport/test/whiny_nil_test.rb
+++ b/activesupport/test/whiny_nil_test.rb
@@ -13,7 +13,7 @@ class WhinyNilTest < Test::Unit::TestCase
def test_unchanged
nil.method_thats_not_in_whiners
rescue NoMethodError => nme
- assert_match(/nil.method_thats_not_in_whiners/, nme.message)
+ assert(nme.message =~ /nil:NilClass/)
end
def test_active_record
@@ -35,4 +35,16 @@ class WhinyNilTest < Test::Unit::TestCase
rescue RuntimeError => nme
assert(!(nme.message =~ /nil:NilClass/))
end
+
+ def test_no_to_ary_coercion
+ nil.to_ary
+ rescue NoMethodError => nme
+ assert(nme.message =~ /nil:NilClass/)
+ end
+
+ def test_no_to_str_coercion
+ nil.to_str
+ rescue NoMethodError => nme
+ assert(nme.message =~ /nil:NilClass/)
+ end
end
diff --git a/arel b/arel
new file mode 160000
+Subproject 0faeb5047407348533db952d9cf93ea59d2526d
diff --git a/rack-mount b/rack-mount
new file mode 160000
+Subproject 3784e633b42f43a4131e02519be60080d179da2
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index d6311f77a0..66e0d5e9c5 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* Fixed that the debugger wouldn't go into IRB mode because of left-over ARGVs [DHH]
+
* I18n support for plugins. #2325 [Antonio Tapiador, Sven Fuchs]
* Ruby 1.9: use UTF-8 for default internal and external encodings. [Jeremy Kemper]
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index 43ece14a49..c23b67e321 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -1 +1,13 @@
+require "pathname"
+
+require 'rails/initializable'
+require 'rails/application'
+require 'rails/railties_path'
+require 'rails/version'
+require 'rails/rack'
+require 'rails/paths'
+require 'rails/core'
+require 'rails/configuration'
+require 'rails/deprecation'
require 'rails/initializer'
+require 'rails/plugin' \ No newline at end of file
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index b43dcb0041..7c2d8eab67 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -3,6 +3,11 @@ module Rails
include Initializable
class << self
+ def inherited(klass)
+ Rails.application ||= klass unless klass.name =~ /Rails/
+ super
+ end
+
# Stub out App initialize
def initialize!
new
@@ -23,10 +28,6 @@ module Rails
@config = config
end
- def plugin_loader
- @plugin_loader ||= config.plugin_loader.new(self)
- end
-
def root
config.root
end
@@ -37,7 +38,7 @@ module Rails
end
def initialize
- run_initializers
+ run_initializers(self)
end
def config
@@ -46,10 +47,6 @@ module Rails
alias configuration config
- def plugin_loader
- self.class.plugin_loader
- end
-
def middleware
config.middleware
end
@@ -58,6 +55,18 @@ module Rails
ActionController::Routing::Routes
end
+ def initializers
+ initializers = super
+ plugins.each { |p| initializers += p.initializers }
+ initializers
+ end
+
+ def plugins
+ @plugins ||= begin
+ Plugin::Vendored.all(config.plugins || [:all], config.paths.vendor.plugins)
+ end
+ end
+
def call(env)
@app ||= middleware.build(routes)
@app.call(env)
@@ -111,13 +120,6 @@ module Rails
config.load_once_paths.freeze
end
- # Adds all load paths from plugins to the global set of load paths, so that
- # code from plugins can be required (explicitly or automatically via ActiveSupport::Dependencies).
- initializer :add_plugin_load_paths do
- require 'active_support/dependencies'
- plugin_loader.add_plugin_load_paths
- end
-
# Create tmp directories
initializer :ensure_tmp_directories_exist do
%w(cache pids sessions sockets).each do |dir_to_make|
@@ -321,7 +323,6 @@ module Rails
# TODO: Make Rails and metal work without ActionController
if config.frameworks.include?(:action_controller)
Rails::Rack::Metal.requested_metals = config.metals
- Rails::Rack::Metal.metal_paths += plugin_loader.engine_metal_paths
config.middleware.insert_before(
:"ActionDispatch::ParamsParser",
@@ -329,26 +330,6 @@ module Rails
end
end
- # Loads all plugins in <tt>config.plugin_paths</tt>. <tt>plugin_paths</tt>
- # defaults to <tt>vendor/plugins</tt> but may also be set to a list of
- # paths, such as
- # config.plugin_paths = ["#{config.root}/lib/plugins", "#{config.root}/vendor/plugins"]
- #
- # In the default implementation, as each plugin discovered in <tt>plugin_paths</tt> is initialized:
- # * its +lib+ directory, if present, is added to the load path (immediately after the applications lib directory)
- # * <tt>init.rb</tt> is evaluated, if present
- #
- # After all plugins are loaded, duplicates are removed from the load path.
- # If an array of plugin names is specified in config.plugins, only those plugins will be loaded
- # and they plugins will be loaded in that order. Otherwise, plugins are loaded in alphabetical
- # order.
- #
- # if config.plugins ends contains :all then the named plugins will be loaded in the given order and all other
- # plugins will be loaded in alphabetical order
- initializer :load_plugins do
- plugin_loader.load_plugins
- end
-
# # bail out if gems are missing - note that check_gem_dependencies will have
# # already called abort() unless $gems_rake_task is set
# return unless gems_dependencies_loaded
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
index 29359e49a4..2c90851fb2 100644
--- a/railties/lib/rails/commands/server.rb
+++ b/railties/lib/rails/commands/server.rb
@@ -1,4 +1,4 @@
-require 'action_controller'
+require 'action_dispatch'
require 'fileutils'
require 'optparse'
@@ -42,7 +42,7 @@ unless server
end
puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
-puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}}"
+puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}"
if options[:detach]
Process.daemon
diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb
index 2b847ba711..102a0836dc 100644
--- a/railties/lib/rails/configuration.rb
+++ b/railties/lib/rails/configuration.rb
@@ -1,17 +1,13 @@
-require 'rails/plugin/loader'
-require 'rails/plugin/locator'
require 'active_support/ordered_options'
module Rails
class Configuration
- attr_accessor :cache_classes, :load_paths,
- :load_once_paths, :after_initialize_blocks,
- :frameworks, :framework_root_path, :root, :plugin_paths, :plugins,
- :plugin_loader, :plugin_locators, :gems, :loaded_plugins, :reload_plugins,
+ attr_accessor :cache_classes, :load_paths, :load_once_paths, :after_initialize_blocks,
+ :frameworks, :framework_root_path, :root, :gems, :plugins,
:i18n, :gems, :whiny_nils, :consider_all_requests_local,
:action_controller, :active_record, :action_view, :active_support,
:action_mailer, :active_resource,
- :log_path, :log_level, :logger, :preload_frameworks,
+ :reload_plugins, :log_path, :log_level, :logger, :preload_frameworks,
:database_configuration_file, :cache_store, :time_zone,
:view_path, :metals, :controller_paths, :routes_configuration_file,
:eager_load_paths, :dependency_loading, :paths, :serve_static_assets
@@ -19,7 +15,6 @@ module Rails
def initialize
@load_once_paths = []
@after_initialize_blocks = []
- @loaded_plugins = []
@dependency_loading = true
@serve_static_assets = true
@@ -199,24 +194,6 @@ module Rails
@frameworks ||= [ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ]
end
- def plugin_paths
- @plugin_paths ||= ["#{root}/vendor/plugins"]
- end
-
- def plugin_loader
- @plugin_loader ||= begin
- Plugin::Loader
- end
- end
-
- def plugin_locators
- @plugin_locators ||= begin
- locators = []
- locators << Plugin::GemLocator if defined? Gem
- locators << Plugin::FileSystemLocator
- end
- end
-
def i18n
@i18n ||= begin
i18n = ActiveSupport::OrderedOptions.new
@@ -235,10 +212,6 @@ module Rails
"#{root}/config/environments/#{RAILS_ENV}.rb"
end
- def reload_plugins?
- @reload_plugins
- end
-
# Holds generators configuration:
#
# config.generators do |g|
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 19412c259e..85c2fd52a4 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -86,28 +86,16 @@ module Rails
@options ||= DEFAULT_OPTIONS.dup
end
- # We have two scenarios here: when rubygems is loaded and when bundler is
- # being used. If rubygems is loaded, we get all generators paths from loaded
- # specs. Otherwise we just have to look into vendor/gems/gems.
- #
- def self.gems_generators_paths
- paths = []
-
- if defined?(Gem) && Gem.respond_to?(:loaded_specs)
- Gem.loaded_specs.each do |name, spec|
- generator_path = File.join(spec.full_gem_path, "lib/generators")
- paths << generator_path if File.exist?(generator_path)
- end
+ def self.gems_generators_paths #:nodoc:
+ return [] unless defined?(Gem) && Gem.respond_to?(:loaded_specs)
+ Gem.loaded_specs.inject([]) do |paths, (name, spec)|
+ paths += Dir[File.join(spec.full_gem_path, "lib/{generators,rails_generators}")]
end
-
- paths
end
- # Load paths from plugin.
- #
- def self.plugins_generators_paths
+ def self.plugins_generators_paths #:nodoc:
return [] unless Rails.root
- Dir[File.join(Rails.root, "vendor", "plugins", "*", "lib", "generators")]
+ Dir[File.join(Rails.root, "vendor", "plugins", "*", "lib", "{generators,rails_generators}")]
end
# Hold configured generators fallbacks. If a plugin developer wants a
@@ -147,8 +135,8 @@ module Rails
def self.load_paths
@load_paths ||= begin
paths = []
- paths << File.join(Rails.root, "lib", "generators") if Rails.root
- paths << File.join(Thor::Util.user_home, ".rails", "generators")
+ paths += Dir[File.join(Rails.root, "lib", "{generators,rails_generators}")] if Rails.root
+ paths += Dir[File.join(Thor::Util.user_home, ".rails", "{generators,rails_generators}")]
paths += self.plugins_generators_paths
paths += self.gems_generators_paths
paths << File.expand_path(File.join(File.dirname(__FILE__), "generators"))
@@ -210,7 +198,7 @@ module Rails
return klass if klass
end
- invoke_fallbacks_for(name, base)
+ invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
end
# Receives a namespace, arguments and the behavior to invoke the generator.
@@ -278,13 +266,13 @@ module Rails
# By default, Rails strips the generator namespace to make invocations
# easier. This method generaters the both possibilities names.
- def self.generator_names(first, second)
+ def self.generator_names(first, second) #:nodoc:
[ "#{first}:generators:#{second}", "#{first}:#{second}" ]
end
# Try callbacks for the given base.
#
- def self.invoke_fallbacks_for(name, base)
+ def self.invoke_fallbacks_for(name, base) #:nodoc:
return nil unless base && fallbacks[base.to_sym]
invoked_fallbacks = []
diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb
index d78eab9a75..3866b856b2 100644
--- a/railties/lib/rails/initializable.rb
+++ b/railties/lib/rails/initializable.rb
@@ -42,7 +42,8 @@ module Rails
if initializer.before
index = index_for(initializer.before)
elsif initializer.after
- index = index_for(initializer.after) + 1
+ index = index_for(initializer.after)
+ index += 1 if index
else
index = length
end
diff --git a/railties/lib/rails/initializer.rb b/railties/lib/rails/initializer.rb
index 2ad1e52746..44d04688c8 100644
--- a/railties/lib/rails/initializer.rb
+++ b/railties/lib/rails/initializer.rb
@@ -1,14 +1,4 @@
-require "pathname"
-
-require 'rails/initializable'
-require 'rails/application'
-require 'rails/railties_path'
-require 'rails/version'
-require 'rails/rack'
-require 'rails/paths'
-require 'rails/core'
-require 'rails/configuration'
-require 'rails/deprecation'
+require "rails" # In case people require this file directly
RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV)
@@ -24,4 +14,4 @@ module Rails
end
end
end
-end
+end \ No newline at end of file
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 0f24106353..b3d105d8c7 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -64,7 +64,7 @@ module Rails
end
class Path
- include PathParent
+ include PathParent, Enumerable
attr_reader :path
attr_accessor :glob
@@ -83,6 +83,10 @@ module Rails
@root.all_paths << self
end
+ def each
+ to_a.each { |p| yield p }
+ end
+
def push(path)
@paths.push path
end
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index 090ec6e4cb..86bf032641 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -1,183 +1,64 @@
module Rails
- # The Plugin class should be an object which provides the following methods:
- #
- # * +name+ - Used during initialisation to order the plugin (based on name and
- # the contents of <tt>config.plugins</tt>).
- # * +valid?+ - Returns true if this plugin can be loaded.
- # * +load_paths+ - Each path within the returned array will be added to the <tt>$LOAD_PATH</tt>.
- # * +load+ - Finally 'load' the plugin.
- #
- # These methods are expected by the Rails::Plugin::Locator and Rails::Plugin::Loader classes.
- # The default implementation returns the <tt>lib</tt> directory as its <tt>load_paths</tt>,
- # and evaluates <tt>init.rb</tt> when <tt>load</tt> is called.
- #
- # You can also inspect the about.yml data programmatically:
- #
- # plugin = Rails::Plugin.new(path_to_my_plugin)
- # plugin.about["author"] # => "James Adam"
- # plugin.about["url"] # => "http://interblah.net"
class Plugin
- include Comparable
include Initializable
-
- attr_reader :directory, :name
-
- def initialize(directory)
- @directory = directory
- @name = File.basename(@directory) rescue nil
- @loaded = false
- end
-
- def valid?
- File.directory?(directory) && (has_app_directory? || has_lib_directory? || has_init_file?)
- end
-
- # Returns a list of paths this plugin wishes to make available in <tt>$LOAD_PATH</tt>.
- def load_paths
- report_nonexistant_or_empty_plugin! unless valid?
-
- load_paths = []
- load_paths << lib_path if has_lib_directory?
- load_paths << app_paths if has_app_directory?
- load_paths.flatten
- end
-
- # Evaluates a plugin's init.rb file.
- def load(initializer)
- return if loaded?
- report_nonexistant_or_empty_plugin! unless valid?
- evaluate_init_rb(initializer)
- @loaded = true
- end
-
- def loaded?
- @loaded
- end
-
- def <=>(other_plugin)
- name <=> other_plugin.name
- end
-
- def about
- @about ||= load_about_information
- end
-
- # Engines are plugins with an app/ directory.
- def engine?
- has_app_directory?
- end
-
- # Returns true if the engine ships with a routing file
- def routed?
- File.exist?(routing_file)
- end
-
- # Returns true if there is any localization file in locale_path
- def localized?
- locale_files.any?
- end
-
- def view_path
- File.join(directory, 'app', 'views')
- end
-
- def controller_path
- File.join(directory, 'app', 'controllers')
- end
-
- def metal_path
- File.join(directory, 'app', 'metal')
- end
-
- def routing_file
- File.join(directory, 'config', 'routes.rb')
- end
-
- def locale_path
- File.join(directory, 'config', 'locales')
- end
-
- def locale_files
- Dir[ File.join(locale_path, '*.{rb,yml}') ]
- end
-
- private
- def load_about_information
- about_yml_path = File.join(@directory, "about.yml")
- parsed_yml = File.exist?(about_yml_path) ? YAML.load(File.read(about_yml_path)) : {}
- parsed_yml || {}
- rescue Exception
- {}
- end
- def report_nonexistant_or_empty_plugin!
- raise LoadError, "Can not find the plugin named: #{name}"
- end
-
- def app_paths
- [ File.join(directory, 'app', 'models'), File.join(directory, 'app', 'helpers'), controller_path, metal_path ]
- end
-
- def lib_path
- File.join(directory, 'lib')
- end
-
- def classic_init_path
- File.join(directory, 'init.rb')
- end
+ class Vendored < Plugin
+ def self.all(list, paths)
+ plugins = []
+ paths.each do |path|
+ Dir["#{path}/*"].each do |plugin_path|
+ plugin = new(plugin_path)
+ next unless list.include?(plugin.name) || list.include?(:all)
+ plugins << plugin
+ end
+ end
- def gem_init_path
- File.join(directory, 'rails', 'init.rb')
- end
+ plugins.sort_by do |p|
+ [list.index(p.name) || list.index(:all), p.name.to_s]
+ end
+ end
- def init_path
- File.file?(gem_init_path) ? gem_init_path : classic_init_path
- end
+ attr_reader :name, :path
- def has_app_directory?
- File.directory?(File.join(directory, 'app'))
- end
+ def initialize(path)
+ @name = File.basename(path).to_sym
+ @path = path
+ end
- def has_lib_directory?
- File.directory?(lib_path)
- end
+ def load_paths
+ Dir["#{path}/{lib}", "#{path}/app/{models,controllers,helpers}"]
+ end
- def has_init_file?
- File.file?(init_path)
- end
+ initializer :add_to_load_path, :after => :set_autoload_paths do |app|
+ load_paths.each do |path|
+ $LOAD_PATH << path
+ require "active_support/dependencies"
- def evaluate_init_rb(initializer)
- if has_init_file?
- require 'active_support/core_ext/kernel/reporting'
- silence_warnings do
- # Allow plugins to reference the current configuration object
- config = initializer.configuration
+ ActiveSupport::Dependencies.load_paths << path
- eval(IO.read(init_path), binding, init_path)
+ unless app.config.reload_plugins
+ ActiveSupport::Dependencies.load_once_paths << path
+ end
end
end
- end
- class Vendored < Plugin
- initializer :init_rb do |application|
- evaluate_init_rb(application)
+ initializer :load_init_rb, :before => :load_application_initializers do |app|
+ file = "#{@path}/init.rb"
+ config = app.config
+ eval File.read(file), binding, file if File.file?(file)
end
- end
- end
- # This Plugin subclass represents a Gem plugin. Although RubyGems has already
- # taken care of $LOAD_PATHs, it exposes its load_paths to add them
- # to Dependencies.load_paths.
- class GemPlugin < Plugin
- # Initialize this plugin from a Gem::Specification.
- def initialize(spec, gem)
- directory = spec.full_gem_path
- super(directory)
- @name = spec.name
- end
+ initializer :add_view_paths, :after => :initialize_framework_views do
+ ActionController::Base.view_paths.concat ["#{path}/app/views"] if File.directory?("#{path}/app/views")
+ end
- def init_path
- File.join(directory, 'rails', 'init.rb')
+ initializer :add_routing_file, :after => :initialize_routing do |app|
+ routing_file = "#{path}/config/routes.rb"
+ if File.exist?(routing_file)
+ app.routes.add_configuration_file(routing_file)
+ app.routes.reload!
+ end
+ end
end
end
-end
+end \ No newline at end of file
diff --git a/railties/lib/rails/plugin/loader.rb b/railties/lib/rails/plugin/loader.rb
deleted file mode 100644
index 4808c6ad57..0000000000
--- a/railties/lib/rails/plugin/loader.rb
+++ /dev/null
@@ -1,200 +0,0 @@
-require "rails/plugin"
-
-module Rails
- class Plugin
- class Loader
- attr_reader :initializer
-
- # Creates a new Plugin::Loader instance, associated with the given
- # Rails::Initializer. This default implementation automatically locates
- # all plugins, and adds all plugin load paths, when it is created. The plugins
- # are then fully loaded (init.rb is evaluated) when load_plugins is called.
- #
- # It is the loader's responsibility to ensure that only the plugins specified
- # in the configuration are actually loaded, and that the order defined
- # is respected.
- def initialize(initializer)
- @initializer = initializer
- end
-
- # Returns the plugins to be loaded, in the order they should be loaded.
- def plugins
- @plugins ||= all_plugins.select { |plugin| should_load?(plugin) }.sort { |p1, p2| order_plugins(p1, p2) }
- end
-
- # Returns the plugins that are in engine-form (have an app/ directory)
- def engines
- @engines ||= plugins.select {|plugin| plugin.engine? }
- end
-
- # Returns all the plugins that could be found by the current locators.
- def all_plugins
- @all_plugins ||= locate_plugins
- @all_plugins
- end
-
- def load_plugins
- plugins.each do |plugin|
- plugin.load(initializer)
- register_plugin_as_loaded(plugin)
- end
-
- configure_engines
-
- ensure_all_registered_plugins_are_loaded!
- end
-
- # Adds the load paths for every plugin into the $LOAD_PATH. Plugin load paths are
- # added *after* the application's <tt>lib</tt> directory, to ensure that an application
- # can always override code within a plugin.
- #
- # Plugin load paths are also added to Dependencies.load_paths, and Dependencies.load_once_paths.
- def add_plugin_load_paths
- plugins.each do |plugin|
- plugin.load_paths.each do |path|
- $LOAD_PATH.insert(application_lib_index + 1, path)
-
- ActiveSupport::Dependencies.load_paths << path
-
- unless configuration.reload_plugins?
- ActiveSupport::Dependencies.load_once_paths << path
- end
- end
- end
-
- $LOAD_PATH.uniq!
- end
-
- def engine_metal_paths
- engines.collect {|engine| engine.metal_path }
- end
-
- protected
- def configure_engines
- if engines.any?
- add_engine_routing_configurations
- add_engine_locales
- add_engine_controller_paths
- add_engine_view_paths
- end
- end
-
- def add_engine_routing_configurations
- engines.select {|engine| engine.routed? }.map {|engine| engine.routing_file }.each do |routing_file|
- ActionController::Routing::Routes.add_configuration_file(routing_file)
- end
- end
-
- def add_engine_locales
- localized_engines = engines.select { |engine| engine.localized? }
-
- # reverse it such that the last engine can overwrite translations from the first, like with routes
- locale_files = localized_engines.collect { |engine| engine.locale_files }.reverse.flatten
- I18n.load_path += locale_files - I18n.load_path
- end
-
- def add_engine_controller_paths
- ActionController::Routing.controller_paths += engines.collect {|engine| engine.controller_path }
- end
-
- def add_engine_view_paths
- # reverse it such that the last engine can overwrite view paths from the first, like with routes
- paths = ActionView::PathSet.new(engines.collect {|engine| engine.view_path }.reverse)
- ActionController::Base.view_paths.concat(paths)
- ActionMailer::Base.view_paths.concat(paths) if configuration.frameworks.include?(:action_mailer)
- end
-
- # The locate_plugins method uses each class in config.plugin_locators to
- # find the set of all plugins available to this Rails application.
- def locate_plugins
- configuration.plugin_locators.map do |locator|
- locator.new(initializer).plugins
- end.flatten
- # TODO: sorting based on config.plugins
- end
-
- def register_plugin_as_loaded(plugin)
- initializer.config.loaded_plugins << plugin
- end
-
- def configuration
- initializer.configuration
- end
-
- def should_load?(plugin)
- # uses Plugin#name and Plugin#valid?
- enabled?(plugin) && plugin.valid?
- end
-
- def order_plugins(plugin_a, plugin_b)
- if !explicit_plugin_loading_order?
- plugin_a <=> plugin_b
- else
- if !explicitly_enabled?(plugin_a) && !explicitly_enabled?(plugin_b)
- plugin_a <=> plugin_b
- else
- effective_order_of(plugin_a) <=> effective_order_of(plugin_b)
- end
- end
- end
-
- def effective_order_of(plugin)
- if explicitly_enabled?(plugin)
- registered_plugin_names.index(plugin.name)
- else
- registered_plugin_names.index('all')
- end
- end
-
- def application_lib_index
- $LOAD_PATH.index(File.join(Rails.root, 'lib')) || 0
- end
-
- def enabled?(plugin)
- !explicit_plugin_loading_order? || registered?(plugin)
- end
-
- def explicit_plugin_loading_order?
- !registered_plugin_names.nil?
- end
-
- def registered?(plugin)
- explicit_plugin_loading_order? && registered_plugins_names_plugin?(plugin)
- end
-
- def explicitly_enabled?(plugin)
- !explicit_plugin_loading_order? || explicitly_registered?(plugin)
- end
-
- def explicitly_registered?(plugin)
- explicit_plugin_loading_order? && registered_plugin_names.include?(plugin.name)
- end
-
- def registered_plugins_names_plugin?(plugin)
- registered_plugin_names.include?(plugin.name) || registered_plugin_names.include?('all')
- end
-
- # The plugins that have been explicitly listed with config.plugins. If this list is nil
- # then it means the client does not care which plugins or in what order they are loaded,
- # so we load all in alphabetical order. If it is an empty array, we load no plugins, if it is
- # non empty, we load the named plugins in the order specified.
- def registered_plugin_names
- configuration.plugins ? configuration.plugins.map {|plugin| plugin.to_s } : nil
- end
-
- def loaded?(plugin_name)
- initializer.config.loaded_plugins.detect { |plugin| plugin.name == plugin_name.to_s }
- end
-
- def ensure_all_registered_plugins_are_loaded!
- if explicit_plugin_loading_order?
- if configuration.plugins.detect {|plugin| plugin != :all && !loaded?(plugin) }
- missing_plugins = configuration.plugins - (plugins.map{|p| p.name.to_sym} + [:all])
- raise LoadError, "Could not locate the following plugins: #{missing_plugins.to_sentence(:locale => :en)}"
- end
- end
- end
-
- end
- end
-end
diff --git a/railties/lib/rails/plugin/locator.rb b/railties/lib/rails/plugin/locator.rb
deleted file mode 100644
index 56cbaf37c6..0000000000
--- a/railties/lib/rails/plugin/locator.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-module Rails
- class Plugin
-
- # The Plugin::Locator class should be subclasses to provide custom plugin-finding
- # abilities to Rails (i.e. loading plugins from Gems, etc). Each subclass should implement
- # the <tt>located_plugins</tt> method, which return an array of Plugin objects that have been found.
- class Locator
- include Enumerable
-
- attr_reader :initializer
-
- def initialize(initializer)
- @initializer = initializer
- end
-
- # This method should return all the plugins which this Plugin::Locator can find
- # These will then be used by the current Plugin::Loader, which is responsible for actually
- # loading the plugins themselves
- def plugins
- raise "The `plugins' method must be defined by concrete subclasses of #{self.class}"
- end
-
- def each(&block)
- plugins.each(&block)
- end
-
- def plugin_names
- plugins.map {|plugin| plugin.name }
- end
- end
-
- # The Rails::Plugin::FileSystemLocator will try to locate plugins by examining the directories
- # in the paths given in configuration.plugin_paths. Any plugins that can be found are returned
- # in a list.
- #
- # The criteria for a valid plugin in this case is found in Rails::Plugin#valid?, although
- # other subclasses of Rails::Plugin::Locator can of course use different conditions.
- class FileSystemLocator < Locator
-
- # Returns all the plugins which can be loaded in the filesystem, under the paths given
- # by configuration.plugin_paths.
- def plugins
- initializer.configuration.plugin_paths.flatten.inject([]) do |plugins, path|
- plugins.concat locate_plugins_under(path)
- plugins
- end.flatten
- end
-
- private
-
- # Attempts to create a plugin from the given path. If the created plugin is valid?
- # (see Rails::Plugin#valid?) then the plugin instance is returned; otherwise nil.
- def create_plugin(path)
- plugin = Rails::Plugin.new(path)
- plugin.valid? ? plugin : nil
- end
-
- # This starts at the base path looking for valid plugins (see Rails::Plugin#valid?).
- # Since plugins can be nested arbitrarily deep within an unspecified number of intermediary
- # directories, this method runs recursively until it finds a plugin directory, e.g.
- #
- # locate_plugins_under('vendor/plugins/acts/acts_as_chunky_bacon')
- # => <Rails::Plugin name: 'acts_as_chunky_bacon' ... >
- #
- def locate_plugins_under(base_path)
- Dir.glob(File.join(base_path, '*')).sort.inject([]) do |plugins, path|
- if plugin = create_plugin(path)
- plugins << plugin
- elsif File.directory?(path)
- plugins.concat locate_plugins_under(path)
- end
- plugins
- end
- end
- end
-
- # The GemLocator scans all the loaded RubyGems, looking for gems with
- # a <tt>rails/init.rb</tt> file.
- class GemLocator < Locator
- def plugins
- gem_index = {}
- specs = gem_index.keys
- specs += Gem.loaded_specs.values.select do |spec|
- spec.loaded_from && # prune stubs
- File.exist?(File.join(spec.full_gem_path, "rails", "init.rb"))
- end
- specs.compact!
-
- require "rubygems/dependency_list"
-
- deps = Gem::DependencyList.new
- deps.add(*specs) unless specs.empty?
-
- deps.dependency_order.collect do |spec|
- Rails::GemPlugin.new(spec, gem_index[spec])
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/railties/lib/rails/rack/debugger.rb b/railties/lib/rails/rack/debugger.rb
index aa2711c616..0a7b405553 100644
--- a/railties/lib/rails/rack/debugger.rb
+++ b/railties/lib/rails/rack/debugger.rb
@@ -4,6 +4,8 @@ module Rails
def initialize(app)
@app = app
+ ARGV.clear # clear ARGV so that script/server options aren't passed to IRB
+
require_library_or_gem 'ruby-debug'
::Debugger.start
::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings)
diff --git a/railties/lib/rails/rack/static.rb b/railties/lib/rails/rack/static.rb
index d6b8face27..ebe8b9e103 100644
--- a/railties/lib/rails/rack/static.rb
+++ b/railties/lib/rails/rack/static.rb
@@ -1,5 +1,5 @@
require 'action_dispatch'
module Rails::Rack
- Static = Deprecation::DeprecatedConstantProxy.new('Rails::Rack::Static', ActionDispatch::Static)
+ Static = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Rails::Rack::Static', ActionDispatch::Static)
end
diff --git a/railties/test/application/plugins_test.rb b/railties/test/application/plugins_test.rb
deleted file mode 100644
index 80a19856d7..0000000000
--- a/railties/test/application/plugins_test.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-require "isolation/abstract_unit"
-
-module ApplicationTests
- class PluginTest < Test::Unit::TestCase
- include ActiveSupport::Testing::Isolation
-
- def assert_plugins(list_of_names, array_of_plugins, message=nil)
- assert_equal list_of_names.map { |n| n.to_s }, array_of_plugins.map { |p| p.name }, message
- end
-
- def setup
- build_app
- boot_rails
- require "rails"
- @failure_tip = "It's likely someone has added a new plugin fixture without updating this list"
- # Tmp hax to get tests working
- FileUtils.cp_r "#{File.dirname(__FILE__)}/../fixtures/plugins", "#{app_path}/vendor"
- end
-
- test "all plugins are loaded when registered plugin list is untouched" do
- Rails::Initializer.run { |c| c.root = app_path }
- Rails.initialize!
- assert_plugins [
- :a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby
- ], Rails.application.config.loaded_plugins, @failure_tip
- end
-
- test "no plugins are loaded if the configuration has an empty plugin list" do
- Rails::Initializer.run { |c| c.root = app_path; c.plugins = [] }
- assert_plugins [], Rails.application.config.loaded_plugins
- end
-
- test "only the specified plugins are located in the order listed" do
- plugin_names = [:plugin_with_no_lib_dir, :acts_as_chunky_bacon]
- Rails::Initializer.run { |c| c.root = app_path; c.plugins = plugin_names }
- Rails.initialize!
- assert_plugins plugin_names, Rails.application.config.loaded_plugins
- end
-
- test "all plugins loaded after all" do
- Rails::Initializer.run do |config|
- config.root = app_path
- config.plugins = [:stubby, :all, :acts_as_chunky_bacon]
- end
- Rails.initialize!
- assert_plugins [:stubby, :a, :engine, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], Rails.application.config.loaded_plugins, @failure_tip
- end
-
- test "plugin names may be strings" do
- plugin_names = ['stubby', 'acts_as_chunky_bacon', :a, :plugin_with_no_lib_dir]
- Rails::Initializer.run do |config|
- config.root = app_path
- config.plugins = ['stubby', 'acts_as_chunky_bacon', :a, :plugin_with_no_lib_dir]
- end
- Rails.initialize!
-
- assert_plugins plugin_names, Rails.application.config.loaded_plugins, @failure_tip
- end
-
- test "all plugins loaded when all is used" do
- Rails::Initializer.run do |config|
- config.root = app_path
- config.plugins = [:stubby, :acts_as_chunky_bacon, :all]
- end
- Rails.initialize!
-
- assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :engine, :gemlike, :plugin_with_no_lib_dir], Rails.application.config.loaded_plugins, @failure_tip
- end
-
- test "all loaded plugins are added to the load paths" do
- Rails::Initializer.run do |config|
- config.root = app_path
- config.plugins = [:stubby, :acts_as_chunky_bacon]
- end
- Rails.initialize!
-
- assert $LOAD_PATH.include?("#{app_path}/vendor/plugins/default/stubby/lib")
- assert $LOAD_PATH.include?("#{app_path}/vendor/plugins/default/acts/acts_as_chunky_bacon/lib")
- end
-
- test "registering a plugin name that does not exist raises a load error" do
- Rails::Initializer.run do |config|
- config.root = app_path
- config.plugins = [:stubby, :acts_as_a_non_existant_plugin]
- end
-
- assert_raise(LoadError) do
- Rails.initialize!
- end
- end
-
- test "load error messages mention missing plugins and no others" do
- valid_plugins = [:stubby, :acts_as_chunky_bacon]
- invalid_plugins = [:non_existant_plugin1, :non_existant_plugin2]
-
- begin
- Rails::Initializer.run do |config|
- config.root = app_path
- config.plugins = [:stubby, :acts_as_chunky_bacon, :non_existant_plugin1, :non_existant_plugin2]
- end
- Rails.initialize!
- flunk "Expected a LoadError but did not get one"
- rescue LoadError => e
- assert_plugins valid_plugins, Rails.application.config.loaded_plugins, @failure_tip
-
- invalid_plugins.each do |plugin|
- assert_match(/#{plugin.to_s}/, e.message, "LoadError message should mention plugin '#{plugin}'")
- end
-
- valid_plugins.each do |plugin|
- assert_no_match(/#{plugin.to_s}/, e.message, "LoadError message should not mention '#{plugin}'")
- end
- end
- end
-
- end
-end \ No newline at end of file
diff --git a/railties/test/fixtures/vendor/plugins/mspec/lib/generators/mspec_generator.rb b/railties/test/fixtures/vendor/plugins/mspec/lib/rails_generators/mspec_generator.rb
index 191bdbf2fc..191bdbf2fc 100644
--- a/railties/test/fixtures/vendor/plugins/mspec/lib/generators/mspec_generator.rb
+++ b/railties/test/fixtures/vendor/plugins/mspec/lib/rails_generators/mspec_generator.rb
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index a8716d9992..4b7b80c7f5 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -150,6 +150,13 @@ class GeneratorsTest < GeneratorsTestCase
assert_equal "test_unit:generators:plugin", klass.namespace
end
+ def test_fallbacks_for_generators_on_find_by_namespace_with_context
+ Rails::Generators.fallbacks[:remarkable] = :test_unit
+ klass = Rails::Generators.find_by_namespace(:remarkable, :rails, :plugin)
+ assert klass
+ assert_equal "test_unit:generators:plugin", klass.namespace
+ end
+
def test_fallbacks_for_generators_on_invoke
Rails::Generators.fallbacks[:shoulda] = :test_unit
TestUnit::Generators::ModelGenerator.expects(:start).with(["Account"], {})
diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb
index 2920883132..a9e60680ac 100644
--- a/railties/test/initializable_test.rb
+++ b/railties/test/initializable_test.rb
@@ -94,6 +94,10 @@ module InitializableTests
include Rails::Initializable
initializer :startup, :before => :last do
+ $arr << 3
+ end
+
+ initializer :terminate, :after => :first do
$arr << two
end
@@ -109,7 +113,7 @@ module InitializableTests
end
initializer :last do
- $arr << 3
+ $arr << 4
end
def self.initializers
@@ -189,7 +193,7 @@ module InitializableTests
test "merges in the initializers from the parent in the right order" do
$arr = []
OverriddenInitializer.new.run_initializers
- assert_equal [1, 2, 3], $arr
+ assert_equal [1, 2, 3, 4], $arr
end
end
end \ No newline at end of file
diff --git a/railties/test/initializer/initialize_i18n_test.rb b/railties/test/initializer/initialize_i18n_test.rb
index d664f96ad1..076816d73b 100644
--- a/railties/test/initializer/initialize_i18n_test.rb
+++ b/railties/test/initializer/initialize_i18n_test.rb
@@ -30,27 +30,27 @@ module InitializerTests
end
test "i18n finds locale files in engines" do
- app_file "vendor/plugins/engine/init.rb", ""
- app_file "vendor/plugins/engine/app/models/hellos.rb", "class Hello ; end"
- app_file "vendor/plugins/engine/lib/omg.rb", "puts 'omg'"
- app_file "vendor/plugins/engine/config/locales/en.yml", "hello:"
-
- Rails::Initializer.run do |c|
- c.root = app_path
- c.i18n.load_path << "my/other/locale.yml"
- end
- Rails.initialize!
-
- #{RAILS_FRAMEWORK_ROOT}/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml
- assert_equal %W(
- #{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/locale/en.yml
- #{RAILS_FRAMEWORK_ROOT}/activemodel/lib/active_model/locale/en.yml
- #{RAILS_FRAMEWORK_ROOT}/activerecord/lib/active_record/locale/en.yml
- #{RAILS_FRAMEWORK_ROOT}/actionpack/lib/action_view/locale/en.yml
- #{app_path}/config/locales/en.yml
- my/other/locale.yml
- #{app_path}/vendor/plugins/engine/config/locales/en.yml
- ).map { |path| File.expand_path(path) }, I18n.load_path.map { |path| File.expand_path(path) }
+ # app_file "vendor/plugins/engine/init.rb", ""
+ # app_file "vendor/plugins/engine/app/models/hellos.rb", "class Hello ; end"
+ # app_file "vendor/plugins/engine/lib/omg.rb", "puts 'omg'"
+ # app_file "vendor/plugins/engine/config/locales/en.yml", "hello:"
+ #
+ # Rails::Initializer.run do |c|
+ # c.root = app_path
+ # c.i18n.load_path << "my/other/locale.yml"
+ # end
+ # Rails.initialize!
+ #
+ # #{RAILS_FRAMEWORK_ROOT}/railties/test/fixtures/plugins/engines/engine/config/locales/en.yml
+ # assert_equal %W(
+ # #{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/locale/en.yml
+ # #{RAILS_FRAMEWORK_ROOT}/activemodel/lib/active_model/locale/en.yml
+ # #{RAILS_FRAMEWORK_ROOT}/activerecord/lib/active_record/locale/en.yml
+ # #{RAILS_FRAMEWORK_ROOT}/actionpack/lib/action_view/locale/en.yml
+ # #{app_path}/config/locales/en.yml
+ # my/other/locale.yml
+ # #{app_path}/vendor/plugins/engine/config/locales/en.yml
+ # ).map { |path| File.expand_path(path) }, I18n.load_path.map { |path| File.expand_path(path) }
end
end
end \ No newline at end of file
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 0b479e944c..ba8b35d5cc 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -92,6 +92,34 @@ module TestHelpers
add_to_config 'config.action_controller.session = { :key => "_myapp_session", :secret => "bac838a849c1d5c4de2e6a50af826079" }'
end
+ class Bukkit
+ def initialize(path)
+ @path = path
+ end
+
+ def write(file, string)
+ path = "#{@path}/#{file}"
+ FileUtils.mkdir_p(File.dirname(path))
+ File.open(path, "w") {|f| f.puts string }
+ end
+
+ def delete(file)
+ File.delete("#{@path}/#{file}")
+ end
+ end
+
+ def plugin(name, string = "")
+ dir = "#{app_path}/vendor/plugins/#{name}"
+ FileUtils.mkdir_p(dir)
+ File.open("#{dir}/init.rb", 'w') do |f|
+ f.puts "::#{name.upcase} = 'loaded'"
+ f.puts string
+ end
+ Bukkit.new(dir).tap do |bukkit|
+ yield bukkit if block_given?
+ end
+ end
+
def script(script)
Dir.chdir(app_path) do
`#{Gem.ruby} #{app_path}/script/#{script}`
@@ -157,7 +185,7 @@ Module.new do
FileUtils.mkdir(tmp_path)
environment = File.expand_path('../../../../vendor/gems/environment', __FILE__)
- if File.exist?(environment)
+ if File.exist?("#{environment}.rb")
require_environment = "-r #{environment}"
end
diff --git a/railties/test/plugin_loader_test.rb b/railties/test/plugin_loader_test.rb
deleted file mode 100644
index 0b43c49bb2..0000000000
--- a/railties/test/plugin_loader_test.rb
+++ /dev/null
@@ -1,176 +0,0 @@
-require 'plugin_test_helper'
-
-$:.unshift File.dirname(__FILE__) + "/../../actionpack/lib"
-$:.unshift File.dirname(__FILE__) + "/../../actionmailer/lib"
-require 'action_controller'
-require 'action_mailer'
-
-# TODO: Rewrite all these tests
-class FakeInitializerSlashApplication
- attr_reader :config
- alias configuration config
-
- def initialize
- @config = Rails::Configuration.new
- end
-end
-
-class TestPluginLoader < Test::Unit::TestCase
- ORIGINAL_LOAD_PATH = $LOAD_PATH.dup
-
- def setup
- reset_load_path!
-
- @initializer = FakeInitializerSlashApplication.new
- @configuration = @initializer.config
- Rails.application = @initializer
- @configuration.plugin_paths << plugin_fixture_root_path
- @valid_plugin_path = plugin_fixture_path('default/stubby')
- @empty_plugin_path = plugin_fixture_path('default/empty')
-
- @failure_tip = "It's likely someone has added a new plugin fixture without updating this list"
-
- @loader = Rails::Plugin::Loader.new(@initializer)
- end
-
- def test_should_locate_plugins_by_asking_each_locator_specifed_in_configuration_for_its_plugins_result
- locator_1 = stub(:plugins => [:a, :b, :c])
- locator_2 = stub(:plugins => [:d, :e, :f])
- locator_class_1 = stub(:new => locator_1)
- locator_class_2 = stub(:new => locator_2)
- @configuration.plugin_locators = [locator_class_1, locator_class_2]
- assert_equal [:a, :b, :c, :d, :e, :f], @loader.send(:locate_plugins)
- end
-
- def test_should_memoize_the_result_of_locate_plugins_as_all_plugins
- plugin_list = [:a, :b, :c]
- @loader.expects(:locate_plugins).once.returns(plugin_list)
- assert_equal plugin_list, @loader.all_plugins
- assert_equal plugin_list, @loader.all_plugins # ensuring that locate_plugins isn't called again
- end
-
- def test_should_return_empty_array_if_configuration_plugins_is_empty
- @configuration.plugins = []
- assert_equal [], @loader.plugins
- end
-
- def test_should_find_all_availble_plugins_and_return_as_all_plugins
- assert_plugins [ :engine, :stubby, :plugin_with_no_lib_dir, :gemlike, :acts_as_chunky_bacon, :a], @loader.all_plugins.reverse, @failure_tip
- end
-
- def test_should_return_all_plugins_as_plugins_when_registered_plugin_list_is_untouched
- assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
- end
-
- def test_should_return_all_plugins_as_plugins_when_registered_plugin_list_is_nil
- @configuration.plugins = nil
- assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
- end
-
- def test_should_return_specific_plugins_named_in_config_plugins_array_if_set
- plugin_names = [:acts_as_chunky_bacon, :stubby]
- only_load_the_following_plugins! plugin_names
- assert_plugins plugin_names, @loader.plugins
- end
-
- def test_should_respect_the_order_of_plugins_given_in_configuration
- plugin_names = [:stubby, :acts_as_chunky_bacon]
- only_load_the_following_plugins! plugin_names
- assert_plugins plugin_names, @loader.plugins
- end
-
- def test_should_load_all_plugins_in_natural_order_when_all_is_used
- only_load_the_following_plugins! [:all]
- assert_plugins [:a, :acts_as_chunky_bacon, :engine, :gemlike, :plugin_with_no_lib_dir, :stubby], @loader.plugins, @failure_tip
- end
-
- def test_should_load_specified_plugins_in_order_and_then_all_remaining_plugins_when_all_is_used
- only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon, :all]
- assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :engine, :gemlike, :plugin_with_no_lib_dir], @loader.plugins, @failure_tip
- end
-
- def test_should_be_able_to_specify_loading_of_plugins_loaded_after_all
- only_load_the_following_plugins! [:stubby, :all, :acts_as_chunky_bacon]
- assert_plugins [:stubby, :a, :engine, :gemlike, :plugin_with_no_lib_dir, :acts_as_chunky_bacon], @loader.plugins, @failure_tip
- end
-
- def test_should_accept_plugin_names_given_as_strings
- only_load_the_following_plugins! ['stubby', 'acts_as_chunky_bacon', :a, :plugin_with_no_lib_dir]
- assert_plugins [:stubby, :acts_as_chunky_bacon, :a, :plugin_with_no_lib_dir], @loader.plugins, @failure_tip
- end
-
- def test_should_add_plugin_load_paths_to_global_LOAD_PATH_array
- only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon]
- stubbed_application_lib_index_in_LOAD_PATHS = 4
- @loader.stubs(:application_lib_index).returns(stubbed_application_lib_index_in_LOAD_PATHS)
-
- @loader.add_plugin_load_paths
-
- assert $LOAD_PATH.index(File.join(plugin_fixture_path('default/stubby'), 'lib')) >= stubbed_application_lib_index_in_LOAD_PATHS
- assert $LOAD_PATH.index(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) >= stubbed_application_lib_index_in_LOAD_PATHS
- end
-
- def test_should_add_plugin_load_paths_to_Dependencies_load_paths
- only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon]
-
- @loader.add_plugin_load_paths
-
- assert ActiveSupport::Dependencies.load_paths.include?(File.join(plugin_fixture_path('default/stubby'), 'lib'))
- assert ActiveSupport::Dependencies.load_paths.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib'))
- end
-
- def test_should_add_engine_load_paths_to_Dependencies_load_paths
- only_load_the_following_plugins! [:engine]
-
- @loader.add_plugin_load_paths
-
- %w( models controllers metal helpers ).each do |app_part|
- assert ActiveSupport::Dependencies.load_paths.include?(
- File.join(plugin_fixture_path('engines/engine'), 'app', app_part)
- ), "Couldn't find #{app_part} in load path"
- end
- end
-
- def test_engine_controllers_and_action_mailers_should_have_their_view_path_set_when_loaded
- only_load_the_following_plugins!([ :engine ])
-
- @loader.send :add_engine_view_paths
-
- assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionController::Base.view_paths.map { |p| p.to_s }
- assert_equal [ File.join(plugin_fixture_path('engines/engine'), 'app', 'views') ], ActionMailer::Base.view_paths.map { |p| p.to_s }
- end
-
- def test_should_add_plugin_load_paths_to_Dependencies_load_once_paths
- only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon]
-
- @loader.add_plugin_load_paths
-
- assert ActiveSupport::Dependencies.load_once_paths.include?(File.join(plugin_fixture_path('default/stubby'), 'lib'))
- assert ActiveSupport::Dependencies.load_once_paths.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib'))
- end
-
- def test_should_add_all_load_paths_from_a_plugin_to_LOAD_PATH_array
- plugin_load_paths = ["a", "b"]
- plugin = stub(:load_paths => plugin_load_paths)
- @loader.stubs(:plugins).returns([plugin])
-
- @loader.add_plugin_load_paths
-
- plugin_load_paths.each { |path| assert $LOAD_PATH.include?(path) }
- end
-
- def test_should_add_locale_files_to_I18n_load_path
- only_load_the_following_plugins! [:engine]
-
- @loader.send :add_engine_locales
-
- assert I18n.load_path.include?(File.join(plugin_fixture_path('engines/engine'), 'config', 'locales', 'en.yml'))
- end
-
-
- private
- def reset_load_path!
- $LOAD_PATH.clear
- ORIGINAL_LOAD_PATH.each { |path| $LOAD_PATH << path }
- end
-end
diff --git a/railties/test/plugin_locator_test.rb b/railties/test/plugin_locator_test.rb
deleted file mode 100644
index ef57e7ed4c..0000000000
--- a/railties/test/plugin_locator_test.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-require 'plugin_test_helper'
-
-# TODO: Rewrite all these tests
-class FakeInitializerSlashApplication
- attr_reader :config
- alias configuration config
-
- def initialize
- @config = Rails::Configuration.new
- end
-end
-
-class PluginLocatorTest < Test::Unit::TestCase
- def test_should_require_subclasses_to_implement_the_plugins_method
- assert_raise(RuntimeError) do
- Rails::Plugin::Locator.new(nil).plugins
- end
- end
-
- def test_should_iterator_over_plugins_returned_by_plugins_when_calling_each
- locator = Rails::Plugin::Locator.new(nil)
- locator.stubs(:plugins).returns([:a, :b, :c])
- plugin_consumer = mock
- plugin_consumer.expects(:consume).with(:a)
- plugin_consumer.expects(:consume).with(:b)
- plugin_consumer.expects(:consume).with(:c)
-
- locator.each do |plugin|
- plugin_consumer.consume(plugin)
- end
- end
-end
-
-class PluginFileSystemLocatorTest < Test::Unit::TestCase
- def setup
- @initializer = FakeInitializerSlashApplication.new
- @configuration = @initializer.config
- Rails.application = @initializer
- # We need to add our testing plugin directory to the plugin paths so
- # the locator knows where to look for our plugins
- @configuration.plugin_paths << plugin_fixture_root_path
- @locator = Rails::Plugin::FileSystemLocator.new(@initializer)
- @valid_plugin_path = plugin_fixture_path('default/stubby')
- @empty_plugin_path = plugin_fixture_path('default/empty')
- end
-
- def test_should_return_rails_plugin_instances_when_calling_create_plugin_with_a_valid_plugin_directory
- assert_kind_of Rails::Plugin, @locator.send(:create_plugin, @valid_plugin_path)
- end
-
- def test_should_return_nil_when_calling_create_plugin_with_an_invalid_plugin_directory
- assert_nil @locator.send(:create_plugin, @empty_plugin_path)
- end
-
- def test_should_return_all_plugins_found_under_the_set_plugin_paths
- assert_equal ["a", "acts_as_chunky_bacon", "engine", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map { |p| p.name }.sort
- end
-
- def test_should_find_plugins_only_under_the_plugin_paths_set_in_configuration
- @configuration.plugin_paths = [File.join(plugin_fixture_root_path, "default")]
- assert_equal ["acts_as_chunky_bacon", "gemlike", "plugin_with_no_lib_dir", "stubby"].sort, @locator.plugins.map { |p| p.name }.sort
-
- @configuration.plugin_paths = [File.join(plugin_fixture_root_path, "alternate")]
- assert_equal ["a"], @locator.plugins.map { |p| p.name }
- end
-
- def test_should_not_raise_any_error_and_return_no_plugins_if_the_plugin_path_value_does_not_exist
- @configuration.plugin_paths = ["some_missing_directory"]
- assert_nothing_raised do
- assert @locator.plugins.empty?
- end
- end
-end
diff --git a/railties/test/plugin_test.rb b/railties/test/plugin_test.rb
deleted file mode 100644
index 199adcfe39..0000000000
--- a/railties/test/plugin_test.rb
+++ /dev/null
@@ -1,174 +0,0 @@
-require 'plugin_test_helper'
-
-# TODO: Rewrite all these tests
-class FakeInitializerSlashApplication
- attr_reader :config
- alias configuration config
-
- def initialize
- @config = Rails::Configuration.new
- end
-end
-
-class PluginTest < Test::Unit::TestCase
- def setup
- @initializer = FakeInitializerSlashApplication.new
- @configuration = @initializer.config
- Rails.application = @initializer
- @valid_plugin_path = plugin_fixture_path('default/stubby')
- @empty_plugin_path = plugin_fixture_path('default/empty')
- @gemlike_plugin_path = plugin_fixture_path('default/gemlike')
- end
-
- def test_should_determine_plugin_name_from_the_directory_of_the_plugin
- assert_equal 'stubby', plugin_for(@valid_plugin_path).name
- assert_equal 'empty', plugin_for(@empty_plugin_path).name
- end
-
- def test_should_not_be_loaded_when_created
- assert !plugin_for(@valid_plugin_path).loaded?
- end
-
- def test_should_be_marked_as_loaded_when_load_is_called
- plugin = plugin_for(@valid_plugin_path)
- assert !plugin.loaded?
- plugin.stubs(:evaluate_init_rb)
- assert_nothing_raised do
- plugin.send(:load, anything)
- end
- assert plugin.loaded?
- end
-
- def test_should_determine_validity_of_given_path
- # This is a plugin path, with a lib dir
- assert plugin_for(@valid_plugin_path).valid?
- # This just has an init.rb and no lib dir
- assert plugin_for(plugin_fixture_path('default/plugin_with_no_lib_dir')).valid?
- # This would be a plugin path, but the directory is empty
- assert !plugin_for(plugin_fixture_path('default/empty')).valid?
- # This is a non sense path
- assert !plugin_for(plugin_fixture_path('default/this_directory_does_not_exist')).valid?
- end
-
- def test_should_return_empty_array_for_load_paths_when_plugin_has_no_lib_directory
- assert_equal [], plugin_for(plugin_fixture_path('default/plugin_with_no_lib_dir')).load_paths
- end
-
- def test_should_return_array_with_lib_path_for_load_paths_when_plugin_has_a_lib_directory
- expected_lib_dir = File.join(plugin_fixture_path('default/stubby'), 'lib')
- assert_equal [expected_lib_dir], plugin_for(plugin_fixture_path('default/stubby')).load_paths
- end
-
- def test_should_raise_a_load_error_when_trying_to_determine_the_load_paths_from_an_invalid_plugin
- assert_nothing_raised do
- plugin_for(@valid_plugin_path).load_paths
- end
-
- assert_raise(LoadError) do
- plugin_for(@empty_plugin_path).load_paths
- end
-
- assert_raise(LoadError) do
- plugin_for('this_is_not_a_plugin_directory').load_paths
- end
- end
-
- def test_should_raise_a_load_error_when_trying_to_load_an_invalid_plugin
- # This path is fine so nothing is raised
- assert_nothing_raised do
- plugin = plugin_for(@valid_plugin_path)
- plugin.stubs(:evaluate_init_rb)
- plugin.send(:load, @initializer)
- end
-
- # This path is fine so nothing is raised
- assert_nothing_raised do
- plugin = plugin_for(@gemlike_plugin_path)
- plugin.stubs(:evaluate_init_rb)
- plugin.send(:load, @initializer)
- end
-
- # This is an empty path so it raises
- assert_raise(LoadError) do
- plugin = plugin_for(@empty_plugin_path)
- plugin.stubs(:evaluate_init_rb)
- plugin.send(:load, @initializer)
- end
-
- assert_raise(LoadError) do
- plugin = plugin_for('this_is_not_a_plugin_directory')
- plugin.stubs(:evaluate_init_rb)
- plugin.send(:load, @initializer)
- end
- end
-
- def test_should_raise_a_load_error_when_trying_to_access_load_paths_of_an_invalid_plugin
- # This path is fine so nothing is raised
- assert_nothing_raised do
- plugin_for(@valid_plugin_path).load_paths
- end
-
- # This is an empty path so it raises
- assert_raise(LoadError) do
- plugin_for(@empty_plugin_path).load_paths
- end
-
- assert_raise(LoadError) do
- plugin_for('this_is_not_a_plugin_directory').load_paths
- end
- end
-
- def test_loading_a_plugin_gives_the_init_file_access_to_all_it_needs
- failure_tip = "Perhaps someone has written another test that loads this same plugin and therefore makes the StubbyMixin constant defined already."
- assert !defined?(StubbyMixin), failure_tip
- plugin = plugin_for(@valid_plugin_path)
- plugin.load_paths.each { |path| $LOAD_PATH.unshift(path) }
- # The init.rb of this plugin raises if it doesn't have access to all the things it needs
- assert_nothing_raised do
- plugin.load(@initializer)
- end
- assert defined?(StubbyMixin)
- end
-
- def test_should_sort_naturally_by_name
- a = plugin_for("path/a")
- b = plugin_for("path/b")
- z = plugin_for("path/z")
- assert_equal [a, b, z], [b, z, a].sort
- end
-
- def test_should_only_be_loaded_once
- plugin = plugin_for(@valid_plugin_path)
- assert !plugin.loaded?
- plugin.expects(:evaluate_init_rb)
- assert_nothing_raised do
- plugin.send(:load, @initializer)
- plugin.send(:load, @initializer)
- end
- assert plugin.loaded?
- end
-
- def test_should_make_about_yml_available_as_about_method_on_plugin
- plugin = plugin_for(@valid_plugin_path)
- assert_equal "Plugin Author", plugin.about['author']
- assert_equal "1.0.0", plugin.about['version']
- end
-
- def test_should_return_empty_hash_for_about_if_about_yml_is_missing
- assert_equal({}, plugin_for(about_yml_plugin_path('plugin_without_about_yaml')).about)
- end
-
- def test_should_return_empty_hash_for_about_if_about_yml_is_malformed
- assert_equal({}, plugin_for(about_yml_plugin_path('bad_about_yml')).about)
- end
-
- private
-
- def about_yml_plugin_path(name)
- File.join(File.dirname(__FILE__), 'fixtures', 'about_yml_plugins', name)
- end
-
- def plugin_for(path)
- Rails::Plugin.new(path)
- end
-end
diff --git a/railties/test/plugin_test_helper.rb b/railties/test/plugin_test_helper.rb
deleted file mode 100644
index 93004e0ddf..0000000000
--- a/railties/test/plugin_test_helper.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-$:.unshift File.dirname(__FILE__) + "/../lib"
-$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib"
-
-require 'test/unit'
-require 'active_support'
-require 'rails/initializer'
-require 'abstract_unit'
-
-# We need to set RAILS_ROOT if it isn't already set
-RAILS_ROOT = '.' unless defined?(RAILS_ROOT)
-
-class Test::Unit::TestCase
- private
- def plugin_fixture_root_path
- File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'plugins'))
- end
-
- def only_load_the_following_plugins!(plugins)
- @initializer.configuration.plugins = plugins
- end
-
- def plugin_fixture_path(path)
- File.join(plugin_fixture_root_path, path)
- end
-
- def assert_plugins(list_of_names, array_of_plugins, message=nil)
- assert_equal list_of_names.map { |n| n.to_s }, array_of_plugins.map { |p| p.name }, message
- end
-end
diff --git a/railties/test/plugins/vendored_test.rb b/railties/test/plugins/vendored_test.rb
index 71de542ff7..9a2d40cad8 100644
--- a/railties/test/plugins/vendored_test.rb
+++ b/railties/test/plugins/vendored_test.rb
@@ -1,19 +1,195 @@
require "isolation/abstract_unit"
-module ApplicationTests
- class PluginTest < Test::Unit::TestCase
+module PluginsTest
+ class VendoredTest < Test::Unit::TestCase
include ActiveSupport::Testing::Isolation
def setup
build_app
- end
- test "generates the plugin" do
- script "generate plugin my_plugin"
- File.open("#{app_path}/vendor/plugins/my_plugin/init.rb", 'w') do |f|
- f.puts "OMG = 'hello'"
+ @plugin = plugin "bukkits", "::LEVEL = config.log_level" do |plugin|
+ plugin.write "lib/bukkits.rb", "class Bukkits; end"
end
+ end
+
+ def boot_rails
+ super
+ require "#{app_path}/config/environment"
+ end
+
+ test "it loads the plugin's init.rb file" do
+ boot_rails
+ assert_equal "loaded", BUKKITS
+ end
+
+ test "the init.rb file has access to the config object" do
+ boot_rails
+ assert_equal :debug, LEVEL
+ end
+
+ test "the plugin puts its lib directory on the load path" do
+ boot_rails
+ require "bukkits"
+ assert_equal "Bukkits", Bukkits.name
+ end
+
+ test "plugin paths get added to the AS::Dependency list" do
+ boot_rails
+ assert_equal "Bukkits", Bukkits.name
+ end
+
+ test "plugin constants do not get reloaded by default" do
+ boot_rails
+ assert_equal "Bukkits", Bukkits.name
+ ActiveSupport::Dependencies.clear
+ @plugin.delete("lib/bukkits.rb")
+ assert_nothing_raised { Bukkits }
+ end
+
+ test "plugin constants get reloaded if config.reload_plugins is set" do
+ add_to_config <<-RUBY
+ config.reload_plugins = true
+ RUBY
+
+ boot_rails
+
+ assert_equal "Bukkits", Bukkits.name
+ ActiveSupport::Dependencies.clear
+ @plugin.delete("lib/bukkits.rb")
+ assert_raises(NameError) { Bukkits }
+ end
+
+ test "plugin should work without init.rb" do
+ @plugin.delete("init.rb")
+
+ boot_rails
+
+ require "bukkits"
+ assert_nothing_raised { Bukkits }
+ end
+
+ test "the plugin puts its models directory on the load path" do
+ @plugin.write "app/models/my_bukkit.rb", "class MyBukkit ; end"
+
+ boot_rails
+
+ assert_nothing_raised { MyBukkit }
+ end
+
+ test "the plugin puts is controllers directory on the load path" do
+ @plugin.write "app/controllers/bukkit_controller.rb", "class BukkitController ; end"
+
+ boot_rails
+
+ assert_nothing_raised { BukkitController }
+ end
+
+ test "the plugin adds its view to the load path" do
+ @plugin.write "app/controllers/bukkit_controller.rb", <<-RUBY
+ class BukkitController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ @plugin.write "app/views/bukkit/index.html.erb", "Hello bukkits"
+
+ boot_rails
+
+ require "action_controller"
+ require "rack/mock"
+ response = BukkitController.action(:index).call(Rack::MockRequest.env_for("/"))
+ assert_equal "Hello bukkits\n", response[2].body
+ end
+
+ test "the plugin adds helpers to the controller's views" do
+ @plugin.write "app/controllers/bukkit_controller.rb", <<-RUBY
+ class BukkitController < ActionController::Base
+ def index
+ end
+ end
+ RUBY
+
+ @plugin.write "app/helpers/bukkit_helper.rb", <<-RUBY
+ module BukkitHelper
+ def bukkits
+ "bukkits"
+ end
+ end
+ RUBY
+
+ @plugin.write "app/views/bukkit/index.html.erb", "Hello <%= bukkits %>"
+
+ boot_rails
+
+ require "rack/mock"
+ response = BukkitController.action(:index).call(Rack::MockRequest.env_for("/"))
+ assert_equal "Hello bukkits\n", response[2].body
+ end
+
+ test "routes.rb are added to the router" do
+ @plugin.write "config/routes.rb", <<-RUBY
+ class Sprokkit
+ def self.call(env)
+ [200, {'Content-Type' => 'text/html'}, ["I am a Sprokkit"]]
+ end
+ end
+
+ ActionController::Routing::Routes.draw do
+ match "/sprokkit", :to => Sprokkit
+ end
+ RUBY
+
+ boot_rails
+ require "rack/mock"
+ response = Rails.application.call(Rack::MockRequest.env_for("/sprokkit"))
+ assert_equal "I am a Sprokkit", response[2].join
+ end
+ end
+
+ class VendoredOrderingTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ $arr = []
+ plugin "a_plugin", "$arr << :a"
+ plugin "b_plugin", "$arr << :b"
+ plugin "c_plugin", "$arr << :c"
+ end
+
+ def boot_rails
+ super
require "#{app_path}/config/environment"
end
+
+ test "plugins are loaded alphabetically by default" do
+ boot_rails
+ assert_equal [:a, :b, :c], $arr
+ end
+
+ test "if specified, only those plugins are loaded" do
+ add_to_config "config.plugins = [:b_plugin]"
+ boot_rails
+ assert_equal [:b], $arr
+ end
+
+ test "the plugins are initialized in the order they are specified" do
+ add_to_config "config.plugins = [:b_plugin, :a_plugin]"
+ boot_rails
+ assert_equal [:b, :a], $arr
+ end
+
+ test "if :all is specified, the remaining plugins are loaded in alphabetical order" do
+ add_to_config "config.plugins = [:c_plugin, :all]"
+ boot_rails
+ assert_equal [:c, :a, :b], $arr
+ end
+
+ test "if :all is at the beginning, it represents the plugins not otherwise specified" do
+ add_to_config "config.plugins = [:all, :b_plugin]"
+ boot_rails
+ assert_equal [:a, :c, :b], $arr
+ end
end
end \ No newline at end of file
diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb
index dcf9966c0d..fc28d7e912 100644
--- a/railties/test/rails_info_test.rb
+++ b/railties/test/rails_info_test.rb
@@ -1,15 +1,4 @@
-$:.unshift File.dirname(__FILE__) + "/../lib"
-$:.unshift File.dirname(__FILE__) + "/../builtin/rails_info"
-$:.unshift File.dirname(__FILE__) + "/../../activesupport/lib"
-$:.unshift File.dirname(__FILE__) + "/../../actionpack/lib"
-
-require 'rubygems'
-gem 'rack', '~> 1.0.0'
-
-require 'test/unit'
-require 'active_support'
-require 'active_support/test_case'
-require 'action_controller'
+require 'abstract_unit'
unless defined?(Rails) && defined?(Rails::Info)
module Rails