aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2008-10-05 19:46:48 +0100
committerPratik Naik <pratiknaik@gmail.com>2008-10-05 19:46:48 +0100
commit6090513cfb8acb5554a6653a6f2cb87648585d41 (patch)
tree99bfd589a48153e33f19ae72baa6e98f5708a9b8
parent01159a6431bbc2dc7d7d95ce294c8567c954f39e (diff)
parent4df45d86097efbeabceecfe53d8ea2da9ccbb107 (diff)
downloadrails-6090513cfb8acb5554a6653a6f2cb87648585d41.tar.gz
rails-6090513cfb8acb5554a6653a6f2cb87648585d41.tar.bz2
rails-6090513cfb8acb5554a6653a6f2cb87648585d41.zip
Merge commit 'mainstream/master'
Conflicts: activerecord/lib/active_record/association_preload.rb
-rw-r--r--actionmailer/lib/action_mailer/base.rb3
-rw-r--r--actionmailer/test/abstract_unit.rb2
-rw-r--r--actionmailer/test/fixtures/test_mailer/body_ivar.erb2
-rw-r--r--actionmailer/test/mail_service_test.rb12
-rw-r--r--actionpack/lib/action_controller/base.rb19
-rw-r--r--actionpack/lib/action_controller/dispatcher.rb30
-rw-r--r--actionpack/lib/action_controller/integration.rb2
-rwxr-xr-xactionpack/lib/action_controller/request.rb5
-rw-r--r--actionpack/lib/action_controller/rescue.rb130
-rw-r--r--actionpack/lib/action_controller/test_case.rb2
-rw-r--r--actionpack/lib/action_view/base.rb18
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb10
-rw-r--r--actionpack/test/abstract_unit.rb2
-rw-r--r--actionpack/test/controller/render_test.rb41
-rw-r--r--actionpack/test/controller/rescue_test.rb35
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb6
-rw-r--r--activerecord/lib/active_record/association_preload.rb32
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb11
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb4
-rwxr-xr-xactiverecord/lib/active_record/base.rb8
-rw-r--r--activerecord/lib/active_record/calculations.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/dynamic_finder_match.rb8
-rw-r--r--activerecord/lib/active_record/reflection.rb135
-rw-r--r--activerecord/test/cases/active_schema_test_mysql.rb5
-rw-r--r--activerecord/test/cases/associations/eager_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb1
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb17
-rw-r--r--activerecord/test/cases/calculations_test.rb6
-rw-r--r--activerecord/test/cases/finder_test.rb27
-rw-r--r--activerecord/test/cases/reflection_test.rb4
-rw-r--r--activerecord/test/cases/reload_models_test.rb20
-rw-r--r--activerecord/test/models/post.rb2
-rw-r--r--activeresource/lib/active_resource/base.rb3
-rw-r--r--activeresource/test/base_test.rb24
-rw-r--r--activesupport/CHANGELOG2
-rw-r--r--activesupport/lib/active_support.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/extending.rb45
-rw-r--r--activesupport/lib/active_support/rescuable.rb108
-rw-r--r--activesupport/lib/active_support/secure_random.rb4
-rw-r--r--activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb11
-rw-r--r--activesupport/test/rescuable_test.rb75
-rw-r--r--activesupport/test/secure_random_test.rb4
-rw-r--r--activesupport/test/test_test.rb18
-rw-r--r--railties/configs/databases/ibm_db.yml62
-rw-r--r--railties/lib/initializer.rb2
-rw-r--r--railties/lib/rails/gem_builder.rb6
-rw-r--r--railties/lib/rails/gem_dependency.rb142
-rw-r--r--railties/lib/rails/plugin.rb2
-rw-r--r--railties/lib/rails/vendor_gem_source_index.rb93
-rw-r--r--railties/lib/rails_generator/generators/applications/app/app_generator.rb2
-rw-r--r--railties/lib/tasks/databases.rake2
-rw-r--r--railties/lib/tasks/gems.rake20
-rw-r--r--railties/test/gem_dependency_test.rb49
-rw-r--r--railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification28
-rw-r--r--railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb1
-rw-r--r--railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification28
-rw-r--r--railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb1
-rw-r--r--railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification28
-rw-r--r--railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb1
-rw-r--r--railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification28
-rw-r--r--railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb1
-rw-r--r--railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification28
-rw-r--r--railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb1
66 files changed, 1142 insertions, 345 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index bfe435550b..043f56ba17 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -296,6 +296,9 @@ module ActionMailer #:nodoc:
@@default_implicit_parts_order = [ "text/html", "text/enriched", "text/plain" ]
cattr_accessor :default_implicit_parts_order
+ cattr_reader :protected_instance_variables
+ @@protected_instance_variables = %w(@body)
+
# Specify the BCC addresses for the message
adv_attr_accessor :bcc
diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb
index 107b2e8bbe..905f25c9f7 100644
--- a/actionmailer/test/abstract_unit.rb
+++ b/actionmailer/test/abstract_unit.rb
@@ -1,6 +1,8 @@
require 'test/unit'
$:.unshift "#{File.dirname(__FILE__)}/../lib"
+$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
+$:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib"
require 'action_mailer'
require 'action_mailer/test_case'
diff --git a/actionmailer/test/fixtures/test_mailer/body_ivar.erb b/actionmailer/test/fixtures/test_mailer/body_ivar.erb
new file mode 100644
index 0000000000..1421e5c908
--- /dev/null
+++ b/actionmailer/test/fixtures/test_mailer/body_ivar.erb
@@ -0,0 +1,2 @@
+body: <%= @body %>
+bar: <%= @bar %> \ No newline at end of file
diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb
index f57c6f3fb8..7f9540c44b 100644
--- a/actionmailer/test/mail_service_test.rb
+++ b/actionmailer/test/mail_service_test.rb
@@ -273,6 +273,13 @@ class TestMailer < ActionMailer::Base
headers "return-path" => "another@somewhere.test"
end
+ def body_ivar(recipient)
+ recipients recipient
+ subject "Body as a local variable"
+ from "test@example.com"
+ body :body => "foo", :bar => "baz"
+ end
+
class <<self
attr_accessor :received_body
end
@@ -926,6 +933,11 @@ EOF
TestMailer.deliver_return_path
assert_match %r{^Return-Path: <another@somewhere.test>}, MockSMTP.deliveries[0][0]
end
+
+ def test_body_is_stored_as_an_ivar
+ mail = TestMailer.create_body_ivar(@recipient)
+ assert_equal "body: foo\nbar: baz", mail.body
+ end
end
end # uses_mocha
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 91f531f12c..413f6d48e5 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -290,8 +290,6 @@ module ActionController #:nodoc:
@@allow_concurrency = false
cattr_accessor :allow_concurrency
- @@guard = Monitor.new
-
# Modern REST web services often need to submit complex data to the web application.
# The <tt>@@param_parsers</tt> hash lets you register handlers which will process the HTTP body and add parameters to the
# <tt>params</tt> hash. These handlers are invoked for POST and PUT requests.
@@ -532,12 +530,7 @@ module ActionController #:nodoc:
assign_names
log_processing
-
- if @@allow_concurrency
- send(method, *arguments)
- else
- @@guard.synchronize { send(method, *arguments) }
- end
+ send(method, *arguments)
send_response
ensure
@@ -975,13 +968,15 @@ module ActionController #:nodoc:
# Sets the Last-Modified response header. Returns 304 Not Modified if the
# If-Modified-Since request header is <= last modified.
def last_modified!(utc_time)
- head(:not_modified) if response.last_modified!(utc_time)
+ response.last_modified= utc_time
+ head(:not_modified) if response.last_modified == request.if_modified_since
end
# Sets the ETag response header. Returns 304 Not Modified if the
# If-None-Match request header matches.
def etag!(etag)
- head(:not_modified) if response.etag!(etag)
+ response.etag = etag
+ head(:not_modified) if response.etag == request.if_none_match
end
# Clears the rendered results, allowing for another render to be performed.
@@ -1256,7 +1251,7 @@ module ActionController #:nodoc:
action_name = strip_out_controller(action_name)
end
end
- "#{self.class.controller_path}/#{action_name}"
+ "#{self.controller_path}/#{action_name}"
end
def strip_out_controller(path)
@@ -1264,7 +1259,7 @@ module ActionController #:nodoc:
end
def template_path_includes_controller?(path)
- self.class.controller_path.split('/')[-1] == path.split('/')[0]
+ self.controller_path.split('/')[-1] == path.split('/')[0]
end
def process_cleanup
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index bdae5f9d86..90c8400c11 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -2,6 +2,8 @@ module ActionController
# Dispatches requests to the appropriate controller and takes care of
# reloading the app after each request when Dependencies.load? is true.
class Dispatcher
+ @@guard = Mutex.new
+
class << self
def define_dispatcher_callbacks(cache_classes)
unless cache_classes
@@ -20,6 +22,7 @@ module ActionController
end
if defined?(ActiveRecord)
+ after_dispatch :checkin_connections
before_dispatch { ActiveRecord::Base.verify_active_connections! }
to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
end
@@ -98,7 +101,7 @@ module ActionController
@output, @request, @response = output, request, response
end
- def dispatch
+ def dispatch_unlocked
begin
run_callbacks :before_dispatch
handle_request
@@ -109,6 +112,16 @@ module ActionController
end
end
+ def dispatch
+ if ActionController::Base.allow_concurrency
+ dispatch_unlocked
+ else
+ @@guard.synchronize do
+ dispatch_unlocked
+ end
+ end
+ end
+
def dispatch_cgi(cgi, session_options)
if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new }
@request = CgiRequest.new(cgi, session_options)
@@ -145,6 +158,21 @@ module ActionController
Base.logger.flush
end
+ def mark_as_test_request!
+ @test_request = true
+ self
+ end
+
+ def test_request?
+ @test_request
+ end
+
+ def checkin_connections
+ # Don't return connection (and peform implicit rollback) if this request is a part of integration test
+ return if test_request?
+ ActiveRecord::Base.clear_active_connections!
+ end
+
protected
def handle_request
@controller = Routing::Routes.recognize(@request)
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index a98c1af7f9..fc473c269c 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -276,7 +276,7 @@ module ActionController
ActionController::Base.clear_last_instantiation!
env['rack.input'] = data.is_a?(IO) ? data : StringIO.new(data || '')
- @status, @headers, result_body = ActionController::Dispatcher.new.call(env)
+ @status, @headers, result_body = ActionController::Dispatcher.new.mark_as_test_request!.call(env)
@request_count += 1
@controller = ActionController::Base.last_instantiation
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 8e6cfb41dc..5e492e3ee1 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -167,7 +167,7 @@ module ActionController
parameter_format = parameters[:format]
if parameter_format
- parameter_format.to_sym
+ parameter_format
elsif xhr?
:js
else
@@ -176,8 +176,7 @@ module ActionController
end
def cache_format
- parameter_format = parameters[:format]
- parameter_format && parameter_format.to_sym
+ parameters[:format]
end
# Returns true if the request's "X-Requested-With" header contains
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index 83c4218af4..ec8e9b92d5 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -41,10 +41,9 @@ module ActionController #:nodoc:
base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
- base.class_inheritable_array :rescue_handlers
- base.rescue_handlers = []
-
base.extend(ClassMethods)
+ base.send :include, ActiveSupport::Rescuable
+
base.class_eval do
alias_method_chain :perform_action, :rescue
end
@@ -54,82 +53,12 @@ module ActionController #:nodoc:
def process_with_exception(request, response, exception) #:nodoc:
new.process(request, response, :rescue_action, exception)
end
-
- # Rescue exceptions raised in controller actions.
- #
- # <tt>rescue_from</tt> receives a series of exception classes or class
- # names, and a trailing <tt>:with</tt> option with the name of a method
- # or a Proc object to be called to handle them. Alternatively a block can
- # be given.
- #
- # Handlers that take one argument will be called with the exception, so
- # that the exception can be inspected when dealing with it.
- #
- # Handlers are inherited. They are searched from right to left, from
- # bottom to top, and up the hierarchy. The handler of the first class for
- # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
- # any.
- #
- # class ApplicationController < ActionController::Base
- # rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
- # rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
- #
- # rescue_from 'MyAppError::Base' do |exception|
- # render :xml => exception, :status => 500
- # end
- #
- # protected
- # def deny_access
- # ...
- # end
- #
- # def show_errors(exception)
- # exception.record.new_record? ? ...
- # end
- # end
- def rescue_from(*klasses, &block)
- options = klasses.extract_options!
- unless options.has_key?(:with)
- block_given? ? options[:with] = block : raise(ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument.")
- end
-
- klasses.each do |klass|
- key = if klass.is_a?(Class) && klass <= Exception
- klass.name
- elsif klass.is_a?(String)
- klass
- else
- raise(ArgumentError, "#{klass} is neither an Exception nor a String")
- end
-
- # Order is important, we put the pair at the end. When dealing with an
- # exception we will follow the documented order going from right to left.
- rescue_handlers << [key, options[:with]]
- end
- end
end
protected
# Exception handler called when the performance of an action raises an exception.
def rescue_action(exception)
- if handler_for_rescue(exception)
- rescue_action_with_handler(exception)
- else
- log_error(exception) if logger
- erase_results if performed?
-
- # Let the exception alter the response if it wants.
- # For example, MethodNotAllowed sets the Allow header.
- if exception.respond_to?(:handle_response!)
- exception.handle_response!(response)
- end
-
- if consider_all_requests_local || local_request?
- rescue_action_locally(exception)
- else
- rescue_action_in_public(exception)
- end
- end
+ rescue_with_handler(exception) || rescue_action_without_handler(exception)
end
# Overwrite to implement custom logging of errors. By default logs as fatal.
@@ -185,15 +114,20 @@ module ActionController #:nodoc:
render_for_file(rescues_path("layout"), response_code_for_rescue(exception))
end
- # Tries to rescue the exception by looking up and calling a registered handler.
- def rescue_action_with_handler(exception)
- if handler = handler_for_rescue(exception)
- if handler.arity != 0
- handler.call(exception)
- else
- handler.call
- end
- true # don't rely on the return value of the handler
+ def rescue_action_without_handler(exception)
+ log_error(exception) if logger
+ erase_results if performed?
+
+ # Let the exception alter the response if it wants.
+ # For example, MethodNotAllowed sets the Allow header.
+ if exception.respond_to?(:handle_response!)
+ exception.handle_response!(response)
+ end
+
+ if consider_all_requests_local || local_request?
+ rescue_action_locally(exception)
+ else
+ rescue_action_in_public(exception)
end
end
@@ -216,36 +150,6 @@ module ActionController #:nodoc:
rescue_responses[exception.class.name]
end
- def handler_for_rescue(exception)
- # We go from right to left because pairs are pushed onto rescue_handlers
- # as rescue_from declarations are found.
- _, handler = *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.
- #
- # Since this loop needs the constants it would be inconsistent to
- # assume they should exist at this point. An early raised exception
- # could trigger some other handler and the array could include
- # precisely a string whose corresponding constant has not yet been
- # seen. This is why we are tolerant to unknown constants.
- #
- # Note that this tolerance only matters if the exception was given as
- # a string, otherwise a NameError will be raised by the interpreter
- # itself when rescue_from CONSTANT is executed.
- klass = self.class.const_get(klass_name) rescue nil
- klass ||= klass_name.constantize rescue nil
- exception.is_a?(klass) if klass
- end
-
- case handler
- when Symbol
- method(handler)
- when Proc
- handler.bind(self)
- end
- end
-
def clean_backtrace(exception)
if backtrace = exception.backtrace
if defined?(RAILS_ROOT)
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 3e66947d5f..6a39039504 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -84,7 +84,7 @@ module ActionController
module RaiseActionExceptions
attr_accessor :exception
- def rescue_action(e)
+ def rescue_action_without_handler(e)
self.exception = e
if request.remote_addr == "0.0.0.0"
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 8c00670087..8df10c40cc 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -290,21 +290,23 @@ module ActionView #:nodoc:
private
attr_accessor :_first_render, :_last_render
- # Evaluate the local assigns and pushes them to the view.
+ # Evaluates the local assigns and controller ivars, pushes them to the view.
def _evaluate_assigns_and_ivars #:nodoc:
unless @assigns_added
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
-
- if @controller
- variables = @controller.instance_variables
- variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
- variables.each {|name| instance_variable_set(name, @controller.instance_variable_get(name)) }
- end
-
+ _copy_ivars_from_controller
@assigns_added = true
end
end
+ def _copy_ivars_from_controller #:nodoc:
+ if @controller
+ variables = @controller.instance_variable_names
+ variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
+ variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
+ end
+ end
+
def _set_controller_content_type(content_type) #:nodoc:
if controller.respond_to?(:response)
controller.response.content_type ||= content_type
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 294c22521e..208bf91dd4 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -403,6 +403,7 @@ module ActionView
# Creates a field set for grouping HTML form elements.
#
# <tt>legend</tt> will become the fieldset's title (optional as per W3C).
+ # <tt>options</tt> accept the same values as tag.
#
# === Examples
# <% field_set_tag do %>
@@ -414,9 +415,14 @@ module ActionView
# <p><%= text_field_tag 'name' %></p>
# <% end %>
# # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
- def field_set_tag(legend = nil, &block)
+ #
+ # <% field_set_tag nil, :class => 'format' do %>
+ # <p><%= text_field_tag 'name' %></p>
+ # <% end %>
+ # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
+ def field_set_tag(legend = nil, options = nil, &block)
content = capture(&block)
- concat(tag(:fieldset, {}, true))
+ concat(tag(:fieldset, options, true))
concat(content_tag(:legend, legend)) unless legend.blank?
concat(content)
concat("</fieldset>")
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 9db4cddd6a..673efa6af0 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -1,5 +1,5 @@
$:.unshift(File.dirname(__FILE__) + '/../lib')
-$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib/active_support')
+$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
require 'yaml'
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index af7b5dde62..5a6ca98b2e 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -39,6 +39,16 @@ class TestController < ActionController::Base
render :action => 'hello_world'
end
end
+
+ def conditional_hello_with_bangs
+ render :action => 'hello_world'
+ end
+ before_filter :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs
+
+ def handle_last_modified_and_etags
+ last_modified! Time.now.utc.beginning_of_day
+ etag! [:foo, 123]
+ end
def render_hello_world
render :template => "test/hello_world"
@@ -1306,6 +1316,7 @@ class EtagRenderTest < Test::Unit::TestCase
@controller = TestController.new
@request.host = "www.nextangle.com"
+ @expected_bang_etag = etag_for(expand_key([:foo, 123]))
end
def test_render_200_should_set_etag
@@ -1365,11 +1376,27 @@ class EtagRenderTest < Test::Unit::TestCase
assert_equal "<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n", @response.body
assert_equal etag_for("<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n"), @response.headers['ETag']
end
-
+
+ def test_etag_with_bang_should_set_etag
+ get :conditional_hello_with_bangs
+ assert_equal @expected_bang_etag, @response.headers["ETag"]
+ assert_response :success
+ end
+
+ def test_etag_with_bang_should_obey_if_none_match
+ @request.if_none_match = @expected_bang_etag
+ get :conditional_hello_with_bangs
+ assert_response :not_modified
+ end
+
protected
def etag_for(text)
%("#{Digest::MD5.hexdigest(text)}")
end
+
+ def expand_key(args)
+ ActiveSupport::Cache.expand_cache_key(args)
+ end
end
class LastModifiedRenderTest < Test::Unit::TestCase
@@ -1402,6 +1429,18 @@ class LastModifiedRenderTest < Test::Unit::TestCase
assert !@response.body.blank?
assert_equal @last_modified, @response.headers['Last-Modified']
end
+
+ def test_request_with_bang_gets_last_modified
+ get :conditional_hello_with_bangs
+ assert_equal @last_modified, @response.headers['Last-Modified']
+ assert_response :success
+ end
+
+ def test_request_with_bang_obeys_last_modified
+ @request.if_modified_since = @last_modified
+ get :conditional_hello_with_bangs
+ assert_response :not_modified
+ end
end
class RenderingLoggingTest < Test::Unit::TestCase
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index da076d2090..32c6c013f1 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -75,7 +75,7 @@ class RescueController < ActionController::Base
def method_not_allowed
raise ActionController::MethodNotAllowed.new(:get, :head, :put)
end
-
+
def not_implemented
raise ActionController::NotImplemented.new(:get, :put)
end
@@ -107,7 +107,7 @@ class RescueController < ActionController::Base
def record_invalid_raise_as_string
raise RecordInvalidToRescueAsString
end
-
+
def bad_gateway
raise BadGateway
end
@@ -135,18 +135,19 @@ class RescueController < ActionController::Base
end
end
-class RescueTest < Test::Unit::TestCase
+class RescueControllerTest < ActionController::TestCase
FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures".freeze
- def setup
- @controller = RescueController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+ setup :set_all_requests_local
+ setup :populate_exception_object
+ def set_all_requests_local
RescueController.consider_all_requests_local = true
@request.remote_addr = '1.2.3.4'
@request.host = 'example.com'
+ end
+ def populate_exception_object
begin
raise 'foo'
rescue => @exception
@@ -307,7 +308,7 @@ class RescueTest < Test::Unit::TestCase
assert_nil @controller.send(:clean_backtrace, Exception.new)
end
end
-
+
def test_not_implemented
with_all_requests_local false do
with_rails_public_path(".") do
@@ -463,14 +464,7 @@ class ExceptionInheritanceRescueController < ActionController::Base
end
end
-class ExceptionInheritanceRescueTest < Test::Unit::TestCase
-
- def setup
- @controller = ExceptionInheritanceRescueController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
-
+class ExceptionInheritanceRescueControllerTest < ActionController::TestCase
def test_bottom_first
get :raise_grandchild_exception
assert_response :no_content
@@ -500,14 +494,7 @@ class ControllerInheritanceRescueController < ExceptionInheritanceRescueControll
end
end
-class ControllerInheritanceRescueControllerTest < Test::Unit::TestCase
-
- def setup
- @controller = ControllerInheritanceRescueController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
-
+class ControllerInheritanceRescueControllerTest < ActionController::TestCase
def test_first_exception_in_child_controller
get :raise_first_exception_in_child_controller
assert_response :gone
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 6473d011d5..ad8baef5e4 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -271,6 +271,12 @@ class FormTagHelperTest < ActionView::TestCase
expected = %(<fieldset>Hello world!</fieldset>)
assert_dom_equal expected, output_buffer
+
+ self.output_buffer = ''
+ field_set_tag('', :class => 'format') { concat "Hello world!" }
+
+ expected = %(<fieldset class="format">Hello world!</fieldset>)
+ assert_dom_equal expected, output_buffer
end
def protect_against_forgery?
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index cef8cd8647..99e3973830 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -161,12 +161,13 @@ module ActiveRecord
# the objects' IDs to the relevant objects. Returns a 2-tuple
# <tt>(id_to_record_map, ids)</tt> where +id_to_record_map+ is the Hash,
# and +ids+ is an Array of record IDs.
- def construct_id_map(records)
+ def construct_id_map(records, primary_key=nil)
id_to_record_map = {}
ids = []
records.each do |record|
- ids << record.id
- mapped_records = (id_to_record_map[record.id.to_s] ||= [])
+ primary_key ||= record.class.primary_key
+ ids << record[primary_key]
+ mapped_records = (id_to_record_map[ids.last.to_s] ||= [])
mapped_records << record
end
ids.uniq!
@@ -180,7 +181,7 @@ module ActiveRecord
options = reflection.options
conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
- conditions << append_conditions(options, preload_options)
+ conditions << append_conditions(reflection, preload_options)
associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
@@ -213,23 +214,24 @@ module ActiveRecord
end
def preload_has_many_association(records, reflection, preload_options={})
- id_to_record_map, ids = construct_id_map(records)
- records.each {|record| record.send(reflection.name).loaded}
options = reflection.options
+ primary_key_name = reflection.through_reflection_primary_key_name
+ id_to_record_map, ids = construct_id_map(records, primary_key_name)
+ records.each {|record| record.send(reflection.name).loaded}
+
if options[:through]
through_records = preload_through_records(records, reflection, options[:through])
through_reflection = reflections[options[:through]]
- through_primary_key = through_reflection.primary_key_name
unless through_records.empty?
source = reflection.source_reflection.name
- #add conditions from reflection!
- through_records.first.class.preload_associations(through_records, source, reflection.options)
+ through_records.first.class.preload_associations(through_records, source, options)
through_records.each do |through_record|
- add_preloaded_records_to_collection(id_to_record_map[through_record[through_primary_key].to_s],
- reflection.name, through_record.send(source))
+ through_record_id = through_record[reflection.through_reflection_primary_key].to_s
+ add_preloaded_records_to_collection(id_to_record_map[through_record_id], reflection.name, through_record.send(source))
end
end
+
else
set_association_collection_records(id_to_record_map, reflection.name, find_associated_records(ids, reflection, preload_options),
reflection.primary_key_name)
@@ -317,7 +319,7 @@ module ActiveRecord
end
end
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}"
- conditions << append_conditions(options, preload_options)
+ conditions << append_conditions(reflection, preload_options)
associated_records = klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
:select => options[:select],
@@ -338,7 +340,7 @@ module ActiveRecord
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
end
- conditions << append_conditions(options, preload_options)
+ conditions << append_conditions(reflection, preload_options)
reflection.klass.find(:all,
:select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
@@ -354,9 +356,9 @@ module ActiveRecord
instance_eval("%@#{sql.gsub('@', '\@')}@")
end
- def append_conditions(options, preload_options)
+ def append_conditions(reflection, preload_options)
sql = ""
- sql << " AND (#{interpolate_sql_for_preload(sanitize_sql(options[:conditions]))})" if options[:conditions]
+ sql << " AND (#{interpolate_sql_for_preload(reflection.sanitized_conditions)})" if reflection.sanitized_conditions
sql << " AND (#{sanitize_sql preload_options[:conditions]})" if preload_options[:conditions]
sql
end
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index acdcd14ec8..b617147f67 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -240,7 +240,7 @@ module ActiveRecord
# the kind of the class of the associated objects. Meant to be used as
# a sanity check when you are about to assign an associated record.
def raise_on_type_mismatch(record)
- unless record.is_a?(@reflection.klass)
+ unless record.is_a?(@reflection.klass) || record.is_a?(@reflection.class_name.constantize)
message = "#{@reflection.class_name}(##{@reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 3171665e19..a0bb3a45b0 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -32,6 +32,14 @@ module ActiveRecord
end
protected
+ def target_reflection_has_associated_record?
+ if @reflection.through_reflection.macro == :belongs_to && @owner[@reflection.through_reflection.primary_key_name].blank?
+ false
+ else
+ true
+ end
+ end
+
def construct_find_options!(options)
options[:select] = construct_select(options[:select])
options[:from] ||= construct_from
@@ -61,6 +69,7 @@ module ActiveRecord
end
def find_target
+ return [] unless target_reflection_has_associated_record?
@reflection.klass.find(:all,
:select => construct_select,
:conditions => construct_conditions,
@@ -102,6 +111,8 @@ module ActiveRecord
"#{as}_type" => reflection.klass.quote_value(
@owner.class.base_class.name.to_s,
reflection.klass.columns_hash["#{as}_type"]) }
+ elsif reflection.macro == :belongs_to
+ { reflection.klass.primary_key => @owner[reflection.primary_key_name] }
else
{ reflection.primary_key_name => owner_quoted_id }
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index e5486738f0..1c753524de 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -342,7 +342,9 @@ module ActiveRecord
method_name = method.to_s
if super
return true
- elsif self.private_methods.include?(method_name) && !include_private_methods
+ elsif !include_private_methods && super(method, true)
+ # If we're here than we haven't found among non-private methods
+ # but found among all methods. Which means that given method is private.
return false
elsif !self.class.generated_methods?
self.class.define_attribute_methods
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 6fb05b5551..6a1a3794a2 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1780,10 +1780,10 @@ module ActiveRecord #:nodoc:
#{'result = ' if bang}if options[:conditions]
with_scope(:find => finder_options) do
- ActiveSupport::Deprecation.silence { send(:#{finder}, options) }
+ find(:#{finder}, options)
end
else
- ActiveSupport::Deprecation.silence { send(:#{finder}, options.merge(finder_options)) }
+ find(:#{finder}, options.merge(finder_options))
end
#{'result || raise(RecordNotFound)' if bang}
end
@@ -1806,9 +1806,9 @@ module ActiveRecord #:nodoc:
options = { :conditions => find_attributes }
set_readonly_option!(options)
- record = find_initial(options)
+ record = find(:first, options)
- if record.nil?
+ if record.nil?
record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) }
#{'yield(record) if block_given?'}
#{'record.save' if instantiator == :create}
diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb
index 80992dd34b..5e33cf1bd4 100644
--- a/activerecord/lib/active_record/calculations.rb
+++ b/activerecord/lib/active_record/calculations.rb
@@ -217,7 +217,7 @@ module ActiveRecord
sql << " ORDER BY #{options[:order]} " if options[:order]
add_limit!(sql, options, scope)
- sql << ") AS #{aggregate_alias}_subquery" if use_workaround
+ sql << ") #{aggregate_alias}_subquery" if use_workaround
sql
end
@@ -285,11 +285,15 @@ module ActiveRecord
operation = operation.to_s.downcase
case operation
when 'count' then value.to_i
- when 'sum' then value =~ /\./ ? value.to_f : value.to_i
- when 'avg' then value && value.to_f
- else column ? column.type_cast(value) : value
+ when 'sum' then type_cast_using_column(value || '0', column)
+ when 'avg' then value && value.to_d
+ else type_cast_using_column(value, column)
end
end
+
+ def type_cast_using_column(value, column)
+ column ? column.type_cast(value) : value
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 10cae5d840..432c341e6c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -172,21 +172,24 @@ module ActiveRecord
# within the timeout period.
def checkout
# Checkout an available connection
- conn = @connection_mutex.synchronize do
- if @checked_out.size < @connections.size
- checkout_existing_connection
- elsif @connections.size < @size
- checkout_new_connection
- end
- end
- return conn if conn
-
- # No connections available; wait for one
@connection_mutex.synchronize do
- if @queue.wait(@timeout)
- checkout_existing_connection
- else
- raise ConnectionTimeoutError, "could not obtain a database connection within #{@timeout} seconds. The pool size is currently #{@size}, perhaps you need to increase it?"
+ loop do
+ conn = if @checked_out.size < @connections.size
+ checkout_existing_connection
+ elsif @connections.size < @size
+ checkout_new_connection
+ end
+ return conn if conn
+ # No connections available; wait for one
+ if @queue.wait(@timeout)
+ next
+ else
+ # try looting dead threads
+ clear_stale_cached_connections!
+ if @size == @checked_out.size
+ raise ConnectionTimeoutError, "could not obtain a database connection within #{@timeout} seconds. The pool size is currently #{@size}, perhaps you need to increase it?"
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index a26fd02b90..1e452ae88a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -218,7 +218,7 @@ module ActiveRecord
s = column.class.string_to_binary(value).unpack("H*")[0]
"x'#{s}'"
elsif value.kind_of?(BigDecimal)
- "'#{value.to_s("F")}'"
+ value.to_s("F")
else
super
end
@@ -371,9 +371,9 @@ module ActiveRecord
end
end
- def recreate_database(name) #:nodoc:
+ def recreate_database(name, options = {}) #:nodoc:
drop_database(name)
- create_database(name)
+ create_database(name, options)
end
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb
index f4a5712981..8f9f05ce36 100644
--- a/activerecord/lib/active_record/dynamic_finder_match.rb
+++ b/activerecord/lib/active_record/dynamic_finder_match.rb
@@ -6,11 +6,11 @@ module ActiveRecord
end
def initialize(method)
- @finder = :find_initial
+ @finder = :first
case method.to_s
when /^find_(all_by|last_by|by)_([_a-zA-Z]\w*)$/
- @finder = :find_last if $1 == 'last_by'
- @finder = :find_every if $1 == 'all_by'
+ @finder = :last if $1 == 'last_by'
+ @finder = :all if $1 == 'all_by'
names = $2
when /^find_by_([_a-zA-Z]\w*)\!$/
@bang = true
@@ -31,7 +31,7 @@ module ActiveRecord
end
def instantiator?
- @finder == :find_initial && !@instantiator.nil?
+ @finder == :first && !@instantiator.nil?
end
def bang?
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index a1b498eceb..dbff4f24d6 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -13,14 +13,15 @@ module ActiveRecord
def create_reflection(macro, name, options, active_record)
case macro
when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
- reflection = AssociationReflection.new(macro, name, options, active_record)
+ klass = options[:through] ? ThroughReflection : AssociationReflection
+ reflection = klass.new(macro, name, options, active_record)
when :composed_of
reflection = AggregateReflection.new(macro, name, options, active_record)
end
write_inheritable_hash :reflections, name => reflection
reflection
end
-
+
# Returns a hash containing all AssociationReflection objects for the current class
# Example:
#
@@ -30,7 +31,7 @@ module ActiveRecord
def reflections
read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
end
-
+
# Returns an array of AggregateReflection objects for all the aggregations in the class.
def reflect_on_all_aggregations
reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
@@ -116,6 +117,11 @@ module ActiveRecord
@sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
end
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
+ def belongs_to?
+ macro == :belongs_to
+ end
+
private
def derive_class_name
name.to_s.camelize
@@ -192,6 +198,52 @@ module ActiveRecord
end
end
+ def check_validity!
+ end
+
+ def through_reflection
+ false
+ end
+
+ def through_reflection_primary_key_name
+ end
+
+ def source_reflection
+ nil
+ end
+
+ private
+ def derive_class_name
+ class_name = name.to_s.camelize
+ class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
+ class_name
+ end
+
+ def derive_primary_key_name
+ if belongs_to?
+ "#{name}_id"
+ elsif options[:as]
+ "#{options[:as]}_id"
+ else
+ active_record.name.foreign_key
+ end
+ end
+ end
+
+ # Holds all the meta-data about a :through association as it was specified in the Active Record class.
+ class ThroughReflection < AssociationReflection #:nodoc:
+ # Gets the source of the through reflection. It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
+ # (The <tt>:tags</tt> association on Tagging below.)
+ #
+ # class Post < ActiveRecord::Base
+ # has_many :taggings
+ # has_many :tags, :through => :taggings
+ # end
+ #
+ def source_reflection
+ @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
+ end
+
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
# of a HasManyThrough or HasOneThrough association. Example:
#
@@ -204,7 +256,7 @@ module ActiveRecord
# taggings_reflection = tags_reflection.through_reflection
#
def through_reflection
- @through_reflection ||= options[:through] ? active_record.reflect_on_association(options[:through]) : false
+ @through_reflection ||= active_record.reflect_on_association(options[:through])
end
# Gets an array of possible <tt>:through</tt> source reflection names:
@@ -215,63 +267,40 @@ module ActiveRecord
@source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
end
- # Gets the source of the through reflection. It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
- # (The <tt>:tags</tt> association on Tagging below.)
- #
- # class Post < ActiveRecord::Base
- # has_many :taggings
- # has_many :tags, :through => :taggings
- # end
- #
- def source_reflection
- return nil unless through_reflection
- @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
- end
-
def check_validity!
- if options[:through]
- if through_reflection.nil?
- raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
- end
-
- if source_reflection.nil?
- raise HasManyThroughSourceAssociationNotFoundError.new(self)
- end
+ if through_reflection.nil?
+ raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
+ end
- if options[:source_type] && source_reflection.options[:polymorphic].nil?
- raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
- end
-
- if source_reflection.options[:polymorphic] && options[:source_type].nil?
- raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
- end
-
- unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
- raise HasManyThroughSourceAssociationMacroError.new(self)
- end
+ if source_reflection.nil?
+ raise HasManyThroughSourceAssociationNotFoundError.new(self)
+ end
+
+ if options[:source_type] && source_reflection.options[:polymorphic].nil?
+ raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
end
+
+ if source_reflection.options[:polymorphic] && options[:source_type].nil?
+ raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
+ end
+
+ unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
+ raise HasManyThroughSourceAssociationMacroError.new(self)
+ end
+ end
+
+ def through_reflection_primary_key
+ through_reflection.belongs_to? ? through_reflection.klass.primary_key : through_reflection.primary_key_name
+ end
+
+ def through_reflection_primary_key_name
+ through_reflection.primary_key_name if through_reflection.belongs_to?
end
private
def derive_class_name
# get the class_name of the belongs_to association of the through reflection
- if through_reflection
- options[:source_type] || source_reflection.class_name
- else
- class_name = name.to_s.camelize
- class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
- class_name
- end
- end
-
- def derive_primary_key_name
- if macro == :belongs_to
- "#{name}_id"
- elsif options[:as]
- "#{options[:as]}_id"
- else
- active_record.name.foreign_key
- end
+ options[:source_type] || source_reflection.class_name
end
end
end
diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb
index 2a42dc3517..9aff538ce9 100644
--- a/activerecord/test/cases/active_schema_test_mysql.rb
+++ b/activerecord/test/cases/active_schema_test_mysql.rb
@@ -25,6 +25,11 @@ class ActiveSchemaTest < ActiveRecord::TestCase
assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
end
+
+ def test_recreate_mysql_database_with_encoding
+ create_database(:luca, {:charset => 'latin1'})
+ assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
+ end
end
def test_add_column
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index e78624a98d..7f42577ab0 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -116,6 +116,13 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal 2, posts.first.comments.size
end
+ def test_loading_from_an_association_that_has_a_hash_of_conditions
+ assert_nothing_raised do
+ Author.find(:all, :include => :hello_posts_with_hash_conditions)
+ end
+ assert !Author.find(authors(:david).id, :include => :hello_posts_with_hash_conditions).hello_posts.empty?
+ end
+
def test_loading_with_no_associations
assert_nil Post.find(posts(:authorless).id, :include => :author).author
end
@@ -268,6 +275,15 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
end
+ def test_eager_with_has_many_through_a_belongs_to_association
+ author = authors(:mary)
+ post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
+ author.author_favorites.create(:favorite_author_id => 1)
+ author.author_favorites.create(:favorite_author_id => 2)
+ posts_with_author_favorites = author.posts.find(:all, :include => :author_favorites)
+ assert_no_queries { posts_with_author_favorites.first.author_favorites.first.author_id }
+ end
+
def test_eager_with_has_many_through_an_sti_join_model
author = Author.find(:first, :include => :special_post_comments, :order => 'authors.id')
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 315d77de07..1bc9c39c19 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1081,3 +1081,4 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
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 12cce98c26..a07f4bcbdd 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -5,7 +5,7 @@ require 'models/reader'
require 'models/comment'
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
- fixtures :posts, :readers, :people, :comments
+ fixtures :posts, :readers, :people, :comments, :authors
def test_associate_existing
assert_queries(2) { posts(:thinking);people(:david) }
@@ -229,4 +229,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
end
+
+ def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
+ author = authors(:mary)
+ post = Post.create!(:title => "TITLE", :body => "BODY")
+ assert_equal [], post.author_favorites
+ end
+
+ def test_has_many_association_through_a_belongs_to_association
+ author = authors(:mary)
+ post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
+ author.author_favorites.create(:favorite_author_id => 1)
+ author.author_favorites.create(:favorite_author_id => 2)
+ author.author_favorites.create(:favorite_author_id => 3)
+ assert_equal post.author.author_favorites, post.author_favorites
+ end
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 754fd58f35..0fa61500c0 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -18,8 +18,8 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_average_field
value = Account.average(:credit_limit)
- assert_kind_of Float, value
- assert_in_delta 53.0, value, 0.001
+ assert_kind_of BigDecimal, value
+ assert_equal BigDecimal.new('53.0'), value
end
def test_should_return_nil_as_average
@@ -273,7 +273,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_sum_expression
- assert_equal 636, Account.sum("2 * credit_limit")
+ assert_equal '636', Account.sum("2 * credit_limit")
end
def test_count_with_from_option
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 292b88edbc..853474916c 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -21,7 +21,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location")
assert_not_nil match
assert match.finder?
- assert_equal :find_initial, match.finder
+ assert_equal :first, match.finder
assert_equal %w(age sex location), match.attribute_names
end
@@ -30,7 +30,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
assert_not_nil match
assert match.finder?
assert match.bang?
- assert_equal :find_initial, match.finder
+ assert_equal :first, match.finder
assert_equal %w(age sex location), match.attribute_names
end
@@ -38,7 +38,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
match = ActiveRecord::DynamicFinderMatch.match("find_all_by_age_and_sex_and_location")
assert_not_nil match
assert match.finder?
- assert_equal :find_every, match.finder
+ assert_equal :all, match.finder
assert_equal %w(age sex location), match.attribute_names
end
@@ -47,7 +47,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
assert_not_nil match
assert !match.finder?
assert match.instantiator?
- assert_equal :find_initial, match.finder
+ assert_equal :first, match.finder
assert_equal :new, match.instantiator
assert_equal %w(age sex location), match.attribute_names
end
@@ -57,7 +57,7 @@ class DynamicFinderMatchTest < ActiveRecord::TestCase
assert_not_nil match
assert !match.finder?
assert match.instantiator?
- assert_equal :find_initial, match.finder
+ assert_equal :first, match.finder
assert_equal :create, match.instantiator
assert_equal %w(age sex location), match.attribute_names
end
@@ -500,6 +500,23 @@ class FinderTest < ActiveRecord::TestCase
assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
end
+ uses_mocha('test_dynamic_finder_should_go_through_the_find_class_method') do
+ def test_dynamic_finders_should_go_through_the_find_class_method
+ Topic.expects(:find).with(:first, :conditions => { :title => 'The First Topic!' })
+ Topic.find_by_title("The First Topic!")
+
+ Topic.expects(:find).with(:last, :conditions => { :title => 'The Last Topic!' })
+ Topic.find_last_by_title("The Last Topic!")
+
+ Topic.expects(:find).with(:all, :conditions => { :title => 'A Topic.' })
+ Topic.find_all_by_title("A Topic.")
+
+ Topic.expects(:find).with(:first, :conditions => { :title => 'Does not exist yet for sure!' }).times(2)
+ Topic.find_or_initialize_by_title('Does not exist yet for sure!')
+ Topic.find_or_create_by_title('Does not exist yet for sure!')
+ end
+ end
+
def test_find_by_one_attribute
assert_equal topics(:first), Topic.find_by_title("The First Topic")
assert_nil Topic.find_by_title("The First Topic!")
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index e339ef41ab..e0ed3e5886 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -170,6 +170,10 @@ class ReflectionTest < ActiveRecord::TestCase
assert_nothing_raised { Firm.reflections[:clients] == Object.new }
end
+ def test_has_many_through_reflection
+ assert_kind_of ActiveRecord::Reflection::ThroughReflection, Subscriber.reflect_on_association(:books)
+ end
+
private
def assert_reflection(klass, association, options)
assert reflection = klass.reflect_on_association(association)
diff --git a/activerecord/test/cases/reload_models_test.rb b/activerecord/test/cases/reload_models_test.rb
new file mode 100644
index 0000000000..411b5f6afa
--- /dev/null
+++ b/activerecord/test/cases/reload_models_test.rb
@@ -0,0 +1,20 @@
+require "cases/helper"
+require 'models/owner'
+require 'models/pet'
+
+class ReloadModelsTest < ActiveRecord::TestCase
+ def test_has_one_with_reload
+ pet = Pet.find_by_name('parrot')
+ pet.owner = Owner.find_by_name('ashley')
+
+ # Reload the class Owner, simulating auto-reloading of model classes in a
+ # development environment. Note that meanwhile the class Pet is not
+ # reloaded, simulating a class that is present in a plugin.
+ Object.class_eval { remove_const :Owner }
+ Kernel.load(File.expand_path(File.join(File.dirname(__FILE__), "../models/owner.rb")))
+
+ pet = Pet.find_by_name('parrot')
+ pet.owner = Owner.find_by_name('ashley')
+ assert_equal pet.owner, Owner.find_by_name('ashley')
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 3adbc0ce1f..6da37c31ff 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -22,6 +22,8 @@ class Post < ActiveRecord::Base
end
end
+ has_many :author_favorites, :through => :author
+
has_many :comments_with_interpolated_conditions, :class_name => 'Comment',
:conditions => ['#{"#{aliased_table_name}." rescue ""}body = ?', 'Thank you for the welcome']
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index d966062c7f..74d8128c0e 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -884,6 +884,7 @@ module ActiveResource
#
# ==== Examples
# my_attrs = {:name => 'J&J Textiles', :industry => 'Cloth and textiles'}
+ # my_attrs = {:name => 'Marty', :colors => ["red", "green", "blue"]}
#
# the_supplier = Supplier.find(:first)
# the_supplier.name # => 'J&M Textiles'
@@ -906,7 +907,7 @@ module ActiveResource
case value
when Array
resource = find_or_create_resource_for_collection(key)
- value.map { |attrs| resource.new(attrs) }
+ value.map { |attrs| attrs.is_a?(String) ? attrs.dup : resource.new(attrs) }
when Hash
resource = find_or_create_resource_for(key)
resource.new(value)
diff --git a/activeresource/test/base_test.rb b/activeresource/test/base_test.rb
index 7460fd45b0..bb08098683 100644
--- a/activeresource/test/base_test.rb
+++ b/activeresource/test/base_test.rb
@@ -46,10 +46,24 @@ class BaseTest < Test::Unit::TestCase
:children => [{:name => 'Natacha'}]},
{:name => 'Milena',
:children => []}]}]}.to_xml(:root => 'customer')
+ # - resource with yaml array of strings; for ActiveRecords using serialize :bar, Array
+ @marty = <<-eof
+ <?xml version=\"1.0\" encoding=\"UTF-8\"?>
+ <person>
+ <id type=\"integer\">5</id>
+ <name>Marty</name>
+ <colors type=\"yaml\">---
+ - \"red\"
+ - \"green\"
+ - \"blue\"
+ </colors>
+ </person>
+ eof
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/1.xml", {}, @matz
mock.get "/people/2.xml", {}, @david
+ mock.get "/people/5.xml", {}, @marty
mock.get "/people/Greg.xml", {}, @greg
mock.get "/people/4.xml", {'key' => 'value'}, nil, 404
mock.put "/people/1.xml", {}, nil, 204
@@ -851,4 +865,14 @@ class BaseTest < Test::Unit::TestCase
end
end
end
+
+ def test_load_yaml_array
+ assert_nothing_raised do
+ marty = Person.find(5)
+ assert_equal 3, marty.colors.size
+ marty.colors.each do |color|
+ assert_kind_of String, color
+ end
+ end
+ end
end
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 9700a11531..2c6f4ed582 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* Add ActiveSupport::Rescuable module abstracting ActionController::Base rescue_from features. [Norbert Crombach, Pratik]
+
* Switch from String#chars to String#mb_chars for the unicode proxy. [Manfred Stienstra]
This helps with 1.8.7 compatibility and also improves performance for some operations by reducing indirection.
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index b30faff06d..0ff09067ec 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -56,6 +56,8 @@ require 'active_support/time_with_zone'
require 'active_support/secure_random'
+require 'active_support/rescuable'
+
I18n.load_path << File.dirname(__FILE__) + '/active_support/locale/en-US.yml'
Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector')
diff --git a/activesupport/lib/active_support/core_ext/object/extending.rb b/activesupport/lib/active_support/core_ext/object/extending.rb
index 082e98a297..bbf6f8563b 100644
--- a/activesupport/lib/active_support/core_ext/object/extending.rb
+++ b/activesupport/lib/active_support/core_ext/object/extending.rb
@@ -3,22 +3,43 @@ class Object
Class.remove_class(*subclasses_of(*superclasses))
end
- # Exclude this class unless it's a subclass of our supers and is defined.
- # We check defined? in case we find a removed class that has yet to be
- # garbage collected. This also fails for anonymous classes -- please
- # submit a patch if you have a workaround.
- def subclasses_of(*superclasses) #:nodoc:
- subclasses = []
-
- superclasses.each do |sup|
- ObjectSpace.each_object(class << sup; self; end) do |k|
- if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
- subclasses << k
+ begin
+ ObjectSpace.each_object(Class.new) {}
+
+ # Exclude this class unless it's a subclass of our supers and is defined.
+ # We check defined? in case we find a removed class that has yet to be
+ # garbage collected. This also fails for anonymous classes -- please
+ # submit a patch if you have a workaround.
+ def subclasses_of(*superclasses) #:nodoc:
+ subclasses = []
+
+ superclasses.each do |sup|
+ ObjectSpace.each_object(class << sup; self; end) do |k|
+ if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
+ subclasses << k
+ end
end
end
+
+ subclasses
end
+ rescue RuntimeError
+ # JRuby and any implementations which cannot handle the objectspace traversal
+ # above fall back to this implementation
+ def subclasses_of(*superclasses) #:nodoc:
+ subclasses = []
- subclasses
+ superclasses.each do |sup|
+ ObjectSpace.each_object(Class) do |k|
+ if superclasses.any? { |superclass| k < superclass } &&
+ (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
+ subclasses << k
+ end
+ end
+ subclasses.uniq!
+ end
+ subclasses
+ end
end
def extended_by #:nodoc:
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
new file mode 100644
index 0000000000..f2bc12e832
--- /dev/null
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -0,0 +1,108 @@
+module ActiveSupport
+ # Rescuable module adds support for easier exception handling.
+ module Rescuable
+ def self.included(base) # :nodoc:
+ base.class_inheritable_accessor :rescue_handlers
+ base.rescue_handlers = []
+
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ # Rescue exceptions raised in controller actions.
+ #
+ # <tt>rescue_from</tt> receives a series of exception classes or class
+ # names, and a trailing <tt>:with</tt> option with the name of a method
+ # or a Proc object to be called to handle them. Alternatively a block can
+ # be given.
+ #
+ # Handlers that take one argument will be called with the exception, so
+ # that the exception can be inspected when dealing with it.
+ #
+ # Handlers are inherited. They are searched from right to left, from
+ # bottom to top, and up the hierarchy. The handler of the first class for
+ # which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
+ # any.
+ #
+ # class ApplicationController < ActionController::Base
+ # rescue_from User::NotAuthorized, :with => :deny_access # self defined exception
+ # rescue_from ActiveRecord::RecordInvalid, :with => :show_errors
+ #
+ # rescue_from 'MyAppError::Base' do |exception|
+ # render :xml => exception, :status => 500
+ # end
+ #
+ # protected
+ # def deny_access
+ # ...
+ # end
+ #
+ # def show_errors(exception)
+ # exception.record.new_record? ? ...
+ # end
+ # end
+ def rescue_from(*klasses, &block)
+ options = klasses.extract_options!
+
+ unless options.has_key?(:with)
+ if block_given?
+ options[:with] = block
+ else
+ raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
+ end
+ end
+
+ klasses.each do |klass|
+ key = if klass.is_a?(Class) && klass <= Exception
+ klass.name
+ elsif klass.is_a?(String)
+ klass
+ else
+ raise ArgumentError, "#{klass} is neither an Exception nor a String"
+ end
+
+ # put the new handler at the end because the list is read in reverse
+ rescue_handlers << [key, options[:with]]
+ end
+ end
+ end
+
+ # Tries to rescue the exception by looking up and calling a registered handler.
+ def rescue_with_handler(exception)
+ if handler = handler_for_rescue(exception)
+ handler.arity != 0 ? handler.call(exception) : handler.call
+ true # don't rely on the return value of the handler
+ end
+ end
+
+ def handler_for_rescue(exception)
+ # We go from right to left because pairs are pushed onto rescue_handlers
+ # as rescue_from declarations are found.
+ _, handler = Array(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.
+ #
+ # Since this loop needs the constants it would be inconsistent to
+ # assume they should exist at this point. An early raised exception
+ # could trigger some other handler and the array could include
+ # precisely a string whose corresponding constant has not yet been
+ # seen. This is why we are tolerant to unknown constants.
+ #
+ # Note that this tolerance only matters if the exception was given as
+ # a string, otherwise a NameError will be raised by the interpreter
+ # itself when rescue_from CONSTANT is executed.
+ klass = self.class.const_get(klass_name) rescue nil
+ klass ||= klass_name.constantize rescue nil
+ exception.is_a?(klass) if klass
+ end
+
+ case handler
+ when Symbol
+ method(handler)
+ when Proc
+ handler.bind(self)
+ end
+ end
+ end
+end
diff --git a/activesupport/lib/active_support/secure_random.rb b/activesupport/lib/active_support/secure_random.rb
index 688165f9a3..97971e8830 100644
--- a/activesupport/lib/active_support/secure_random.rb
+++ b/activesupport/lib/active_support/secure_random.rb
@@ -164,13 +164,13 @@ module ActiveSupport
hex = n.to_s(16)
hex = '0' + hex if (hex.length & 1) == 1
bin = [hex].pack("H*")
- mask = bin[0].ord
+ mask = bin[0]
mask |= mask >> 1
mask |= mask >> 2
mask |= mask >> 4
begin
rnd = SecureRandom.random_bytes(bin.length)
- rnd[0] = (rnd[0].ord & mask).chr
+ rnd[0] = rnd[0] & mask
end until rnd < bin
rnd.unpack("H*")[0].hex
else
diff --git a/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb b/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb
index 63d1ba6507..e5853bf828 100644
--- a/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb
+++ b/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb
@@ -37,15 +37,18 @@ module Test
# end
def assert_difference(expressions, difference = 1, message = nil, &block)
expression_evaluations = Array(expressions).map do |expression|
- lambda do
+ [expression, lambda do
eval(expression, block.__send__(:binding))
- end
+ end]
end
- original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression.call }
+ original_values = expression_evaluations.inject([]) { |memo, expression| memo << expression[1].call }
yield
expression_evaluations.each_with_index do |expression, i|
- assert_equal original_values[i] + difference, expression.call, message
+ full_message = ""
+ full_message << "#{message}.\n" if message
+ full_message << "<#{expression[0]}> was the expression that failed"
+ assert_equal original_values[i] + difference, expression[1].call, full_message
end
end
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
new file mode 100644
index 0000000000..9f2b783b2e
--- /dev/null
+++ b/activesupport/test/rescuable_test.rb
@@ -0,0 +1,75 @@
+require 'abstract_unit'
+
+class WraithAttack < StandardError
+end
+
+class NuclearExplosion < StandardError
+end
+
+class MadRonon < StandardError
+ attr_accessor :message
+
+ def initialize(message)
+ @message = message
+ super()
+ end
+end
+
+class Stargate
+ attr_accessor :result
+
+ include ActiveSupport::Rescuable
+
+ rescue_from WraithAttack, :with => :sos
+
+ rescue_from NuclearExplosion do
+ @result = 'alldead'
+ end
+
+ rescue_from MadRonon do |e|
+ @result = e.message
+ end
+
+ def dispatch(method)
+ send(method)
+ rescue Exception => e
+ rescue_with_handler(e)
+ end
+
+ def attack
+ raise WraithAttack
+ end
+
+ def nuke
+ raise NuclearExplosion
+ end
+
+ def ronanize
+ raise MadRonon.new("dex")
+ end
+
+ def sos
+ @result = 'killed'
+ end
+end
+
+class RescueableTest < Test::Unit::TestCase
+ def setup
+ @stargate = Stargate.new
+ end
+
+ def test_rescue_from_with_method
+ @stargate.dispatch :attack
+ assert_equal 'killed', @stargate.result
+ end
+
+ def test_rescue_from_with_block
+ @stargate.dispatch :nuke
+ assert_equal 'alldead', @stargate.result
+ end
+
+ def test_rescue_from_with_block_with_args
+ @stargate.dispatch :ronanize
+ assert_equal 'dex', @stargate.result
+ end
+end
diff --git a/activesupport/test/secure_random_test.rb b/activesupport/test/secure_random_test.rb
index b0b6c21a81..44694cd811 100644
--- a/activesupport/test/secure_random_test.rb
+++ b/activesupport/test/secure_random_test.rb
@@ -12,4 +12,8 @@ class SecureRandomTest < Test::Unit::TestCase
b2 = ActiveSupport::SecureRandom.hex(64)
assert_not_equal b1, b2
end
+
+ def test_random_number
+ assert ActiveSupport::SecureRandom.random_number(5000) < 5000
+ end
end
diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb
index 26a45af255..4e253848f6 100644
--- a/activesupport/test/test_test.rb
+++ b/activesupport/test/test_test.rb
@@ -60,6 +60,24 @@ class AssertDifferenceTest < Test::Unit::TestCase
@object.increment
end
end
+
+ def test_array_of_expressions_identify_failure
+ assert_difference ['@object.num', '1 + 1'] do
+ @object.increment
+ end
+ fail 'should not get to here'
+ rescue Test::Unit::AssertionFailedError => e
+ assert_equal "<1 + 1> was the expression that failed.\n<3> expected but was\n<2>.", e.message
+ end
+
+ def test_array_of_expressions_identify_failure_when_message_provided
+ assert_difference ['@object.num', '1 + 1'], 1, 'something went wrong' do
+ @object.increment
+ end
+ fail 'should not get to here'
+ rescue Test::Unit::AssertionFailedError => e
+ assert_equal "something went wrong.\n<1 + 1> was the expression that failed.\n<3> expected but was\n<2>.", e.message
+ end
else
def default_test; end
end
diff --git a/railties/configs/databases/ibm_db.yml b/railties/configs/databases/ibm_db.yml
new file mode 100644
index 0000000000..a9716ddb44
--- /dev/null
+++ b/railties/configs/databases/ibm_db.yml
@@ -0,0 +1,62 @@
+# IBM Dataservers
+#
+# Home Page
+# http://rubyforge.org/projects/rubyibm/
+#
+# To install the ibm_db gem:
+# On Linux:
+# Source the db2profile file and set the necessary environment variables:
+#
+# . /home/db2inst1/sqllib/db2profile
+# export IBM_DB_DIR=/opt/ibm/db2/V9.1
+# export IBM_DB_LIB=/opt/ibm/db2/V9.1/lib32
+#
+# Then issue the command: gem install ibm_db
+#
+# On Windows:
+# Issue the command: gem install ibm_db
+# If prompted, select the mswin32 option
+#
+# For more details on the installation refer to http://rubyforge.org/docman/view.php/2361/7682/IBM_DB_GEM.pdf
+#
+# For more details on the connection parameters below refer to:
+# http://rubyibm.rubyforge.org/docs/adapter/0.9.0/rdoc/classes/ActiveRecord/ConnectionAdapters/IBM_DBAdapter.html
+
+development:
+ adapter: ibm_db
+ username: db2inst1
+ password:
+ database: <%= app_name[0,4] %>_dev
+ #schema: db2inst1
+ #host: localhost
+ #port: 50000
+ #account: my_account
+ #app_user: my_app_user
+ #application: my_application
+ #workstation: my_workstation
+
+test:
+ adapter: ibm_db
+ username: db2inst1
+ password:
+ database: <%= app_name[0,4] %>_tst
+ #schema: db2inst1
+ #host: localhost
+ #port: 50000
+ #account: my_account
+ #app_user: my_app_user
+ #application: my_application
+ #workstation: my_workstation
+
+production:
+ adapter: ibm_db
+ username: db2inst1
+ password:
+ database: <%= app_name[0,4] %>_prd
+ #schema: db2inst1
+ #host: localhost
+ #port: 50000
+ #account: my_account
+ #app_user: my_app_user
+ #application: my_application
+ #workstation: my_workstation \ No newline at end of file
diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb
index 74d2daa34b..0aec97dece 100644
--- a/railties/lib/initializer.rb
+++ b/railties/lib/initializer.rb
@@ -212,6 +212,7 @@ module Rails
Gem.loaded_specs[stub] = Gem::Specification.new do |s|
s.name = stub
s.version = Rails::VERSION::STRING
+ s.loaded_from = ""
end
end
end
@@ -878,7 +879,6 @@ Run `rake gems:install` to install the missing gems.
components
config
lib
- vendor
).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
paths.concat builtin_directories
diff --git a/railties/lib/rails/gem_builder.rb b/railties/lib/rails/gem_builder.rb
index e7e06d0008..79c61cc034 100644
--- a/railties/lib/rails/gem_builder.rb
+++ b/railties/lib/rails/gem_builder.rb
@@ -12,10 +12,10 @@ module Rails
@spec = spec
@gem_dir = gem_dir
end
-
+
# silence the underlying builder
def say(message)
end
-
+
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb
index d58ae450eb..3b2f4846a6 100644
--- a/railties/lib/rails/gem_dependency.rb
+++ b/railties/lib/rails/gem_dependency.rb
@@ -1,50 +1,76 @@
+require 'rails/vendor_gem_source_index'
+
+module Gem
+ def self.source_index=(index)
+ @@source_index = index
+ end
+end
+
module Rails
class GemDependency
- attr_accessor :name, :requirement, :version, :lib, :source
+ attr_accessor :lib, :source
def self.unpacked_path
@unpacked_path ||= File.join(RAILS_ROOT, 'vendor', 'gems')
end
+ @@framework_gems = {}
+
+ def self.add_frozen_gem_path
+ @@paths_loaded ||= begin
+ Gem.source_index = Rails::VendorGemSourceIndex.new(Gem.source_index)
+ # loaded before us - we can't change them, so mark them
+ Gem.loaded_specs.each do |name, spec|
+ @@framework_gems[name] = spec
+ end
+ end
+ end
+
+ def framework_gem?
+ @@framework_gems.has_key?(name)
+ end
+
+ def vendor_rails?
+ Gem.loaded_specs.has_key?(name) && Gem.loaded_specs[name].loaded_from.empty?
+ end
+
+ def vendor_gem?
+ Gem.loaded_specs.has_key?(name) && Gem.loaded_specs[name].loaded_from.include?(self.class.unpacked_path)
+ end
+
def initialize(name, options = {})
require 'rubygems' unless Object.const_defined?(:Gem)
if options[:requirement]
- @requirement = options[:requirement]
+ req = options[:requirement]
elsif options[:version]
- @requirement = Gem::Requirement.create(options[:version])
+ req = Gem::Requirement.create(options[:version])
+ else
+ req = Gem::Requirement.default
end
- @version = @requirement.instance_variable_get("@requirements").first.last if @requirement
- @name = name.to_s
+ @dep = Gem::Dependency.new(name, req)
@lib = options[:lib]
@source = options[:source]
@loaded = @frozen = @load_paths_added = false
- @unpack_directory = nil
- end
-
- def unpacked_paths
- Dir[File.join(self.class.unpacked_path, "#{@name}-#{@version || "*"}")]
end
def add_load_paths
+ self.class.add_frozen_gem_path
return if @loaded || @load_paths_added
- unpacked_paths = self.unpacked_paths
- if unpacked_paths.empty?
- args = [@name]
- args << @requirement.to_s if @requirement
- gem *args
- else
- $LOAD_PATH.unshift File.join(unpacked_paths.first, 'lib')
- ext = File.join(unpacked_paths.first, 'ext')
- $LOAD_PATH.unshift(ext) if File.exist?(ext)
- @frozen = true
+ if framework_gem?
+ @load_paths_added = @loaded = @frozen = true
+ return
end
+ gem @dep
+ @spec = Gem.loaded_specs[name]
+ @frozen = @spec.loaded_from.include?(self.class.unpacked_path) if @spec
@load_paths_added = true
rescue Gem::LoadError
end
def dependencies
+ return [] if framework_gem?
all_dependencies = specification.dependencies.map do |dependency|
GemDependency.new(dependency.name, :requirement => dependency.version_requirements)
end
@@ -58,22 +84,51 @@ module Rails
def load
return if @loaded || @load_paths_added == false
- require(@lib || @name) unless @lib == false
+ require(@lib || name) unless @lib == false
@loaded = true
rescue LoadError
puts $!.to_s
$!.backtrace.each { |b| puts b }
end
+ def name
+ @dep.name.to_s
+ end
+
+ def requirement
+ r = @dep.version_requirements
+ (r == Gem::Requirement.default) ? nil : r
+ end
+
def frozen?
- @frozen
+ @frozen ||= vendor_rails? || vendor_gem?
end
def loaded?
- @loaded
+ @loaded ||= begin
+ if vendor_rails?
+ true
+ else
+ # check if the gem is loaded by inspecting $"
+ # specification.files lists all the files contained in the gem
+ gem_files = specification.files
+ # select only the files contained in require_paths - typically in bin and lib
+ require_paths_regexp = Regexp.new("^(#{specification.require_paths*'|'})/")
+ gem_lib_files = gem_files.select { |f| require_paths_regexp.match(f) }
+ # chop the leading directory off - a typical file might be in
+ # lib/gem_name/file_name.rb, but it will be 'require'd as gem_name/file_name.rb
+ gem_lib_files.map! { |f| f.split('/', 2)[1] }
+ # if any of the files from the above list appear in $", the gem is assumed to
+ # have been loaded
+ !(gem_lib_files & $").empty?
+ end
+ end
end
def load_paths_added?
+ # always try to add load paths - even if a gem is loaded, it may not
+ # be a compatible version (ie random_gem 0.4 is loaded and a later spec
+ # needs >= 0.5 - gem 'random_gem' will catch this and error out)
@load_paths_added
end
@@ -93,17 +148,44 @@ module Rails
# we can access information about the gem on deployment systems
# without having the gem installed
spec_filename = File.join(gem_dir(directory), '.specification')
+ # Gem.activate changes the spec - get the original
+ spec = Gem::Specification.load(specification.loaded_from)
File.open(spec_filename, 'w') do |file|
- file.puts specification.to_yaml
+ file.puts spec.to_yaml
end
end
def ==(other)
self.name == other.name && self.requirement == other.requirement
end
+ alias_method :"eql?", :"=="
+
+ def hash
+ @dep.hash
+ end
def specification
- @spec ||= Gem.source_index.search(Gem::Dependency.new(@name, @requirement)).sort_by { |s| s.version }.last
+ # code repeated from Gem.activate. Find a matching spec, or the currently loaded version.
+ # error out if loaded version and requested version are incompatible.
+ @spec ||= begin
+ matches = Gem.source_index.search(@dep)
+ matches << @@framework_gems[name] if framework_gem?
+ if Gem.loaded_specs[name] then
+ # This gem is already loaded. If the currently loaded gem is not in the
+ # list of candidate gems, then we have a version conflict.
+ existing_spec = Gem.loaded_specs[name]
+ unless matches.any? { |spec| spec.version == existing_spec.version } then
+ raise Gem::Exception,
+ "can't activate #{@dep}, already activated #{existing_spec.full_name}"
+ end
+ # we're stuck with it, so change to match
+ @dep.version_requirements = Gem::Requirement.create("=#{existing_spec.version}")
+ existing_spec
+ else
+ # new load
+ matches.last
+ end
+ end
end
private
@@ -112,17 +194,15 @@ module Rails
end
def install_command
- cmd = %w(install) << @name
- cmd << "--version" << %("#{@requirement.to_s}") if @requirement
+ cmd = %w(install) << name
+ cmd << "--version" << %("#{requirement.to_s}") if requirement
cmd << "--source" << @source if @source
cmd
end
def unpack_command
- cmd = %w(unpack) << @name
- # We don't quote this requirement as it's run through GemRunner instead
- # of shelling out to gem
- cmd << "--version" << @requirement.to_s if @requirement
+ cmd = %w(unpack) << name
+ cmd << "--version" << "= "+specification.version.to_s if requirement
cmd
end
end
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index b8b2b57038..4d983843af 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -112,7 +112,7 @@ module Rails
class GemPlugin < Plugin
# Initialize this plugin from a Gem::Specification.
def initialize(spec, gem)
- directory = (gem.frozen? && gem.unpacked_paths.first) || File.join(spec.full_gem_path)
+ directory = spec.full_gem_path
super(directory)
@name = spec.name
end
diff --git a/railties/lib/rails/vendor_gem_source_index.rb b/railties/lib/rails/vendor_gem_source_index.rb
new file mode 100644
index 0000000000..c8701101e2
--- /dev/null
+++ b/railties/lib/rails/vendor_gem_source_index.rb
@@ -0,0 +1,93 @@
+require 'rubygems'
+require 'yaml'
+
+module Rails
+
+ class VendorGemSourceIndex
+ # VendorGemSourceIndex acts as a proxy for the Gem source index, allowing
+ # gems to be loaded from vendor/gems. Rather than the standard gem repository format,
+ # vendor/gems contains unpacked gems, with YAML specifications in .specification in
+ # each gem directory.
+ include Enumerable
+
+ attr_reader :installed_source_index
+ attr_reader :vendor_source_index
+
+ def initialize(installed_index, vendor_dir=Rails::GemDependency.unpacked_path)
+ @installed_source_index = installed_index
+ @vendor_dir = vendor_dir
+ refresh!
+ end
+
+ def refresh!
+ # reload the installed gems
+ @installed_source_index.refresh!
+ vendor_gems = {}
+
+ # handle vendor Rails gems - they are identified by having loaded_from set to ""
+ # we add them manually to the list, so that other gems can find them via dependencies
+ Gem.loaded_specs.each do |n, s|
+ next unless s.loaded_from.empty?
+ vendor_gems[s.full_name] = s
+ end
+
+ # load specifications from vendor/gems
+ Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |d|
+ spec = load_specification(d)
+ next unless spec
+ # NOTE: this is a bit of a hack - the gem system expects a different structure
+ # than we have.
+ # It's looking for:
+ # repository
+ # -> specifications
+ # - gem_name.spec <= loaded_from points to this
+ # -> gems
+ # - gem_name <= gem files here
+ # and therefore goes up one directory from loaded_from, then adds gems/gem_name
+ # to the path.
+ # But we have:
+ # vendor
+ # -> gems
+ # -> gem_name <= gem files here
+ # - .specification
+ # so we set loaded_from to vendor/gems/.specification (not a real file) to
+ # get the correct behavior.
+ spec.loaded_from = File.join(Rails::GemDependency.unpacked_path, '.specification')
+ vendor_gems[File.basename(d)] = spec
+ end
+ @vendor_source_index = Gem::SourceIndex.new(vendor_gems)
+ end
+
+ def load_specification(gem_dir)
+ spec_file = File.join(gem_dir, '.specification')
+ YAML.load_file(spec_file) if File.exist?(spec_file)
+ end
+
+ def find_name(gem_name, version_requirement = Gem::Requirement.default)
+ search(/^#{gem_name}$/, version_requirement)
+ end
+
+ def search(*args)
+ # look for vendor gems, and then installed gems - later elements take priority
+ @installed_source_index.search(*args) + @vendor_source_index.search(*args)
+ end
+
+ def each(&block)
+ @vendor_source_index.each(&block)
+ @installed_source_index.each(&block)
+ end
+
+ def add_spec(spec)
+ @vendor_source_index.add_spec spec
+ end
+
+ def remove_spec(spec)
+ @vendor_source_index.remove_spec spec
+ end
+
+ def size
+ @vendor_source_index.size + @installed_source_index.size
+ end
+
+ end
+end \ No newline at end of file
diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
index 9849948339..acd3dc8c8c 100644
--- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb
+++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb
@@ -5,7 +5,7 @@ class AppGenerator < Rails::Generator::Base
DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'],
Config::CONFIG['ruby_install_name'])
- DATABASES = %w(mysql oracle postgresql sqlite2 sqlite3 frontbase)
+ DATABASES = %w(mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db)
DEFAULT_DATABASE = 'sqlite3'
default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE),
diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake
index 1431aa6944..5cb27f1f10 100644
--- a/railties/lib/tasks/databases.rake
+++ b/railties/lib/tasks/databases.rake
@@ -341,7 +341,7 @@ namespace :db do
case abcs["test"]["adapter"]
when "mysql"
ActiveRecord::Base.establish_connection(:test)
- ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"])
+ ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], abcs["test"])
when "postgresql"
ActiveRecord::Base.clear_active_connections!
drop_database(abcs['test'])
diff --git a/railties/lib/tasks/gems.rake b/railties/lib/tasks/gems.rake
index 0321e60e0f..9abdfc56b6 100644
--- a/railties/lib/tasks/gems.rake
+++ b/railties/lib/tasks/gems.rake
@@ -1,14 +1,19 @@
desc "List the gems that this rails application depends on"
task :gems => 'gems:base' do
Rails.configuration.gems.each do |gem|
- code = gem.loaded? ? (gem.frozen? ? "F" : "I") : " "
- puts "[#{code}] #{gem.name} #{gem.requirement.to_s}"
+ print_gem_status(gem)
end
puts
puts "I = Installed"
puts "F = Frozen"
end
+def print_gem_status(gem, indent=1)
+ code = gem.loaded? ? (gem.frozen? ? "F" : "I") : " "
+ puts " "*(indent-1)+" - [#{code}] #{gem.name} #{gem.requirement.to_s}"
+ gem.dependencies.each { |g| print_gem_status(g, indent+1)}
+end
+
namespace :gems do
task :base do
$rails_gem_installer = true
@@ -19,7 +24,7 @@ namespace :gems do
task :build do
$rails_gem_installer = true
require 'rails/gem_builder'
- Dir[File.join(RAILS_ROOT, 'vendor', 'gems', '*')].each do |gem_dir|
+ Dir[File.join(Rails::GemDependency.unpacked_path, '*')].each do |gem_dir|
spec_file = File.join(gem_dir, '.specification')
next unless File.exists?(spec_file)
specification = YAML::load_file(spec_file)
@@ -28,7 +33,7 @@ namespace :gems do
puts "Built gem: '#{gem_dir}'"
end
end
-
+
desc "Installs all required gems for this application."
task :install => :base do
require 'rubygems'
@@ -42,10 +47,10 @@ namespace :gems do
require 'rubygems/gem_runner'
Rails.configuration.gems.each do |gem|
next unless !gem.frozen? && (ENV['GEM'].blank? || ENV['GEM'] == gem.name)
- gem.unpack_to(File.join(RAILS_ROOT, 'vendor', 'gems')) if gem.loaded?
+ gem.unpack_to(Rails::GemDependency.unpacked_path) if gem.loaded?
end
end
-
+
namespace :unpack do
desc "Unpacks the specified gems and its dependencies into vendor/gems"
task :dependencies => :unpack do
@@ -54,9 +59,8 @@ namespace :gems do
Rails.configuration.gems.each do |gem|
next unless ENV['GEM'].blank? || ENV['GEM'] == gem.name
gem.dependencies.each do |dependency|
- dependency.add_load_paths # double check that we have not already unpacked
next if dependency.frozen?
- dependency.unpack_to(File.join(RAILS_ROOT, 'vendor', 'gems'))
+ dependency.unpack_to(Rails::GemDependency.unpacked_path)
end
end
end
diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb
index c94c950455..89e25341d1 100644
--- a/railties/test/gem_dependency_test.rb
+++ b/railties/test/gem_dependency_test.rb
@@ -37,39 +37,78 @@ uses_mocha "Plugin Tests" do
end
def test_gem_with_version_unpack_install_command
+ # stub out specification method, or else test will fail if hpricot 0.6 isn't installed
+ mock_spec = mock()
+ mock_spec.stubs(:version).returns('0.6')
+ @gem_with_version.stubs(:specification).returns(mock_spec)
assert_equal ["unpack", "hpricot", "--version", '= 0.6'], @gem_with_version.unpack_command
end
def test_gem_adds_load_paths
- @gem.expects(:gem).with(@gem.name)
+ @gem.expects(:gem).with(Gem::Dependency.new(@gem.name, nil))
@gem.add_load_paths
end
def test_gem_with_version_adds_load_paths
- @gem_with_version.expects(:gem).with(@gem_with_version.name, @gem_with_version.requirement.to_s)
+ @gem_with_version.expects(:gem).with(Gem::Dependency.new(@gem_with_version.name, @gem_with_version.requirement.to_s))
@gem_with_version.add_load_paths
end
def test_gem_loading
- @gem.expects(:gem).with(@gem.name)
+ @gem.expects(:gem).with(Gem::Dependency.new(@gem.name, nil))
@gem.expects(:require).with(@gem.name)
@gem.add_load_paths
@gem.load
end
def test_gem_with_lib_loading
- @gem_with_lib.expects(:gem).with(@gem_with_lib.name)
+ @gem_with_lib.expects(:gem).with(Gem::Dependency.new(@gem_with_lib.name, nil))
@gem_with_lib.expects(:require).with(@gem_with_lib.lib)
@gem_with_lib.add_load_paths
@gem_with_lib.load
end
def test_gem_without_lib_loading
- @gem_without_load.expects(:gem).with(@gem_without_load.name)
+ @gem_without_load.expects(:gem).with(Gem::Dependency.new(@gem_without_load.name, nil))
@gem_without_load.expects(:require).with(@gem_without_load.lib).never
@gem_without_load.add_load_paths
@gem_without_load.load
end
+ def test_gem_dependencies_compare_for_uniq
+ gem1 = Rails::GemDependency.new "gem1"
+ gem1a = Rails::GemDependency.new "gem1"
+ gem2 = Rails::GemDependency.new "gem2"
+ gem2a = Rails::GemDependency.new "gem2"
+ gem3 = Rails::GemDependency.new "gem2", :version => ">=0.1"
+ gem3a = Rails::GemDependency.new "gem2", :version => ">=0.1"
+ gem4 = Rails::GemDependency.new "gem3"
+ gems = [gem1, gem2, gem1a, gem3, gem2a, gem4, gem3a, gem2, gem4]
+ assert_equal 4, gems.uniq.size
+ end
+
+ def test_gem_load_frozen
+ dummy_gem = Rails::GemDependency.new "dummy-gem-a"
+ dummy_gem.add_load_paths
+ dummy_gem.load
+ assert_not_nil DUMMY_GEM_A_VERSION
+ end
+
+ def test_gem_load_frozen_specific_version
+ dummy_gem = Rails::GemDependency.new "dummy-gem-b", :version => '0.4.0'
+ dummy_gem.add_load_paths
+ dummy_gem.load
+ assert_not_nil DUMMY_GEM_B_VERSION
+ assert_equal '0.4.0', DUMMY_GEM_B_VERSION
+ end
+
+ def test_gem_load_frozen_minimum_version
+ dummy_gem = Rails::GemDependency.new "dummy-gem-c", :version => '>=0.5.0'
+ dummy_gem.add_load_paths
+ dummy_gem.load
+ assert_not_nil DUMMY_GEM_C_VERSION
+ assert_equal '0.6.0', DUMMY_GEM_C_VERSION
+ end
+
end
end
diff --git a/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification b/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification
new file mode 100644
index 0000000000..86dba2092c
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-a-0.4.0/.specification
@@ -0,0 +1,28 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-a
+version: !ruby/object:Gem::Version
+ version: 0.4.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+files:
+- lib
+- lib/dummy-gem-a.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem A
diff --git a/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb b/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb
new file mode 100644
index 0000000000..0453b38ab8
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-a-0.4.0/lib/dummy-gem-a.rb
@@ -0,0 +1 @@
+DUMMY_GEM_A_VERSION="0.4.0"
diff --git a/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification b/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification
new file mode 100644
index 0000000000..5ea692d7a1
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-b-0.4.0/.specification
@@ -0,0 +1,28 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-b
+version: !ruby/object:Gem::Version
+ version: 0.4.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+files:
+- lib
+- lib/dummy-gem-b.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem B
diff --git a/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb b/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb
new file mode 100644
index 0000000000..850b5dda83
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-b-0.4.0/lib/dummy-gem-b.rb
@@ -0,0 +1 @@
+DUMMY_GEM_B_VERSION="0.4.0"
diff --git a/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification b/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification
new file mode 100644
index 0000000000..ab4707124a
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-b-0.6.0/.specification
@@ -0,0 +1,28 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-b
+version: !ruby/object:Gem::Version
+ version: 0.6.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+files:
+- lib
+- lib/dummy-gem-b.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem B
diff --git a/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb b/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb
new file mode 100644
index 0000000000..7d6d01cd48
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-b-0.6.0/lib/dummy-gem-b.rb
@@ -0,0 +1 @@
+DUMMY_GEM_B_VERSION="0.6.0"
diff --git a/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification b/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification
new file mode 100644
index 0000000000..f90f60424c
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-c-0.4.0/.specification
@@ -0,0 +1,28 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-c
+version: !ruby/object:Gem::Version
+ version: 0.4.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+files:
+- lib
+- lib/dummy-gem-c.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem C
diff --git a/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb b/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb
new file mode 100644
index 0000000000..1a416bef82
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-c-0.4.0/lib/dummy-gem-c.rb
@@ -0,0 +1 @@
+DUMMY_GEM_C_VERSION="0.4.0"
diff --git a/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification b/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification
new file mode 100644
index 0000000000..e75c0aa66a
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-c-0.6.0/.specification
@@ -0,0 +1,28 @@
+--- !ruby/object:Gem::Specification
+name: dummy-gem-c
+version: !ruby/object:Gem::Version
+ version: 0.6.0
+platform: ruby
+authors:
+- "Nobody"
+date: 2008-10-03 00:00:00 -04:00
+files:
+- lib
+- lib/dummy-gem-c.rb
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+ version:
+requirements: []
+specification_version: 2
+summary: Dummy Gem C
diff --git a/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb b/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb
new file mode 100644
index 0000000000..9ba2ca8bb5
--- /dev/null
+++ b/railties/test/vendor/gems/dummy-gem-c-0.6.0/lib/dummy-gem-c.rb
@@ -0,0 +1 @@
+DUMMY_GEM_C_VERSION="0.6.0"