aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--actionmailer/CHANGELOG.md2
-rw-r--r--actionmailer/lib/action_mailer/async.rb41
-rw-r--r--actionmailer/lib/action_mailer/base.rb16
-rw-r--r--actionmailer/test/base_test.rb23
-rw-r--r--actionmailer/test/fixtures/async_mailer/welcome.erb1
-rw-r--r--actionmailer/test/mailers/async_mailer.rb3
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb16
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb6
-rw-r--r--actionpack/lib/action_controller/test_case.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb17
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_view/base.rb17
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb26
-rw-r--r--actionpack/lib/action_view/lookup_context.rb2
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb3
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb4
-rw-r--r--actionpack/lib/action_view/template/text.rb10
-rw-r--r--actionpack/test/controller/filters_test.rb12
-rw-r--r--actionpack/test/controller/test_case_test.rb7
-rw-r--r--actionpack/test/dispatch/mapper_test.rb3
-rw-r--r--actionpack/test/dispatch/routing_test.rb49
-rw-r--r--actionpack/test/fixtures/routes/bogus.rb1
-rw-r--r--actionpack/test/fixtures/routes/external.rb1
-rw-r--r--actionpack/test/template/number_helper_i18n_test.rb122
-rw-r--r--actionpack/test/template/number_helper_test.rb58
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb39
-rw-r--r--activemodel/lib/active_model/callbacks.rb14
-rw-r--r--activemodel/lib/active_model/errors.rb6
-rw-r--r--activemodel/lib/active_model/mass_assignment_security/permission_set.rb6
-rw-r--r--activemodel/lib/active_model/model.rb2
-rw-r--r--activemodel/lib/active_model/validations.rb3
-rw-r--r--activemodel/lib/active_model/validations/acceptance.rb6
-rw-r--r--activemodel/lib/active_model/validations/confirmation.rb4
-rw-r--r--activemodel/lib/active_model/validations/exclusion.rb2
-rw-r--r--activemodel/lib/active_model/validations/format.rb8
-rw-r--r--activemodel/lib/active_model/validations/inclusion.rb2
-rw-r--r--activemodel/lib/active_model/validations/length.rb2
-rw-r--r--activemodel/lib/active_model/validations/numericality.rb2
-rw-r--r--activemodel/lib/active_model/validations/presence.rb4
-rw-r--r--activemodel/lib/active_model/validations/with.rb10
-rw-r--r--activerecord/CHANGELOG.md35
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb2
-rw-r--r--activerecord/lib/active_record/errors.rb10
-rw-r--r--activerecord/lib/active_record/railties/databases.rake64
-rw-r--r--activerecord/lib/active_record/relation.rb4
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb29
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb12
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb23
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb9
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb4
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/model.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/enum_test.rb10
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb65
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb1
-rw-r--r--activerecord/test/cases/base_test.rb85
-rw-r--r--activerecord/test/cases/calculations_test.rb11
-rw-r--r--activerecord/test/cases/finder_test.rb4
-rw-r--r--activerecord/test/cases/inclusion_test.rb2
-rw-r--r--activerecord/test/cases/reflection_test.rb52
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb (renamed from activerecord/test/cases/database_tasks_test.rb)11
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb (renamed from activerecord/test/cases/mysql_rake_test.rb)18
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb (renamed from activerecord/test/cases/postgresql_rake_test.rb)26
-rw-r--r--activerecord/test/cases/tasks/sqlite_rake_test.rb (renamed from activerecord/test/cases/sqlite_rake_test.rb)21
-rw-r--r--activerecord/test/models/developer.rb1
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb9
-rw-r--r--activerecord/test/schema/mysql_specific_schema.rb10
-rw-r--r--activerecord/test/schema/schema.rb1
-rw-r--r--activesupport/CHANGELOG.md2
-rw-r--r--activesupport/lib/active_support/callbacks.rb3
-rw-r--r--activesupport/lib/active_support/concern.rb5
-rw-r--r--activesupport/lib/active_support/configurable.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/hash/keys.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb1
-rw-r--r--activesupport/lib/active_support/file_update_checker.rb4
-rw-r--r--activesupport/lib/active_support/inflections.rb2
-rw-r--r--activesupport/lib/active_support/inflector/inflections.rb2
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb1
-rw-r--r--activesupport/lib/active_support/number_helper.rb29
-rw-r--r--activesupport/lib/active_support/testing/declarative.rb2
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb2
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb6
-rw-r--r--activesupport/test/file_update_checker_test.rb15
-rw-r--r--activesupport/test/i18n_test.rb5
-rw-r--r--activesupport/test/number_helper_i18n_test.rb114
-rw-r--r--guides/source/4_0_release_notes.textile97
-rw-r--r--guides/source/action_mailer_basics.textile31
-rw-r--r--guides/source/getting_started.textile12
-rw-r--r--guides/source/routing.textile18
-rw-r--r--railties/CHANGELOG.md13
-rw-r--r--railties/lib/rails/application.rb191
-rw-r--r--railties/lib/rails/application/railties.rb13
-rw-r--r--railties/lib/rails/application/routes_inspector.rb (renamed from railties/lib/rails/application/route_inspector.rb)2
-rw-r--r--railties/lib/rails/application/routes_reloader.rb13
-rw-r--r--railties/lib/rails/engine.rb140
-rw-r--r--railties/lib/rails/engine/configuration.rb1
-rw-r--r--railties/lib/rails/engine/railties.rb26
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb14
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb12
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb4
-rw-r--r--railties/lib/rails/info_controller.rb4
-rw-r--r--railties/lib/rails/initializable.rb2
-rw-r--r--railties/lib/rails/paths.rb10
-rw-r--r--railties/lib/rails/railtie.rb32
-rw-r--r--railties/lib/rails/tasks/routes.rake4
-rw-r--r--railties/railties.gemspec2
-rw-r--r--railties/test/application/configuration_test.rb8
-rw-r--r--railties/test/application/paths_test.rb2
-rw-r--r--railties/test/application/rake_test.rb18
-rw-r--r--railties/test/application/routes_inspect_test.rb (renamed from railties/test/application/route_inspect_test.rb)4
-rw-r--r--railties/test/application/routing_test.rb83
-rw-r--r--railties/test/engine_test.rb10
-rw-r--r--railties/test/generators/generated_attribute_test.rb23
-rw-r--r--railties/test/generators/model_generator_test.rb17
-rw-r--r--railties/test/isolation/abstract_unit.rb16
-rw-r--r--railties/test/paths_test.rb2
-rw-r--r--railties/test/railties/engine_test.rb2
132 files changed, 1263 insertions, 974 deletions
diff --git a/Gemfile b/Gemfile
index 4e34d42fb6..8ffccf50e1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,7 +8,7 @@ else
gem 'arel'
end
-gem 'minitest', '~> 3.1.0'
+gem 'minitest', '~> 3.2.0'
gem 'mocha', '>= 0.11.2'
gem 'rack-test', github: "brynary/rack-test"
gem 'bcrypt-ruby', '~> 3.0.0'
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index a822412090..4d8f739403 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -2,6 +2,8 @@
* Raise an `ActionView::MissingTemplate` exception when no implicit template could be found. *Damien Mathieu*
+* Asynchronously send messages via the Rails Queue *Brian Cardarella*
+
## Rails 3.2.5 (Jun 1, 2012) ##
* No changes.
diff --git a/actionmailer/lib/action_mailer/async.rb b/actionmailer/lib/action_mailer/async.rb
new file mode 100644
index 0000000000..a364342745
--- /dev/null
+++ b/actionmailer/lib/action_mailer/async.rb
@@ -0,0 +1,41 @@
+require 'delegate'
+
+module ActionMailer
+ module Async
+ def method_missing(method_name, *args)
+ if action_methods.include?(method_name.to_s)
+ QueuedMessage.new(queue, self, method_name, *args)
+ else
+ super
+ end
+ end
+
+ def queue
+ Rails.queue
+ end
+
+ class QueuedMessage < ::Delegator
+ attr_reader :queue
+
+ def initialize(queue, mailer_class, method_name, *args)
+ @queue = queue
+ @mailer_class = mailer_class
+ @method_name = method_name
+ @args = args
+ end
+
+ def __getobj__
+ @actual_message ||= @mailer_class.send(:new, @method_name, *@args).message
+ end
+
+ def run
+ __getobj__.deliver
+ end
+
+ # Will push the message onto the Queue to be processed
+ def deliver
+ @queue << self
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 739f9a52a9..f31e1e007b 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -456,6 +456,19 @@ module ActionMailer #:nodoc:
super || action_methods.include?(method.to_s)
end
+ # Will force ActionMailer to push new messages to the queue defined
+ # in the ActionMailer class when set to true.
+ #
+ # class WelcomeMailer < ActionMailer::Base
+ # self.async = true
+ # end
+ def async=(truth)
+ if truth
+ require 'action_mailer/async'
+ extend ActionMailer::Async
+ end
+ end
+
protected
def set_payload_for_mail(payload, mail) #:nodoc:
@@ -773,5 +786,4 @@ module ActionMailer #:nodoc:
ActiveSupport.run_load_hooks(:action_mailer, self)
end
-end
-
+end \ No newline at end of file
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 1b2e39b3f7..144a6bfe39 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -7,6 +7,8 @@ require 'active_support/time'
require 'mailers/base_mailer'
require 'mailers/proc_mailer'
require 'mailers/asset_mailer'
+require 'mailers/async_mailer'
+require 'rails/queueing'
class BaseTest < ActiveSupport::TestCase
def teardown
@@ -419,6 +421,26 @@ class BaseTest < ActiveSupport::TestCase
assert_equal(1, BaseMailer.deliveries.length)
end
+ def stub_queue(klass, queue)
+ Class.new(klass) {
+ extend Module.new {
+ define_method :queue do
+ queue
+ end
+ }
+ }
+ end
+
+ test "delivering message asynchronously" do
+ testing_queue = Rails::Queueing::TestQueue.new
+ AsyncMailer.delivery_method = :test
+ AsyncMailer.deliveries.clear
+ stub_queue(AsyncMailer, testing_queue).welcome.deliver
+ assert_equal(0, AsyncMailer.deliveries.length)
+ testing_queue.drain
+ assert_equal(1, AsyncMailer.deliveries.length)
+ end
+
test "calling deliver, ActionMailer should yield back to mail to let it call :do_delivery on itself" do
mail = Mail::Message.new
mail.expects(:do_delivery).once
@@ -434,6 +456,7 @@ class BaseTest < ActiveSupport::TestCase
end
test "should raise if missing template in implicit render" do
+ BaseMailer.deliveries.clear
assert_raises ActionView::MissingTemplate do
BaseMailer.implicit_different_template('missing_template').deliver
end
diff --git a/actionmailer/test/fixtures/async_mailer/welcome.erb b/actionmailer/test/fixtures/async_mailer/welcome.erb
new file mode 100644
index 0000000000..01f3f00c63
--- /dev/null
+++ b/actionmailer/test/fixtures/async_mailer/welcome.erb
@@ -0,0 +1 @@
+Welcome \ No newline at end of file
diff --git a/actionmailer/test/mailers/async_mailer.rb b/actionmailer/test/mailers/async_mailer.rb
new file mode 100644
index 0000000000..ce601e7343
--- /dev/null
+++ b/actionmailer/test/mailers/async_mailer.rb
@@ -0,0 +1,3 @@
+class AsyncMailer < BaseMailer
+ self.async = true
+end
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 7d73c6af8d..3da2834af0 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -49,9 +49,19 @@ module AbstractController
module ClassMethods
def view_context_class
@view_context_class ||= begin
- routes = _routes if respond_to?(:_routes)
- helpers = _helpers if respond_to?(:_helpers)
- ActionView::Base.prepare(routes, helpers)
+ routes = respond_to?(:_routes) && _routes
+ helpers = respond_to?(:_helpers) && _helpers
+
+ Class.new(ActionView::Base) do
+ if routes
+ include routes.url_helpers
+ include routes.mounted_helpers
+ end
+
+ if helpers
+ include helpers
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index cc1fa23935..39da15e26a 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -72,6 +72,12 @@ module ActionController #:nodoc:
self.controller = nil
end
+ def around(controller)
+ before(controller)
+ yield
+ after(controller)
+ end
+
protected
# gets the action cache path for the given options.
def action_path_for(options)
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 028a8d3fba..a1f29ea1bc 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -450,7 +450,7 @@ module ActionController
Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
when Array
hash_or_array_or_value.map {|i| paramify_values(i)}
- when Rack::Test::UploadedFile
+ when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
hash_or_array_or_value
else
hash_or_array_or_value.to_param
@@ -576,7 +576,7 @@ module ActionController
#
# The exception is stored in the exception accessor for further inspection.
module RaiseActionExceptions
- def self.included(base)
+ def self.included(base) #:nodoc:
unless base.method_defined?(:exception) && base.method_defined?(:exception=)
base.class_eval do
attr_accessor :exception
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 94242ad962..53a4afecb3 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1316,22 +1316,6 @@ module ActionDispatch
parent_resource.instance_of?(Resource) && @scope[:shallow]
end
- def draw(name)
- path = @draw_paths.find do |_path|
- File.exists? "#{_path}/#{name}.rb"
- end
-
- unless path
- msg = "Your router tried to #draw the external file #{name}.rb,\n" \
- "but the file was not found in:\n\n"
- msg += @draw_paths.map { |_path| " * #{_path}" }.join("\n")
- raise ArgumentError, msg
- end
-
- route_path = "#{path}/#{name}.rb"
- instance_eval(File.read(route_path), route_path.to_s)
- end
-
# match 'path' => 'controller#action'
# match 'path', to: 'controller#action'
# match 'path', 'otherpath', on: :member, via: :get
@@ -1581,7 +1565,6 @@ module ActionDispatch
def initialize(set) #:nodoc:
@set = set
- @draw_paths = set.draw_paths
@scope = { :path_names => @set.resources_path_names }
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 64b1d58ae9..1d6ca0c78d 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -237,7 +237,6 @@ module ActionDispatch
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
attr_accessor :disable_clear_and_finalize, :resources_path_names
attr_accessor :default_url_options, :request_class, :valid_conditions
- attr_accessor :draw_paths
alias :routes :set
@@ -249,7 +248,6 @@ module ActionDispatch
self.named_routes = NamedRouteCollection.new
self.resources_path_names = self.class.default_resources_path_names.dup
self.default_url_options = {}
- self.draw_paths = []
self.request_class = request_class
@valid_conditions = { :controller => true, :action => true }
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index f98648d930..7bfbc1f0aa 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -148,7 +148,6 @@ module ActionView #:nodoc:
cattr_accessor :prefix_partial_path_with_controller_namespace
@@prefix_partial_path_with_controller_namespace = true
- class_attribute :helpers
class_attribute :_routes
class_attribute :logger
@@ -166,22 +165,6 @@ module ActionView #:nodoc:
def xss_safe? #:nodoc:
true
end
-
- # This method receives routes and helpers from the controller
- # and return a subclass ready to be used as view context.
- def prepare(routes, helpers) #:nodoc:
- Class.new(self) do
- if routes
- include routes.url_helpers
- include routes.mounted_helpers
- end
-
- if helpers
- include helpers
- self.helpers = helpers
- end
- end
- end
end
attr_accessor :view_renderer
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 8f97d1f014..9720e90429 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -59,7 +59,7 @@ module ActionView
return unless number
options = options.symbolize_keys
- parse_float(number, true) if options[:raise]
+ parse_float(number, true) if options.delete(:raise)
ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options))
end
@@ -109,7 +109,9 @@ module ActionView
return unless number
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_currency(number, options) }
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
+ ActiveSupport::NumberHelper.number_to_currency(number, options)
+ }
end
# Formats a +number+ as a percentage string (e.g., 65%). You can
@@ -152,7 +154,9 @@ module ActionView
return unless number
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_percentage(number, options) }
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
+ ActiveSupport::NumberHelper.number_to_percentage(number, options)
+ }
end
# Formats a +number+ with grouped thousands using +delimiter+
@@ -187,7 +191,9 @@ module ActionView
def number_with_delimiter(number, options = {})
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_delimited(number, options) }
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
+ ActiveSupport::NumberHelper.number_to_delimited(number, options)
+ }
end
# Formats a +number+ with the specified level of
@@ -234,7 +240,9 @@ module ActionView
def number_with_precision(number, options = {})
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_rounded(number, options) }
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
+ ActiveSupport::NumberHelper.number_to_rounded(number, options)
+ }
end
@@ -288,7 +296,9 @@ module ActionView
def number_to_human_size(number, options = {})
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_human_size(number, options) }
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
+ ActiveSupport::NumberHelper.number_to_human_size(number, options)
+ }
end
# Pretty prints (formats and approximates) a number in a way it
@@ -392,7 +402,9 @@ module ActionView
def number_to_human(number, options = {})
options = escape_unsafe_delimiters_and_separators(options.symbolize_keys)
- wrap_with_output_safety_handling(number, options[:raise]){ ActiveSupport::NumberHelper.number_to_human(number, options) }
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
+ ActiveSupport::NumberHelper.number_to_human(number, options)
+ }
end
private
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index b7945a23be..00989ec405 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -24,7 +24,7 @@ module ActionView
Accessors.send :define_method, :"default_#{name}", &block
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{name}
- @details[:#{name}]
+ @details.fetch(:#{name}, [])
end
def #{name}=(value)
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
index 72616b7463..e3d8e9d508 100644
--- a/actionpack/lib/action_view/renderer/abstract_renderer.rb
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -1,7 +1,6 @@
module ActionView
class AbstractRenderer #:nodoc:
- delegate :find_template, :template_exists?, :with_fallbacks, :update_details,
- :with_layout_format, :formats, :to => :@lookup_context
+ delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
def initialize(lookup_context)
@lookup_context = lookup_context
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index 82892593f8..3c1b11396a 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -18,10 +18,10 @@ module ActionView
# Determine the template to be rendered using the given options.
def determine_template(options) #:nodoc:
- keys = options[:locals].try(:keys) || []
+ keys = options.fetch(:locals, {}).keys
if options.key?(:text)
- Template::Text.new(options[:text], formats.try(:first))
+ Template::Text.new(options[:text], formats.first)
elsif options.key?(:file)
with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
elsif options.key?(:inline)
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 4261c3b5e2..3af76dfcdb 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -1,11 +1,11 @@
module ActionView #:nodoc:
# = Action View Text Template
class Template
- class Text < String #:nodoc:
+ class Text #:nodoc:
attr_accessor :mime_type
def initialize(string, mime_type = nil)
- super(string.to_s)
+ @string = string.to_s
@mime_type = Mime[mime_type] || mime_type if mime_type
@mime_type ||= Mime::TEXT
end
@@ -18,8 +18,12 @@ module ActionView #:nodoc:
'text template'
end
+ def to_str
+ @string
+ end
+
def render(*args)
- to_s
+ to_str
end
def formats
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index ef7fbca675..b9cb93f0f4 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -326,6 +326,12 @@ class FilterTest < ActionController::TestCase
controller.instance_variable_set(:"@after_ran", true)
controller.class.execution_log << " after aroundfilter " if controller.respond_to? :execution_log
end
+
+ def around(controller)
+ before(controller)
+ yield
+ after(controller)
+ end
end
class AppendedAroundFilter
@@ -336,6 +342,12 @@ class FilterTest < ActionController::TestCase
def after(controller)
controller.class.execution_log << " after appended aroundfilter "
end
+
+ def around(controller)
+ before(controller)
+ yield
+ after(controller)
+ end
end
class AuditController < ActionController::Base
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 0d6d303b51..49137946fe 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -813,6 +813,13 @@ XML
assert_equal '159528', @response.body
end
+ def test_action_dispatch_uploaded_file_upload
+ filename = 'mona_lisa.jpg'
+ path = "#{FILES_DIR}/#{filename}"
+ post :test_file_upload, :file => ActionDispatch::Http::UploadedFile.new(:filename => path, :type => "image/jpg", :tempfile => File.open(path))
+ assert_equal '159528', @response.body
+ end
+
def test_test_uploaded_file_exception_when_file_doesnt_exist
assert_raise(RuntimeError) { Rack::Test::UploadedFile.new('non_existent_file') }
end
diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb
index bd078d2b21..8070bdec8a 100644
--- a/actionpack/test/dispatch/mapper_test.rb
+++ b/actionpack/test/dispatch/mapper_test.rb
@@ -4,12 +4,11 @@ module ActionDispatch
module Routing
class MapperTest < ActiveSupport::TestCase
class FakeSet
- attr_reader :routes, :draw_paths
+ attr_reader :routes
alias :set :routes
def initialize
@routes = []
- @draw_paths = []
end
def resources_path_names
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index f15dd7214b..fed26d89f8 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -2324,55 +2324,6 @@ class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest
end
end
-class TestDrawExternalFile < ActionDispatch::IntegrationTest
- class ExternalController < ActionController::Base
- def index
- render :text => "external#index"
- end
- end
-
- DRAW_PATH = File.expand_path('../../fixtures/routes', __FILE__)
-
- DefaultScopeRoutes = ActionDispatch::Routing::RouteSet.new.tap do |app|
- app.draw_paths << DRAW_PATH
- end
-
- def app
- DefaultScopeRoutes
- end
-
- def test_draw_external_file
- DefaultScopeRoutes.draw do
- scope :module => 'test_draw_external_file' do
- draw :external
- end
- end
-
- get '/external'
- assert_equal "external#index", @response.body
- end
-
- def test_draw_nonexistent_file
- exception = assert_raise ArgumentError do
- DefaultScopeRoutes.draw do
- draw :nonexistent
- end
- end
- assert_match 'Your router tried to #draw the external file nonexistent.rb', exception.message
- assert_match DRAW_PATH.to_s, exception.message
- end
-
- def test_draw_bogus_file
- exception = assert_raise NoMethodError do
- DefaultScopeRoutes.draw do
- draw :bogus
- end
- end
- assert_match "undefined method `wrong'", exception.message
- assert_match 'test/fixtures/routes/bogus.rb:1', exception.backtrace.first
- end
-end
-
class TestDefaultScope < ActionDispatch::IntegrationTest
module ::Blog
class PostsController < ActionController::Base
diff --git a/actionpack/test/fixtures/routes/bogus.rb b/actionpack/test/fixtures/routes/bogus.rb
deleted file mode 100644
index 41fbf0cd64..0000000000
--- a/actionpack/test/fixtures/routes/bogus.rb
+++ /dev/null
@@ -1 +0,0 @@
-wrong :route
diff --git a/actionpack/test/fixtures/routes/external.rb b/actionpack/test/fixtures/routes/external.rb
deleted file mode 100644
index d103c39f53..0000000000
--- a/actionpack/test/fixtures/routes/external.rb
+++ /dev/null
@@ -1 +0,0 @@
-get '/external' => 'external#index'
diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb
deleted file mode 100644
index d6e9de9555..0000000000
--- a/actionpack/test/template/number_helper_i18n_test.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-require 'abstract_unit'
-
-class NumberHelperTest < ActionView::TestCase
- tests ActionView::Helpers::NumberHelper
-
- def setup
- I18n.backend.store_translations 'ts',
- :number => {
- :format => { :precision => 3, :delimiter => ',', :separator => '.', :significant => false, :strip_insignificant_zeros => false },
- :currency => { :format => { :unit => '&$', :format => '%u - %n', :negative_format => '(%u - %n)', :precision => 2 } },
- :human => {
- :format => {
- :precision => 2,
- :significant => true,
- :strip_insignificant_zeros => true
- },
- :storage_units => {
- :format => "%n %u",
- :units => {
- :byte => "b",
- :kb => "k"
- }
- },
- :decimal_units => {
- :format => "%n %u",
- :units => {
- :deci => {:one => "Tenth", :other => "Tenths"},
- :unit => "u",
- :ten => {:one => "Ten", :other => "Tens"},
- :thousand => "t",
- :million => "m" ,
- :billion =>"b" ,
- :trillion =>"t" ,
- :quadrillion =>"q"
- }
- }
- },
- :percentage => { :format => {:delimiter => '', :precision => 2, :strip_insignificant_zeros => true} },
- :precision => { :format => {:delimiter => '', :significant => true} }
- },
- :custom_units_for_number_to_human => {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"}
- end
-
- def test_number_to_i18n_currency
- assert_equal("&$ - 10.00", number_to_currency(10, :locale => 'ts'))
- assert_equal("(&$ - 10.00)", number_to_currency(-10, :locale => 'ts'))
- assert_equal("-10.00 - &$", number_to_currency(-10, :locale => 'ts', :format => "%n - %u"))
- end
-
- def test_number_to_currency_with_clean_i18n_settings
- clean_i18n do
- assert_equal("$10.00", number_to_currency(10))
- assert_equal("-$10.00", number_to_currency(-10))
- end
- end
-
- def test_number_to_currency_without_currency_negative_format
- clean_i18n do
- I18n.backend.store_translations 'ts', :number => { :currency => { :format => { :unit => '@', :format => '%n %u' } } }
- assert_equal("-10.00 @", number_to_currency(-10, :locale => 'ts'))
- end
- end
-
- def test_number_with_i18n_precision
- #Delimiter was set to ""
- assert_equal("10000", number_with_precision(10000, :locale => 'ts'))
-
- #Precision inherited and significant was set
- assert_equal("1.00", number_with_precision(1.0, :locale => 'ts'))
-
- end
-
- def test_number_with_i18n_delimiter
- #Delimiter "," and separator "."
- assert_equal("1,000,000.234", number_with_delimiter(1000000.234, :locale => 'ts'))
- end
-
- def test_number_to_i18n_percentage
- # to see if strip_insignificant_zeros is true
- assert_equal("1%", number_to_percentage(1, :locale => 'ts'))
- # precision is 2, significant should be inherited
- assert_equal("1.24%", number_to_percentage(1.2434, :locale => 'ts'))
- # no delimiter
- assert_equal("12434%", number_to_percentage(12434, :locale => 'ts'))
- end
-
- def test_number_to_i18n_human_size
- #b for bytes and k for kbytes
- assert_equal("2 k", number_to_human_size(2048, :locale => 'ts'))
- assert_equal("42 b", number_to_human_size(42, :locale => 'ts'))
- end
-
- def test_number_to_human_with_default_translation_scope
- #Using t for thousand
- assert_equal "2 t", number_to_human(2000, :locale => 'ts')
- #Significant was set to true with precision 2, using b for billion
- assert_equal "1.2 b", number_to_human(1234567890, :locale => 'ts')
- #Using pluralization (Ten/Tens and Tenth/Tenths)
- assert_equal "1 Tenth", number_to_human(0.1, :locale => 'ts')
- assert_equal "1.3 Tenth", number_to_human(0.134, :locale => 'ts')
- assert_equal "2 Tenths", number_to_human(0.2, :locale => 'ts')
- assert_equal "1 Ten", number_to_human(10, :locale => 'ts')
- assert_equal "1.2 Ten", number_to_human(12, :locale => 'ts')
- assert_equal "2 Tens", number_to_human(20, :locale => 'ts')
- end
-
- def test_number_to_human_with_custom_translation_scope
- #Significant was set to true with precision 2, with custom translated units
- assert_equal "4.3 cm", number_to_human(0.0432, :locale => 'ts', :units => :custom_units_for_number_to_human)
- end
-
- private
- def clean_i18n
- load_path = I18n.load_path.dup
- I18n.load_path.clear
- I18n.reload!
- yield
- ensure
- I18n.load_path = load_path
- I18n.reload!
- end
-end
diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb
index 057cb47f53..d8fffe75ed 100644
--- a/actionpack/test/template/number_helper_test.rb
+++ b/actionpack/test/template/number_helper_test.rb
@@ -361,69 +361,39 @@ class NumberHelperTest < ActionView::TestCase
end
def test_number_helpers_should_raise_error_if_invalid_when_specified
- assert_raise InvalidNumberError do
+ exception = assert_raise InvalidNumberError do
number_to_human("x", :raise => true)
end
- begin
- number_to_human("x", :raise => true)
- rescue InvalidNumberError => e
- assert_equal "x", e.number
- end
+ assert_equal "x", exception.number
- assert_raise InvalidNumberError do
- number_to_human_size("x", :raise => true)
- end
- begin
+ exception = assert_raise InvalidNumberError do
number_to_human_size("x", :raise => true)
- rescue InvalidNumberError => e
- assert_equal "x", e.number
end
+ assert_equal "x", exception.number
- assert_raise InvalidNumberError do
+ exception = assert_raise InvalidNumberError do
number_with_precision("x", :raise => true)
end
- begin
- number_with_precision("x", :raise => true)
- rescue InvalidNumberError => e
- assert_equal "x", e.number
- end
+ assert_equal "x", exception.number
- assert_raise InvalidNumberError do
+ exception = assert_raise InvalidNumberError do
number_to_currency("x", :raise => true)
end
- begin
- number_with_precision("x", :raise => true)
- rescue InvalidNumberError => e
- assert_equal "x", e.number
- end
+ assert_equal "x", exception.number
- assert_raise InvalidNumberError do
+ exception = assert_raise InvalidNumberError do
number_to_percentage("x", :raise => true)
end
- begin
- number_to_percentage("x", :raise => true)
- rescue InvalidNumberError => e
- assert_equal "x", e.number
- end
+ assert_equal "x", exception.number
- assert_raise InvalidNumberError do
- number_with_delimiter("x", :raise => true)
- end
- begin
+ exception = assert_raise InvalidNumberError do
number_with_delimiter("x", :raise => true)
- rescue InvalidNumberError => e
- assert_equal "x", e.number
end
+ assert_equal "x", exception.number
- assert_raise InvalidNumberError do
+ exception = assert_raise InvalidNumberError do
number_to_phone("x", :raise => true)
end
- begin
- number_to_phone("x", :raise => true)
- rescue InvalidNumberError => e
- assert_equal "x", e.number
- end
-
+ assert_equal "x", exception.number
end
-
end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index f30d00b355..eb06250060 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -1,4 +1,3 @@
-require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/class/attribute'
require 'active_support/deprecation'
@@ -98,7 +97,7 @@ module ActiveModel
# person.clear_name
# person.name # => nil
def attribute_method_prefix(*prefixes)
- self.attribute_method_matchers += prefixes.map { |prefix| AttributeMethodMatcher.new :prefix => prefix }
+ self.attribute_method_matchers += prefixes.map! { |prefix| AttributeMethodMatcher.new prefix: prefix }
undefine_attribute_methods
end
@@ -133,7 +132,7 @@ module ActiveModel
# person.name # => "Bob"
# person.name_short? # => true
def attribute_method_suffix(*suffixes)
- self.attribute_method_matchers += suffixes.map { |suffix| AttributeMethodMatcher.new :suffix => suffix }
+ self.attribute_method_matchers += suffixes.map! { |suffix| AttributeMethodMatcher.new suffix: suffix }
undefine_attribute_methods
end
@@ -169,7 +168,7 @@ module ActiveModel
# person.reset_name_to_default!
# person.name # => 'Gemma'
def attribute_method_affix(*affixes)
- self.attribute_method_matchers += affixes.map { |affix| AttributeMethodMatcher.new :prefix => affix[:prefix], :suffix => affix[:suffix] }
+ self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new prefix: affix[:prefix], suffix: affix[:suffix] }
undefine_attribute_methods
end
@@ -336,15 +335,13 @@ module ActiveModel
end
def attribute_method_matcher(method_name) #:nodoc:
- if attribute_method_matchers_cache.key?(method_name)
- attribute_method_matchers_cache[method_name]
- else
+ attribute_method_matchers_cache.fetch(method_name) do |name|
# Must try to match prefixes/suffixes first, or else the matcher with no prefix/suffix
# will match every time.
matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
match = nil
- matchers.detect { |method| match = method.match(method_name) }
- attribute_method_matchers_cache[method_name] = match
+ matchers.detect { |method| match = method.match(name) }
+ attribute_method_matchers_cache[name] = match
end
end
@@ -352,18 +349,18 @@ module ActiveModel
# using the given `extra` args. This fallbacks `define_method`
# and `send` if the given names cannot be compiled.
def define_proxy_call(include_private, mod, name, send, *extra) #:nodoc:
- if name =~ NAME_COMPILABLE_REGEXP
- defn = "def #{name}(*args)"
+ defn = if name =~ NAME_COMPILABLE_REGEXP
+ "def #{name}(*args)"
else
- defn = "define_method(:'#{name}') do |*args|"
+ "define_method(:'#{name}') do |*args|"
end
- extra = (extra.map(&:inspect) << "*args").join(", ")
+ extra = (extra.map!(&:inspect) << "*args").join(", ")
- if send =~ CALL_COMPILABLE_REGEXP
- target = "#{"self." unless include_private}#{send}(#{extra})"
+ target = if send =~ CALL_COMPILABLE_REGEXP
+ "#{"self." unless include_private}#{send}(#{extra})"
else
- target = "send(:'#{send}', #{extra})"
+ "send(:'#{send}', #{extra})"
end
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
@@ -379,8 +376,6 @@ module ActiveModel
AttributeMethodMatch = Struct.new(:target, :attr_name, :method_name)
def initialize(options = {})
- options.symbolize_keys!
-
if options[:prefix] == '' || options[:suffix] == ''
ActiveSupport::Deprecation.warn(
"Specifying an empty prefix/suffix for an attribute method is no longer " \
@@ -390,7 +385,7 @@ module ActiveModel
)
end
- @prefix, @suffix = options[:prefix] || '', options[:suffix] || ''
+ @prefix, @suffix = options.fetch(:prefix, ''), options.fetch(:suffix, '')
@regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
@method_missing_target = "#{@prefix}attribute#{@suffix}"
@method_name = "#{prefix}%s#{suffix}"
@@ -399,8 +394,6 @@ module ActiveModel
def match(method_name)
if @regex =~ method_name
AttributeMethodMatch.new(method_missing_target, $1, method_name)
- else
- nil
end
end
@@ -459,7 +452,7 @@ module ActiveModel
end
protected
- def attribute_method?(attr_name)
+ def attribute_method?(attr_name) #:nodoc:
respond_to_without_attributes?(:attributes) && attributes.include?(attr_name)
end
@@ -468,7 +461,7 @@ module ActiveModel
# The struct's attributes are prefix, base and suffix.
def match_attribute_method?(method_name)
match = self.class.send(:attribute_method_matcher, method_name)
- match && attribute_method?(match.attr_name) ? match : nil
+ match if match && attribute_method?(match.attr_name)
end
def missing_attribute(attr_name, stack)
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index b8ba1a0a52..e669113001 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -46,7 +46,7 @@ module ActiveModel
# Would only create the +after_create+ and +before_create+ callback methods in
# your class.
module Callbacks
- def self.extended(base)
+ def self.extended(base) #:nodoc:
base.class_eval do
include ActiveSupport::Callbacks
end
@@ -89,11 +89,11 @@ module ActiveModel
def define_model_callbacks(*callbacks)
options = callbacks.extract_options!
options = {
- :terminator => "result == false",
- :skip_after_callbacks_if_terminated => true,
- :scope => [:kind, :name],
- :only => [:before, :around, :after]
- }.merge(options)
+ :terminator => "result == false",
+ :skip_after_callbacks_if_terminated => true,
+ :scope => [:kind, :name],
+ :only => [:before, :around, :after]
+ }.merge!(options)
types = Array(options.delete(:only))
@@ -106,6 +106,8 @@ module ActiveModel
end
end
+ private
+
def _define_before_model_callback(klass, callback) #:nodoc:
klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.before_#{callback}(*args, &block)
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index b022a875ec..4ed3462e7e 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -76,7 +76,7 @@ module ActiveModel
@messages = {}
end
- def initialize_dup(other)
+ def initialize_dup(other) #:nodoc:
@messages = other.messages.dup
super
end
@@ -96,8 +96,8 @@ module ActiveModel
# person.errors.messages # => { :name => ["can not be nil"] }
# person.errors.include?(:name) # => true
# person.errors.include?(:age) # => false
- def include?(error)
- (v = messages[error]) && v.any?
+ def include?(attribute)
+ (v = messages[attribute]) && v.any?
end
# aliases include?
alias :has_key? :include?
diff --git a/activemodel/lib/active_model/mass_assignment_security/permission_set.rb b/activemodel/lib/active_model/mass_assignment_security/permission_set.rb
index 415ab0ad17..f104d0306c 100644
--- a/activemodel/lib/active_model/mass_assignment_security/permission_set.rb
+++ b/activemodel/lib/active_model/mass_assignment_security/permission_set.rb
@@ -2,7 +2,7 @@ require 'set'
module ActiveModel
module MassAssignmentSecurity
- class PermissionSet < Set
+ class PermissionSet < Set #:nodoc:
def +(values)
super(values.compact.map(&:to_s))
@@ -23,14 +23,14 @@ module ActiveModel
end
end
- class WhiteList < PermissionSet
+ class WhiteList < PermissionSet #:nodoc:
def deny?(key)
!include?(key)
end
end
- class BlackList < PermissionSet
+ class BlackList < PermissionSet #:nodoc:
def deny?(key)
include?(key)
diff --git a/activemodel/lib/active_model/model.rb b/activemodel/lib/active_model/model.rb
index 1b2f0e6049..33a530e6bd 100644
--- a/activemodel/lib/active_model/model.rb
+++ b/activemodel/lib/active_model/model.rb
@@ -56,7 +56,7 @@ module ActiveModel
# refer to the specific modules included in <tt>ActiveModel::Model</tt>
# (see below).
module Model
- def self.included(base)
+ def self.included(base) #:nodoc:
base.class_eval do
extend ActiveModel::Naming
extend ActiveModel::Translation
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index cd596e37d2..55ea6be796 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -82,8 +82,7 @@ module ActiveModel
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
def validates_each(*attr_names, &block)
- options = attr_names.extract_options!.symbolize_keys
- validates_with BlockValidator, options.merge(:attributes => attr_names.flatten), &block
+ validates_with BlockValidator, _merge_attributes(attr_names), &block
end
# Adds a validation method or block to the class. This is useful when
diff --git a/activemodel/lib/active_model/validations/acceptance.rb b/activemodel/lib/active_model/validations/acceptance.rb
index 38abd0c1fa..43651094cf 100644
--- a/activemodel/lib/active_model/validations/acceptance.rb
+++ b/activemodel/lib/active_model/validations/acceptance.rb
@@ -2,9 +2,9 @@ module ActiveModel
# == Active Model Acceptance Validator
module Validations
- class AcceptanceValidator < EachValidator
+ class AcceptanceValidator < EachValidator #:nodoc:
def initialize(options)
- super(options.reverse_merge(:allow_nil => true, :accept => "1"))
+ super({ :allow_nil => true, :accept => "1" }.merge!(options))
end
def validate_each(record, attribute, value)
@@ -58,7 +58,7 @@ module ActiveModel
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
# The method, proc or string should return or evaluate to a true or
# false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_acceptance_of(*attr_names)
validates_with AcceptanceValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/confirmation.rb b/activemodel/lib/active_model/validations/confirmation.rb
index ede34d15bc..b6cf82fb19 100644
--- a/activemodel/lib/active_model/validations/confirmation.rb
+++ b/activemodel/lib/active_model/validations/confirmation.rb
@@ -2,7 +2,7 @@ module ActiveModel
# == Active Model Confirmation Validator
module Validations
- class ConfirmationValidator < EachValidator
+ class ConfirmationValidator < EachValidator #:nodoc:
def validate_each(record, attribute, value)
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
human_attribute_name = record.class.human_attribute_name(attribute)
@@ -59,7 +59,7 @@ module ActiveModel
# <tt>:unless => :skip_validation</tt>, or
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_confirmation_of(*attr_names)
validates_with ConfirmationValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb
index edd42d85f2..c8d7057606 100644
--- a/activemodel/lib/active_model/validations/exclusion.rb
+++ b/activemodel/lib/active_model/validations/exclusion.rb
@@ -4,7 +4,7 @@ module ActiveModel
# == Active Model Exclusion Validator
module Validations
- class ExclusionValidator < EachValidator
+ class ExclusionValidator < EachValidator #:nodoc:
include Clusivity
def validate_each(record, attribute, value)
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index ffdf842d94..d48987c482 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -2,7 +2,7 @@ module ActiveModel
# == Active Model Format Validator
module Validations
- class FormatValidator < EachValidator
+ class FormatValidator < EachValidator #:nodoc:
def validate_each(record, attribute, value)
if options[:with]
regexp = option_call(record, :with)
@@ -32,12 +32,12 @@ module ActiveModel
def record_error(record, attribute, name, value)
record.errors.add(attribute, :invalid, options.except(name).merge!(:value => value))
end
-
+
def regexp_using_multiline_anchors?(regexp)
regexp.source.start_with?("^") ||
(regexp.source.end_with?("$") && !regexp.source.end_with?("\\$"))
end
-
+
def check_options_validity(options, name)
option = options[name]
if option && !option.is_a?(Regexp) && !option.respond_to?(:call)
@@ -110,7 +110,7 @@ module ActiveModel
# if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
# or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
# method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
# * <tt>:multiline</tt> - Set to true if your regular expression contains
# anchors that match the beginning or end of lines as opposed to the
diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb
index 8810f2a3c1..154db5aedc 100644
--- a/activemodel/lib/active_model/validations/inclusion.rb
+++ b/activemodel/lib/active_model/validations/inclusion.rb
@@ -4,7 +4,7 @@ module ActiveModel
# == Active Model Inclusion Validator
module Validations
- class InclusionValidator < EachValidator
+ class InclusionValidator < EachValidator #:nodoc:
include Clusivity
def validate_each(record, attribute, value)
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index 64b4fe2d74..40ebe0cd2e 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -2,7 +2,7 @@ module ActiveModel
# == Active Model Length Validator
module Validations
- class LengthValidator < EachValidator
+ class LengthValidator < EachValidator #:nodoc:
MESSAGES = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long }.freeze
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb
index 40b5b92b84..1069ed3906 100644
--- a/activemodel/lib/active_model/validations/numericality.rb
+++ b/activemodel/lib/active_model/validations/numericality.rb
@@ -2,7 +2,7 @@ module ActiveModel
# == Active Model Numericality Validator
module Validations
- class NumericalityValidator < EachValidator
+ class NumericalityValidator < EachValidator #:nodoc:
CHECKS = { :greater_than => :>, :greater_than_or_equal_to => :>=,
:equal_to => :==, :less_than => :<, :less_than_or_equal_to => :<=,
:odd => :odd?, :even => :even?, :other_than => :!= }.freeze
diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb
index 018ef1e733..a7dcdbba3d 100644
--- a/activemodel/lib/active_model/validations/presence.rb
+++ b/activemodel/lib/active_model/validations/presence.rb
@@ -4,7 +4,7 @@ module ActiveModel
# == Active Model Presence Validator
module Validations
- class PresenceValidator < EachValidator
+ class PresenceValidator < EachValidator #:nodoc:
def validate(record)
record.errors.add_on_blank(attributes, options)
end
@@ -40,7 +40,7 @@ module ActiveModel
# if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
# or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
# proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 66cc9daa2c..3c516f8b22 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -3,12 +3,14 @@ module ActiveModel
module HelperMethods
private
def _merge_attributes(attr_names)
- options = attr_names.extract_options!
- options.merge(:attributes => attr_names.flatten)
+ options = attr_names.extract_options!.symbolize_keys
+ attr_names.flatten!
+ options[:attributes] = attr_names
+ options
end
end
- class WithValidator < EachValidator
+ class WithValidator < EachValidator #:nodoc:
def validate_each(record, attr, val)
method_name = options[:with]
@@ -61,7 +63,7 @@ module ActiveModel
# (e.g. <tt>:unless => :skip_validation</tt>, or
# <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>).
# The method, proc or string should return or evaluate to a true or false value.
- # * <tt>:strict</tt> - Specifies whether validation should be strict.
+ # * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
#
# If you pass any additional configuration options, they will be passed
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index ed5f29cfe4..aeb3773c99 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,40 @@
## Rails 4.0.0 (unreleased) ##
+* `ActiveRelation#inspect` no longer calls `#to_a`
+
+ *Brian Cardarella*
+
+* Add `collate` and `ctype` support to PostgreSQL. These are available for PostgreSQL 8.4 or later.
+ Example:
+
+ development:
+ adapter: postgresql
+ host: localhost
+ database: rails_development
+ username: foo
+ password: bar
+ encoding: UTF8
+ collate: ja_JP.UTF8
+ ctype: ja_JP.UTF8
+
+ *kennyj*
+
+* `FinderMethods#exists?` now returns `false` with the `false` argument.
+
+ *Egor Lynko*
+
+* Added support for specifying the precision of a timestamp in the postgresql
+ adapter. So, instead of having to incorrectly specify the precision using the
+ `:limit` option, you may use `:precision`, as intended. For example, in a migration:
+
+ def change
+ create_table :foobars do |t|
+ t.timestamps :precision => 0
+ end
+ end
+
+ *Tony Schneider*
+
* Allow ActiveRecord::Relation#pluck to accept multiple columns. Returns an
array of arrays containing the typecasted values:
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index ab6d253ef8..5b41f72e52 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -158,11 +158,12 @@ module ActiveRecord
begin
send(name + "=", read_value_from_parameter(name, values_with_empty_parameters))
rescue => ex
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name}", ex, name)
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
end
end
unless errors.empty?
- raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
+ error_descriptions = errors.map { |ex| ex.message }.join(",")
+ raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
end
end
@@ -180,15 +181,27 @@ module ActiveRecord
end
def read_time_parameter_value(name, values_hash_from_param)
- # If Date bits were not provided, error
- raise "Missing Parameter" if [1,2,3].any?{|position| !values_hash_from_param.has_key?(position)}
- max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
- # If Date bits were provided but blank, then return nil
- return nil if (1..3).any? {|position| values_hash_from_param[position].blank?}
+ # If column is a :time (and not :date or :timestamp) there is no need to validate if
+ # there are year/month/day fields
+ if column_for_attribute(name).type == :time
+ # if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
+ {1 => 1970, 2 => 1, 3 => 1}.each do |key,value|
+ values_hash_from_param[key] ||= value
+ end
+ else
+ # else column is a timestamp, so if Date bits were not provided, error
+ if missing_parameter = [1,2,3].detect{ |position| !values_hash_from_param.has_key?(position) }
+ raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter}i)")
+ end
+
+ # If Date bits were provided but blank, then return nil
+ return nil if (1..3).any? { |position| values_hash_from_param[position].blank? }
+ end
- set_values = (1..max_position).collect{|position| values_hash_from_param[position] }
+ max_position = extract_max_param_for_multiparameter_attributes(values_hash_from_param, 6)
+ set_values = (1..max_position).collect{ |position| values_hash_from_param[position] }
# If Time bits are not there, then default to 0
- (3..5).each {|i| set_values[i] = set_values[i].blank? ? 0 : set_values[i]}
+ (3..5).each { |i| set_values[i] = set_values[i].blank? ? 0 : set_values[i] }
instantiate_time_object(name, set_values)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 17377bad96..3b4537aab4 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -2,7 +2,7 @@ module ActiveRecord
module ConnectionAdapters # :nodoc:
module QueryCache
class << self
- def included(base)
+ def included(base) #:nodoc:
dirties_query_cache base, :insert, :update, :delete
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 921278d145..df4a9d5afc 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -72,6 +72,8 @@ module ActiveRecord
when /^mediumint/i; 3
when /^smallint/i; 2
when /^tinyint/i; 1
+ when /^enum\((.+)\)/i
+ $1.split(',').map{|enum| enum.strip.length - 2}.max
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 8491d42b86..dd40351a38 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -1,3 +1,5 @@
+require 'uri'
+
module ActiveRecord
module ConnectionAdapters
class ConnectionSpecification #:nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 43e243581c..507e937c3e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -187,6 +187,7 @@ module ActiveRecord
case sql_type
when /^bigint/i; 8
when /^smallint/i; 2
+ when /^timestamp/i; nil
else super
end
end
@@ -201,6 +202,8 @@ module ActiveRecord
def extract_precision(sql_type)
if sql_type == 'money'
self.class.money_precision
+ elsif sql_type =~ /timestamp/i
+ $1.to_i if sql_type =~ /\((\d+)\)/
else
super
end
@@ -913,7 +916,8 @@ module ActiveRecord
end
# Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
- # <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
+ # <tt>:encoding</tt>, <tt>:collate</tt>, <tt>:ctype</tt>,
+ # <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
# <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
#
# Example:
@@ -930,6 +934,10 @@ module ActiveRecord
" TEMPLATE = \"#{value}\""
when :encoding
" ENCODING = '#{value}'"
+ when :collate
+ " LC_COLLATE = '#{value}'"
+ when :ctype
+ " LC_CTYPE = '#{value}'"
when :tablespace
" TABLESPACE = \"#{value}\""
when :connection_limit
@@ -1056,6 +1064,20 @@ module ActiveRecord
end_sql
end
+ # Returns the current database collate.
+ def collate
+ query(<<-end_sql, 'SCHEMA')[0][0]
+ SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
+ end_sql
+ end
+
+ # Returns the current database ctype.
+ def ctype
+ query(<<-end_sql, 'SCHEMA')[0][0]
+ SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
+ end_sql
+ end
+
# Returns an array of schema names.
def schema_names
query(<<-SQL, 'SCHEMA').flatten
@@ -1285,6 +1307,13 @@ module ActiveRecord
when 5..8; 'bigint'
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
end
+ when 'datetime'
+ return super unless precision
+
+ case precision
+ when 0..6; "timestamp(#{precision})"
+ else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
+ end
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index a0c7e559ce..57aa47ab61 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -191,7 +191,7 @@ module ActiveRecord
:decimal => { :name => "decimal" },
:datetime => { :name => "datetime" },
:timestamp => { :name => "datetime" },
- :time => { :name => "datetime" },
+ :time => { :name => "time" },
:date => { :name => "date" },
:binary => { :name => "blob" },
:boolean => { :name => "boolean" }
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 858b667e22..5f157fde6d 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -106,13 +106,11 @@ module ActiveRecord
attr_reader :record, :attempted_action
def initialize(record, attempted_action)
+ super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
@record = record
@attempted_action = attempted_action
end
- def message
- "Attempted to #{attempted_action} a stale object: #{record.class.name}"
- end
end
# Raised when association is being configured improperly or
@@ -168,9 +166,9 @@ module ActiveRecord
class AttributeAssignmentError < ActiveRecordError
attr_reader :exception, :attribute
def initialize(message, exception, attribute)
+ super(message)
@exception = exception
@attribute = attribute
- @message = message
end
end
@@ -189,12 +187,10 @@ module ActiveRecord
attr_reader :model
def initialize(model)
+ super("Unknown primary key for table #{model.table_name} in model #{model}.")
@model = model
end
- def message
- "Unknown primary key for table #{model.table_name} in model #{model}."
- end
end
class ImmutableRelation < ActiveRecordError
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 8199b5c2e0..78ecb1cdc5 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -35,7 +35,7 @@ db_namespace = namespace :db do
ActiveRecord::Tasks::DatabaseTasks.drop_current
end
- desc "Migrate the database (options: VERSION=x, VERBOSE=false)."
+ desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
task :migrate => [:environment, :load_config] do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
@@ -148,13 +148,10 @@ db_namespace = namespace :db do
# desc "Retrieves the collation for the current environment's database"
task :collation => [:environment, :load_config] do
- config = ActiveRecord::Base.configurations[Rails.env || 'development']
- case config['adapter']
- when /mysql/
- ActiveRecord::Base.establish_connection(config)
- puts ActiveRecord::Base.connection.collation
- else
- $stderr.puts 'sorry, your database adapter is not supported yet, feel free to submit a patch'
+ begin
+ puts ActiveRecord::Tasks::DatabaseTasks.collation_current
+ rescue NoMethodError
+ $stderr.puts 'Sorry, your database adapter is not supported yet, feel free to submit a patch'
end
end
@@ -270,6 +267,15 @@ db_namespace = namespace :db do
end
namespace :structure do
+ def set_firebird_env(config)
+ ENV['ISC_USER'] = config['username'].to_s if config['username']
+ ENV['ISC_PASSWORD'] = config['password'].to_s if config['password']
+ end
+
+ def firebird_db_string(config)
+ FireRuby::Database.db_string_for(config.symbolize_keys)
+ end
+
desc 'Dump the database structure to db/structure.sql. Specify another file with DB_STRUCTURE=db/my_structure.sql'
task :dump => [:environment, :load_config] do
abcs = ActiveRecord::Base.configurations
@@ -304,7 +310,7 @@ db_namespace = namespace :db do
filename = ENV['DB_STRUCTURE'] || File.join(Rails.root, "db", "structure.sql")
case abcs[env]['adapter']
when /mysql/, /postgresql/, /sqlite/
- ActiveRecord::Tasks::DatabaseTasks.structure_load(abcs[Rails.env], filename)
+ ActiveRecord::Tasks::DatabaseTasks.structure_load(abcs[env], filename)
when 'sqlserver'
`sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i #{filename}`
when 'oci', 'oracle'
@@ -338,6 +344,13 @@ db_namespace = namespace :db do
end
end
+ # desc "Recreate the test database from an existent schema.rb file"
+ task :load_schema => 'db:test:purge' do
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
+ ActiveRecord::Schema.verbose = false
+ db_namespace["schema:load"].invoke
+ end
+
# desc "Recreate the test database from an existent structure.sql file"
task :load_structure => 'db:test:purge' do
begin
@@ -348,15 +361,18 @@ db_namespace = namespace :db do
end
end
- # desc "Recreate the test database from an existent schema.rb file"
- task :load_schema => 'db:test:purge' do
- ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
- ActiveRecord::Schema.verbose = false
- db_namespace["schema:load"].invoke
+ # desc "Recreate the test database from a fresh schema"
+ task :clone do
+ case ActiveRecord::Base.schema_format
+ when :ruby
+ db_namespace["test:clone_schema"].invoke
+ when :sql
+ db_namespace["test:clone_structure"].invoke
+ end
end
# desc "Recreate the test database from a fresh schema.rb file"
- task :clone => %w(db:schema:dump db:test:load_schema)
+ task :clone_schema => ["db:schema:dump", "db:test:load_schema"]
# desc "Recreate the test database from a fresh structure.sql file"
task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ]
@@ -389,7 +405,7 @@ db_namespace = namespace :db do
# desc 'Check for pending migrations and load the test schema'
task :prepare => 'db:abort_if_pending_migrations' do
unless ActiveRecord::Base.configurations.blank?
- db_namespace[{ :sql => 'test:clone_structure', :ruby => 'test:load' }[ActiveRecord::Base.schema_format]].invoke
+ db_namespace['test:load'].invoke
end
end
end
@@ -405,7 +421,7 @@ db_namespace = namespace :db do
# desc "Clear the sessions table"
task :clear => [:environment, :load_config] do
- ActiveRecord::Base.connection.execute "DELETE FROM #{session_table_name}"
+ ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::SessionStore::Session.table_name}"
end
end
end
@@ -416,7 +432,7 @@ namespace :railties do
task :migrations => :'db:load_config' do
to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip }
railties = {}
- Rails.application.railties.all do |railtie|
+ Rails.application.railties.each do |railtie|
next unless to_load == :all || to_load.include?(railtie.railtie_name)
if railtie.respond_to?(:paths) && (path = railtie.paths['db/migrate'].first)
@@ -440,15 +456,3 @@ end
task 'test:prepare' => 'db:test:prepare'
-def session_table_name
- ActiveRecord::SessionStore::Session.table_name
-end
-
-def set_firebird_env(config)
- ENV['ISC_USER'] = config['username'].to_s if config['username']
- ENV['ISC_PASSWORD'] = config['password'].to_s if config['password']
-end
-
-def firebird_db_string(config)
- FireRuby::Database.db_string_for(config.symbolize_keys)
-end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index fe3aa00a74..e268d451e0 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -492,10 +492,6 @@ module ActiveRecord
end
end
- def inspect
- to_a.inspect
- end
-
def pretty_print(q)
q.pp(self.to_a)
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 86eb8f35b5..e40b958b54 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -262,10 +262,16 @@ module ActiveRecord
end
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
- group_attr = group_values
- association = @klass.reflect_on_association(group_attr.first.to_sym)
- associated = group_attr.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
- group_fields = Array(associated ? association.foreign_key : group_attr)
+ group_attrs = group_values
+
+ if group_attrs.first.respond_to?(:to_sym)
+ association = @klass.reflect_on_association(group_attrs.first.to_sym)
+ associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
+ group_fields = Array(associated ? association.foreign_key : group_attrs)
+ else
+ group_fields = group_attrs
+ end
+
group_aliases = group_fields.map { |field| column_alias_for(field) }
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
[aliaz, column_for(field)]
@@ -288,10 +294,14 @@ module ActiveRecord
select_values += select_values unless having_values.empty?
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
- "#{field} AS #{aliaz}"
+ if field.respond_to?(:as)
+ field.as(aliaz)
+ else
+ "#{field} AS #{aliaz}"
+ end
}
- relation = except(:group).group(group.join(','))
+ relation = except(:group).group(group)
relation.select_values = select_values
calculated_data = @klass.connection.select_all(relation, nil, bind_values)
@@ -303,10 +313,10 @@ module ActiveRecord
end
Hash[calculated_data.map do |row|
- key = group_columns.map { |aliaz, column|
+ key = group_columns.map { |aliaz, column|
type_cast_calculated_value(row[aliaz], column)
}
- key = key.first if key.size == 1
+ key = key.first if key.size == 1
key = key_records[key] if associated
[key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
end]
@@ -321,6 +331,7 @@ module ActiveRecord
# column_alias_for("count(*)") # => "count_all"
# column_alias_for("count", "id") # => "count_id"
def column_alias_for(*keys)
+ keys.map! {|k| k.respond_to?(:to_sql) ? k.to_sql : k}
table_name = keys.join(' ')
table_name.downcase!
table_name.gsub!(/\*/, 'all')
@@ -332,7 +343,7 @@ module ActiveRecord
end
def column_for(field)
- field_name = field.to_s.split('.').last
+ field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
@klass.columns.detect { |c| c.name.to_s == field_name }
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index eb901727de..974cd326ef 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -172,19 +172,19 @@ module ActiveRecord
# Person.exists?(name: 'David')
# Person.exists?(false)
# Person.exists?
- def exists?(id = false)
- id = id.id if ActiveRecord::Model === id
- return false if id.nil?
+ def exists?(conditions = :none)
+ conditions = conditions.id if ActiveRecord::Model === conditions
+ return false if !conditions
join_dependency = construct_join_dependency_for_association_find
relation = construct_relation_for_association_find(join_dependency)
relation = relation.except(:select, :order).select("1 AS one").limit(1)
- case id
+ case conditions
when Array, Hash
- relation = relation.where(id)
+ relation = relation.where(conditions)
else
- relation = relation.where(table[primary_key].eq(id)) if id
+ relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
end
connection.select_value(relation, "#{name} Exists", relation.bind_values)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 5e5aca0396..6f49548aab 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -83,7 +83,9 @@ module ActiveRecord
end
def references!(*args)
- self.references_values = (references_values + args.flatten.map(&:to_s)).uniq
+ args.flatten!
+
+ self.references_values = (references_values + args.map!(&:to_s)).uniq
self
end
@@ -134,7 +136,9 @@ module ActiveRecord
end
def group!(*args)
- self.group_values += args.flatten
+ args.flatten!
+
+ self.group_values += args
self
end
@@ -143,11 +147,10 @@ module ActiveRecord
end
def order!(*args)
- args = args.flatten
+ args.flatten!
references = args.reject { |arg| Arel::Node === arg }
- .map { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }
- .compact
+ references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
references!(references) if references.any?
self.order_values += args
@@ -168,8 +171,10 @@ module ActiveRecord
end
def reorder!(*args)
+ args.flatten!
+
self.reordering_value = true
- self.order_values = args.flatten
+ self.order_values = args
self
end
@@ -327,7 +332,7 @@ module ActiveRecord
#
# Should be used with order.
#
- # User.offset(10).order("name ASC")
+ # User.offset(10).order("name ASC")
def offset(value)
spawn.offset!(value)
end
@@ -410,10 +415,10 @@ module ActiveRecord
#
# Can accept other relation objects. For example:
#
- # Topic.select('title').from(Topics.approved)
+ # Topic.select('title').from(Topic.approved)
# # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
#
- # Topics.select('a.title').from(Topics.approved, :a)
+ # Topic.select('a.title').from(Topic.approved, :a)
# # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
#
def from(value, subquery_name = nil)
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 999b2ebc85..f1241502f5 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -56,6 +56,15 @@ module ActiveRecord
class_for_adapter(configuration['adapter']).new(*arguments).charset
end
+ def collation_current(environment = Rails.env)
+ collation ActiveRecord::Base.configurations[environment]
+ end
+
+ def collation(*arguments)
+ configuration = arguments.first
+ class_for_adapter(configuration['adapter']).new(*arguments).collation
+ end
+
def purge(configuration)
class_for_adapter(configuration['adapter']).new(configuration).purge
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index b39cd2282f..bf62dfd5b5 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -44,6 +44,10 @@ module ActiveRecord
connection.charset
end
+ def collation
+ connection.collation
+ end
+
def structure_dump(filename)
establish_connection configuration
File.open(filename, "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump }
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index a210392e53..4139460273 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -29,6 +29,10 @@ module ActiveRecord
connection.encoding
end
+ def collation
+ connection.collate
+ end
+
def purge
clear_active_connections!
drop
diff --git a/activerecord/lib/rails/generators/active_record/model/templates/model.rb b/activerecord/lib/rails/generators/active_record/model/templates/model.rb
index d56f9f57a4..2cca17b94f 100644
--- a/activerecord/lib/rails/generators/active_record/model/templates/model.rb
+++ b/activerecord/lib/rails/generators/active_record/model/templates/model.rb
@@ -1,7 +1,7 @@
<% module_namespacing do -%>
class <%= class_name %> < <%= parent_class_name.classify %>
<% attributes.select {|attr| attr.reference? }.each do |attribute| -%>
- belongs_to :<%= attribute.name %>
+ belongs_to :<%= attribute.name %><%= ', polymorphic: true' if attribute.polymorphic? %>
<% end -%>
<% if !accessible_attributes.empty? -%>
attr_accessible <%= accessible_attributes.map {|a| ":#{a.name}" }.sort.join(', ') %>
diff --git a/activerecord/test/cases/adapters/mysql/enum_test.rb b/activerecord/test/cases/adapters/mysql/enum_test.rb
new file mode 100644
index 0000000000..40af317ad1
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql/enum_test.rb
@@ -0,0 +1,10 @@
+require "cases/helper"
+
+class MysqlEnumTest < ActiveRecord::TestCase
+ class EnumTest < ActiveRecord::Base
+ end
+
+ def test_enum_limit
+ assert_equal 5, EnumTest.columns.first.limit
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
new file mode 100644
index 0000000000..f3a05e48ad
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -0,0 +1,10 @@
+require "cases/helper"
+
+class Mysql2EnumTest < ActiveRecord::TestCase
+ class EnumTest < ActiveRecord::Base
+ end
+
+ def test_enum_limit
+ assert_equal 5, EnumTest.columns.first.limit
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index 447d729e52..148584214e 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -21,6 +21,10 @@ class PostgresqlActiveSchemaTest < ActiveRecord::TestCase
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
end
+ def test_create_database_with_collate_and_ctype
+ assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'UTF8' LC_COLLATE = 'ja_JP.UTF8' LC_CTYPE = 'ja_JP.UTF8'), create_database(:aimonetti, :encoding => :"UTF8", :collate => :"ja_JP.UTF8", :ctype => :"ja_JP.UTF8")
+ end
+
def test_add_index
# add_index calls index_name_exists? which can't work since execute is stubbed
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_exists?) do |*|
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index f6e168caf2..9b5c5eb72f 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -21,6 +21,14 @@ module ActiveRecord
assert_not_nil @connection.encoding
end
+ def test_collate
+ assert_not_nil @connection.collate
+ end
+
+ def test_ctype
+ assert_not_nil @connection.ctype
+ end
+
def test_default_client_min_messages
assert_equal "warning", @connection.client_min_messages
end
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index 337f43c421..26507ad654 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -27,4 +27,69 @@ class TimestampTest < ActiveRecord::TestCase
d = Developer.create!(:name => 'aaron', :updated_at => -1.0 / 0.0)
assert_equal(-1.0 / 0.0, d.updated_at)
end
+
+ def test_default_datetime_precision
+ ActiveRecord::Base.connection.create_table(:foos)
+ ActiveRecord::Base.connection.add_column :foos, :created_at, :datetime
+ ActiveRecord::Base.connection.add_column :foos, :updated_at, :datetime
+ assert_nil activerecord_column_option('foos', 'created_at', 'precision')
+ end
+
+ def test_timestamp_data_type_with_precision
+ ActiveRecord::Base.connection.create_table(:foos)
+ ActiveRecord::Base.connection.add_column :foos, :created_at, :datetime, :precision => 0
+ ActiveRecord::Base.connection.add_column :foos, :updated_at, :datetime, :precision => 5
+ assert_equal 0, activerecord_column_option('foos', 'created_at', 'precision')
+ assert_equal 5, activerecord_column_option('foos', 'updated_at', 'precision')
+ end
+
+ def test_timestamps_helper_with_custom_precision
+ ActiveRecord::Base.connection.create_table(:foos) do |t|
+ t.timestamps :precision => 4
+ end
+ assert_equal 4, activerecord_column_option('foos', 'created_at', 'precision')
+ assert_equal 4, activerecord_column_option('foos', 'updated_at', 'precision')
+ end
+
+ def test_passing_precision_to_timestamp_does_not_set_limit
+ ActiveRecord::Base.connection.create_table(:foos) do |t|
+ t.timestamps :precision => 4
+ end
+ assert_nil activerecord_column_option("foos", "created_at", "limit")
+ assert_nil activerecord_column_option("foos", "updated_at", "limit")
+ end
+
+ def test_invalid_timestamp_precision_raises_error
+ assert_raises ActiveRecord::ActiveRecordError do
+ ActiveRecord::Base.connection.create_table(:foos) do |t|
+ t.timestamps :precision => 7
+ end
+ end
+ end
+
+ def test_postgres_agrees_with_activerecord_about_precision
+ ActiveRecord::Base.connection.create_table(:foos) do |t|
+ t.timestamps :precision => 4
+ end
+ assert_equal '4', pg_datetime_precision('foos', 'created_at')
+ assert_equal '4', pg_datetime_precision('foos', 'updated_at')
+ end
+
+ private
+
+ def pg_datetime_precision(table_name, column_name)
+ results = ActiveRecord::Base.connection.execute("SELECT column_name, datetime_precision FROM information_schema.columns WHERE table_name ='#{table_name}'")
+ result = results.find do |result_hash|
+ result_hash["column_name"] == column_name
+ end
+ result && result["datetime_precision"]
+ end
+
+ def activerecord_column_option(tablename, column_name, option)
+ result = ActiveRecord::Base.connection.columns(tablename).find do |column|
+ column.name == column_name
+ end
+ result && result.send(option)
+ end
+
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 5e947799cc..4e26c5dda1 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -24,7 +24,7 @@ module ActiveRecord
@conn.extend(LogIntercepter)
@conn.intercepted = true
end
-
+
def teardown
@conn.intercepted = false
@conn.logged = []
@@ -43,11 +43,6 @@ module ActiveRecord
assert(!result.rows.first.include?("blob"), "should not store blobs")
end
- def test_time_column
- owner = Owner.create!(:eats_at => Time.utc(1995,1,1,6,0))
- assert_match(/1995-01-01/, owner.reload.eats_at.to_s)
- end
-
def test_exec_insert
column = @conn.columns('items').find { |col| col.name == 'number' }
vals = [[column, 10]]
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index b8481175b2..3ea6201d60 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -193,7 +193,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_no_sql_should_be_fired_if_association_already_loaded
Car.create(:name => 'honda')
bulbs = Car.first.bulbs
- bulbs.inspect # to load all instances of bulbs
+ bulbs.to_a # to load all instances of bulbs
assert_no_queries do
bulbs.first()
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 1093fedea1..fe385feb4a 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -792,6 +792,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
private
+
def cached_columns
Topic.columns.find_all { |column|
!Topic.serialized_attributes.include? column.name
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 2fee553cef..e34f505a02 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -81,6 +81,12 @@ end
class BasicsTest < ActiveRecord::TestCase
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
+ def setup
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ ActiveRecord::Base.default_timezone = :local
+ Time.zone = nil
+ end
+
def test_generated_methods_modules
modules = Computer.ancestors
assert modules.include?(Computer::GeneratedFeatureMethods)
@@ -504,7 +510,7 @@ class BasicsTest < ActiveRecord::TestCase
end
# Oracle, and Sybase do not have a TIME datatype.
- unless current_adapter?(:OracleAdapter, :SybaseAdapter, :SQLite3Adapter)
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
def test_utc_as_time_zone
Topic.default_timezone = :utc
attributes = { "bonus_time" => "5:42:00AM" }
@@ -686,7 +692,7 @@ class BasicsTest < ActiveRecord::TestCase
}
topic = Topic.find(1)
topic.attributes = attributes
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
end
def test_multiparameter_attributes_on_time_with_no_date
@@ -746,9 +752,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing
- ActiveRecord::Base.time_zone_aware_attributes = false
- ActiveRecord::Base.default_timezone = :local
- Time.zone = nil
attributes = {
"written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12",
"written_on(5i)" => "12", "written_on(6i)" => "02"
@@ -796,8 +799,6 @@ class BasicsTest < ActiveRecord::TestCase
topic = Topic.find(1)
topic.attributes = attributes
assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
- ensure
- ActiveRecord::Base.default_timezone = :local
end
def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
@@ -813,14 +814,9 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
assert_equal Time.zone, topic.written_on.time_zone
- ensure
- ActiveRecord::Base.time_zone_aware_attributes = false
- ActiveRecord::Base.default_timezone = :local
- Time.zone = nil
end
def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
- ActiveRecord::Base.time_zone_aware_attributes = false
Time.zone = ActiveSupport::TimeZone[-28800]
attributes = {
"written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
@@ -830,8 +826,6 @@ class BasicsTest < ActiveRecord::TestCase
topic.attributes = attributes
assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
assert_equal false, topic.written_on.respond_to?(:time_zone)
- ensure
- Time.zone = nil
end
def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
@@ -848,14 +842,11 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
assert_equal false, topic.written_on.respond_to?(:time_zone)
ensure
- ActiveRecord::Base.time_zone_aware_attributes = false
- ActiveRecord::Base.default_timezone = :local
- Time.zone = nil
Topic.skip_time_zone_conversion_for_attributes = []
end
# Oracle, and Sybase do not have a TIME datatype.
- unless current_adapter?(:OracleAdapter, :SybaseAdapter, :SQLite3Adapter)
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
ActiveRecord::Base.time_zone_aware_attributes = true
ActiveRecord::Base.default_timezone = :utc
@@ -868,17 +859,10 @@ class BasicsTest < ActiveRecord::TestCase
topic.attributes = attributes
assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
assert topic.bonus_time.utc?
- ensure
- ActiveRecord::Base.time_zone_aware_attributes = false
- ActiveRecord::Base.default_timezone = :local
- Time.zone = nil
end
end
def test_multiparameter_attributes_on_time_with_empty_seconds
- ActiveRecord::Base.time_zone_aware_attributes = false
- ActiveRecord::Base.default_timezone = :local
- Time.zone = nil
attributes = {
"written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
"written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
@@ -888,36 +872,51 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
end
- def test_multiparameter_assignment_of_aggregation_with_missing_values
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(2)" => address.city, "address(3)" => address.country }
- customer.attributes = attributes
- end
- assert_equal("address", ex.errors[0].attribute)
+ def test_multiparameter_attributes_setting_time_attribute
+ return skip "Oracle does not have TIME data type" if current_adapter? :OracleAdapter
+
+ topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" )
+ assert_equal 1, topic.bonus_time.hour
+ assert_equal 5, topic.bonus_time.min
end
- def test_multiparameter_assignment_of_aggregation_with_large_index
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country }
- customer.attributes = attributes
+ def test_multiparameter_attributes_setting_date_attribute
+ topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" )
+ assert_equal 1952, topic.written_on.year
+ assert_equal 3, topic.written_on.month
+ assert_equal 11, topic.written_on.day
+ end
+
+ def test_multiparameter_attributes_setting_date_and_time_attribute
+ topic = Topic.new(
+ "written_on(1i)" => "1952",
+ "written_on(2i)" => "3",
+ "written_on(3i)" => "11",
+ "written_on(4i)" => "13",
+ "written_on(5i)" => "55")
+ assert_equal 1952, topic.written_on.year
+ assert_equal 3, topic.written_on.month
+ assert_equal 11, topic.written_on.day
+ assert_equal 13, topic.written_on.hour
+ assert_equal 55, topic.written_on.min
+ end
+
+ def test_multiparameter_attributes_setting_time_but_not_date_on_date_field
+ assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do
+ Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" )
end
- assert_equal("address", ex.errors[0].attribute)
end
def test_attributes_on_dummy_time
# Oracle, and Sybase do not have a TIME datatype.
- return true if current_adapter?(:OracleAdapter, :SybaseAdapter, :SQLite3Adapter)
+ return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
attributes = {
"bonus_time" => "5:42:00AM"
}
topic = Topic.find(1)
topic.attributes = attributes
- assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
end
def test_boolean
@@ -1882,8 +1881,6 @@ class BasicsTest < ActiveRecord::TestCase
est_key = Developer.first.cache_key
assert_equal utc_key, est_key
- ensure
- ActiveRecord::Base.time_zone_aware_attributes = false
end
def test_cache_key_format_for_existing_record_with_updated_at
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 4df613488a..e1c1e449ef 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -58,7 +58,16 @@ class CalculationsTest < ActiveRecord::TestCase
def test_should_group_by_field
c = Account.group(:firm_id).sum(:credit_limit)
- [1,6,2].each { |firm_id| assert c.keys.include?(firm_id) }
+ [1,6,2].each do |firm_id|
+ assert c.keys.include?(firm_id), "Group #{c.inspect} does not contain firm_id #{firm_id}"
+ end
+ end
+
+ def test_should_group_by_arel_attribute
+ c = Account.group(Account.arel_table[:firm_id]).sum(:credit_limit)
+ [1,6,2].each do |firm_id|
+ assert c.keys.include?(firm_id), "Group #{c.inspect} does not contain firm_id #{firm_id}"
+ end
end
def test_should_group_by_multiple_fields
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index e62d984ebd..576a455f09 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -55,6 +55,10 @@ class FinderTest < ActiveRecord::TestCase
assert Topic.exists?
end
+ def test_exists_returns_false_with_false_arg
+ assert !Topic.exists?(false)
+ end
+
# exists? should handle nil for id's that come from URLs and always return false
# (example: Topic.exists?(params[:id])) where params[:id] is nil
def test_exists_with_nil_arg
diff --git a/activerecord/test/cases/inclusion_test.rb b/activerecord/test/cases/inclusion_test.rb
index 8726ba5e51..9b9c09d2d8 100644
--- a/activerecord/test/cases/inclusion_test.rb
+++ b/activerecord/test/cases/inclusion_test.rb
@@ -4,7 +4,7 @@ require 'models/teapot'
class BasicInclusionModelTest < ActiveRecord::TestCase
def test_basic_model
Teapot.create!(:name => "Ronnie Kemper")
- assert_equal "Ronnie Kemper", Teapot.find(1).name
+ assert_equal "Ronnie Kemper", Teapot.first.name
end
def test_initialization
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index bac3376c9f..6631dce08f 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -305,6 +305,58 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal Client, Firm.reflect_on_association(:unsorted_clients_with_symbol).klass
end
+ def test_join_table
+ category = Struct.new(:table_name, :pluralize_table_names).new('categories', true)
+ product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
+
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, {}, product)
+ reflection.stubs(:klass).returns(category)
+ assert_equal 'categories_products', reflection.join_table
+
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, {}, category)
+ reflection.stubs(:klass).returns(product)
+ assert_equal 'categories_products', reflection.join_table
+ end
+
+ def test_join_table_with_common_prefix
+ category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true)
+ product = Struct.new(:table_name, :pluralize_table_names).new('catalog_products', true)
+
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, {}, product)
+ reflection.stubs(:klass).returns(category)
+ assert_equal 'catalog_categories_products', reflection.join_table
+
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, {}, category)
+ reflection.stubs(:klass).returns(product)
+ assert_equal 'catalog_categories_products', reflection.join_table
+ end
+
+ def test_join_table_with_different_prefix
+ category = Struct.new(:table_name, :pluralize_table_names).new('catalog_categories', true)
+ page = Struct.new(:table_name, :pluralize_table_names).new('content_pages', true)
+
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, {}, page)
+ reflection.stubs(:klass).returns(category)
+ assert_equal 'catalog_categories_content_pages', reflection.join_table
+
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :pages, {}, category)
+ reflection.stubs(:klass).returns(page)
+ assert_equal 'catalog_categories_content_pages', reflection.join_table
+ end
+
+ def test_join_table_can_be_overridden
+ category = Struct.new(:table_name, :pluralize_table_names).new('categories', true)
+ product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
+
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :categories, { :join_table => 'product_categories' }, product)
+ reflection.stubs(:klass).returns(category)
+ assert_equal 'product_categories', reflection.join_table
+
+ reflection = AssociationReflection.new(:has_and_belongs_to_many, :products, { :join_table => 'product_categories' }, category)
+ reflection.stubs(:klass).returns(product)
+ assert_equal 'product_categories', reflection.join_table
+ end
+
private
def assert_reflection(klass, association, options)
assert reflection = klass.reflect_on_association(association)
diff --git a/activerecord/test/cases/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 5f36b2c841..8c96a8aaa1 100644
--- a/activerecord/test/cases/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -251,6 +251,17 @@ module ActiveRecord
end
end
+ class DatabaseTasksCollationTest < ActiveRecord::TestCase
+ include DatabaseTasksSetupper
+
+ ADAPTERS_TASKS.each do |k, v|
+ define_method("test_#{k}_collation") do
+ eval("@#{v}").expects(:collation)
+ ActiveRecord::Tasks::DatabaseTasks.collation 'adapter' => k
+ end
+ end
+ end
+
class DatabaseTasksStructureDumpTest < ActiveRecord::TestCase
include DatabaseTasksSetupper
diff --git a/activerecord/test/cases/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 42a11b0171..9a0eb423bd 100644
--- a/activerecord/test/cases/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -195,6 +195,24 @@ module ActiveRecord
end
end
+ class MysqlDBCollationTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(:create_database => true)
+ @configuration = {
+ 'adapter' => 'mysql',
+ 'database' => 'my-app-db'
+ }
+
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
+
+ def test_db_retrieves_collation
+ @connection.expects(:collation)
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ end
+ end
+
class MySQLStructureDumpTest < ActiveRecord::TestCase
def setup
@connection = stub(:structure_dump => true)
diff --git a/activerecord/test/cases/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index e8769bd4df..df1075d816 100644
--- a/activerecord/test/cases/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -38,6 +38,14 @@ module ActiveRecord
merge('encoding' => 'latin')
end
+ def test_creates_database_with_given_collate_and_ctype
+ @connection.expects(:create_database).
+ with('my-app-db', @configuration.merge('encoding' => 'utf8', 'collate' => 'ja_JP.UTF8', 'ctype' => 'ja_JP.UTF8'))
+
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.
+ merge('collate' => 'ja_JP.UTF8', 'ctype' => 'ja_JP.UTF8')
+ end
+
def test_establishes_connection_to_new_database
ActiveRecord::Base.expects(:establish_connection).with(@configuration)
@@ -151,6 +159,24 @@ module ActiveRecord
end
end
+ class PostgreSQLDBCollationTest < ActiveRecord::TestCase
+ def setup
+ @connection = stub(:create_database => true)
+ @configuration = {
+ 'adapter' => 'postgresql',
+ 'database' => 'my-app-db'
+ }
+
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
+
+ def test_db_retrieves_collation
+ @connection.expects(:collate)
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ end
+ end
+
class PostgreSQLStructureDumpTest < ActiveRecord::TestCase
def setup
@connection = stub(:structure_dump => true)
diff --git a/activerecord/test/cases/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb
index b5557fc953..06a1d0ffc2 100644
--- a/activerecord/test/cases/sqlite_rake_test.rb
+++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb
@@ -124,6 +124,27 @@ module ActiveRecord
end
end
+ class SqliteDBCollationTest < ActiveRecord::TestCase
+ def setup
+ @database = 'db_create.sqlite3'
+ @connection = stub :connection
+ @configuration = {
+ 'adapter' => 'sqlite3',
+ 'database' => @database
+ }
+
+ File.stubs(:exist?).returns(false)
+ ActiveRecord::Base.stubs(:connection).returns(@connection)
+ ActiveRecord::Base.stubs(:establish_connection).returns(true)
+ end
+
+ def test_db_retrieves_collation
+ assert_raise NoMethodError do
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration, '/rails/root'
+ end
+ end
+ end
+
class SqliteStructureDumpTest < ActiveRecord::TestCase
def setup
@database = "db_create.sqlite3"
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 9fefa9ad3a..43cde4ab73 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -64,7 +64,6 @@ class AuditLog < ActiveRecord::Base
belongs_to :unvalidated_developer, :class_name => 'Developer'
end
-DeveloperSalary = Struct.new(:amount)
class DeveloperWithBeforeDestroyRaise < ActiveRecord::Base
self.table_name = 'developers'
has_and_belongs_to_many :projects, :join_table => 'developers_projects', :foreign_key => 'developer_id'
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 65b6f9f227..24a43d7ece 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -32,4 +32,13 @@ CREATE TABLE collation_tests (
) CHARACTER SET utf8 COLLATE utf8_general_ci
SQL
+ ActiveRecord::Base.connection.execute <<-SQL
+DROP TABLE IF EXISTS enum_tests;
+SQL
+
+ ActiveRecord::Base.connection.execute <<-SQL
+CREATE TABLE enum_tests (
+ enum_column ENUM('true','false')
+)
+SQL
end
diff --git a/activerecord/test/schema/mysql_specific_schema.rb b/activerecord/test/schema/mysql_specific_schema.rb
index 7d324f98c4..802c08b819 100644
--- a/activerecord/test/schema/mysql_specific_schema.rb
+++ b/activerecord/test/schema/mysql_specific_schema.rb
@@ -43,4 +43,14 @@ CREATE TABLE collation_tests (
) CHARACTER SET utf8 COLLATE utf8_general_ci
SQL
+ ActiveRecord::Base.connection.execute <<-SQL
+DROP TABLE IF EXISTS enum_tests;
+SQL
+
+ ActiveRecord::Base.connection.execute <<-SQL
+CREATE TABLE enum_tests (
+ enum_column ENUM('true','false')
+)
+SQL
+
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index ba01ef35f0..00688eab37 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -437,7 +437,6 @@ ActiveRecord::Schema.define do
t.string :name
t.column :updated_at, :datetime
t.column :happy_at, :datetime
- t.column :eats_at, :time
t.string :essay_id
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 00fcfe2001..87b956e3fc 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* AS::Callbacks: deprecate usage of filter object with `#before` and `#after` methods as `around` callback. *Bogdan Gusiev*
+
* Add `Time#prev_quarter' and 'Time#next_quarter' short-hands for months_ago(3) and months_since(3). *SungHee Kang*
* Remove obsolete and unused `require_association` method from dependencies. *fxn*
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 0aa3efbb63..6cc875c69a 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -283,7 +283,8 @@ module ActiveSupport
filter.singleton_class.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{kind}(context, &block) filter(context, &block) end
RUBY_EVAL
- elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
+ elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around && !filter.respond_to?(:around)
+ ActiveSupport::Deprecation.warn("Filter object with #before and #after methods is deprecated. Define #around method instead.")
def filter.around(context)
should_continue = before(context)
yield if should_continue
diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb
index c94a8d99f4..b927b58a9a 100644
--- a/activesupport/lib/active_support/concern.rb
+++ b/activesupport/lib/active_support/concern.rb
@@ -56,7 +56,7 @@ module ActiveSupport
# these from +Host+ directly including +Foo+ in +Bar+:
#
# module Bar
- # include Foo
+ # include Foo
# def self.included(base)
# base.method_injected_by_foo
# end
@@ -94,9 +94,8 @@ module ActiveSupport
# class Host
# include Bar # works, Bar takes care now of its dependencies
# end
- #
module Concern
- def self.extended(base)
+ def self.extended(base) #:nodoc:
base.instance_variable_set("@_dependencies", [])
end
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 4fb8c7af3f..307ae40398 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -98,15 +98,15 @@ module ActiveSupport
names.each do |name|
raise NameError.new('invalid config attribute name') unless name =~ /^[_A-Za-z]\w*$/
- reader, line = "def #{name}; config.#{name}; end", __LINE__
- writer, line = "def #{name}=(value); config.#{name} = value; end", __LINE__
+ reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
+ writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
- singleton_class.class_eval reader, __FILE__, line
- singleton_class.class_eval writer, __FILE__, line
+ singleton_class.class_eval reader, __FILE__, reader_line
+ singleton_class.class_eval writer, __FILE__, writer_line
unless options[:instance_accessor] == false
- class_eval reader, __FILE__, line unless options[:instance_reader] == false
- class_eval writer, __FILE__, line unless options[:instance_writer] == false
+ class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
+ class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
end
end
end
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 1e0de651c7..d6ae031c0d 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -1,6 +1,5 @@
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/keys'
-require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/core_ext/string/inflections'
class Array
@@ -62,14 +61,10 @@ class Array
:last_word_connector => ', and '
}
if defined?(I18n)
- namespace = 'support.array.'
- default_connectors.each_key do |name|
- i18n_key = (namespace + name.to_s).to_sym
- default_connectors[name] = I18n.translate i18n_key, :locale => options[:locale]
- end
+ i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
+ default_connectors.merge!(i18n_connectors)
end
-
- options.reverse_merge! default_connectors
+ options = default_connectors.merge!(options)
case length
when 0
diff --git a/activesupport/lib/active_support/core_ext/hash/keys.rb b/activesupport/lib/active_support/core_ext/hash/keys.rb
index 8e728691c6..e753e36124 100644
--- a/activesupport/lib/active_support/core_ext/hash/keys.rb
+++ b/activesupport/lib/active_support/core_ext/hash/keys.rb
@@ -7,7 +7,7 @@ class Hash
# # => { "NAME" => "Rob", "AGE" => "28" }
def transform_keys
result = {}
- keys.each do |key|
+ each_key do |key|
result[yield(key)] = self[key]
end
result
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index 28c8b53b78..e8623a9c54 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -1,5 +1,6 @@
require 'active_support/duration'
require 'active_support/core_ext/time/conversions'
+require 'active_support/time_with_zone'
class Time
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb
index 48c39d9370..9decbaef32 100644
--- a/activesupport/lib/active_support/file_update_checker.rb
+++ b/activesupport/lib/active_support/file_update_checker.rb
@@ -101,7 +101,9 @@ module ActiveSupport
end
def updated_at(paths)
- @updated_at || paths.map { |path| File.mtime(path) }.max || Time.at(0)
+ time_now = Time.now
+ @updated_at || paths.map { |path| File.mtime(path) }.
+ reject { |time| time > time_now }.max || Time.at(0)
end
def compile_glob(hash)
diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb
index c04c2ed15b..ca2d8cb270 100644
--- a/activesupport/lib/active_support/inflections.rb
+++ b/activesupport/lib/active_support/inflections.rb
@@ -1,3 +1,5 @@
+require 'active_support/inflector/inflections'
+
module ActiveSupport
Inflector.inflections do |inflect|
inflect.plural(/$/, 's')
diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb
index 600e353812..c9e50a9462 100644
--- a/activesupport/lib/active_support/inflector/inflections.rb
+++ b/activesupport/lib/active_support/inflector/inflections.rb
@@ -2,6 +2,8 @@ require 'active_support/core_ext/array/prepend_and_append'
module ActiveSupport
module Inflector
+ extend self
+
# A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
# inflection rules.
#
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 2acc6ddee5..c14a43de0d 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -1,6 +1,7 @@
# encoding: utf-8
require 'active_support/inflector/inflections'
+require 'active_support/inflections'
module ActiveSupport
# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index c736041066..99f6489adb 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -7,9 +7,14 @@ module ActiveSupport
module NumberHelper
extend self
+ DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
+ -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
+
DEFAULT_CURRENCY_VALUES = { :format => "%u%n", :negative_format => "-%u%n", :unit => "$", :separator => ".", :delimiter => ",",
:precision => 2, :significant => false, :strip_insignificant_zeros => false }
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
+
# Formats a +number+ into a US phone number (e.g., (555)
# 123-9876). You can customize the format in the +options+ hash.
#
@@ -116,8 +121,7 @@ module ActiveSupport
number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
end
- formatted_number = format.gsub('%n', self.number_to_rounded(number, options)).gsub('%u', unit)
- formatted_number
+ format.gsub('%n', self.number_to_rounded(number, options)).gsub('%u', unit)
end
# Formats a +number+ as a percentage string (e.g., 65%). You can
@@ -160,9 +164,7 @@ module ActiveSupport
options = defaults.merge!(options)
format = options[:format] || "%n%"
-
- formatted_number = format.gsub('%n', self.number_to_rounded(number, options))
- formatted_number
+ format.gsub('%n', self.number_to_rounded(number, options))
end
# Formats a +number+ with grouped thousands using +delimiter+
@@ -242,10 +244,9 @@ module ActiveSupport
# number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
# # => 1.111,23
def number_to_rounded(number, options = {})
- options = options.symbolize_keys
-
return number unless valid_float?(number)
- number = Float(number)
+ number = Float(number)
+ options = options.symbolize_keys
defaults = format_translations('precision', options[:locale])
options = defaults.merge!(options)
@@ -254,7 +255,7 @@ module ActiveSupport
significant = options.delete :significant
strip_insignificant_zeros = options.delete :strip_insignificant_zeros
- if significant and precision > 0
+ if significant && precision > 0
if number == 0
digits, rounded_number = 1, 0
else
@@ -263,10 +264,10 @@ module ActiveSupport
digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
end
precision -= digits
- precision = precision > 0 ? precision : 0 #don't let it be negative
+ precision = 0 if precision < 0 # don't let it be negative
else
rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
- rounded_number = rounded_number.zero? ? rounded_number.abs : rounded_number #prevent showing negative zeros
+ rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
end
formatted_number = self.number_to_delimited("%01.#{precision}f" % rounded_number, options)
if strip_insignificant_zeros
@@ -277,8 +278,6 @@ module ActiveSupport
end
end
- STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze
-
# Formats the bytes in +number+ into a more understandable
# representation (e.g., giving it 1500 yields 1.5 KB). This
# method is useful for reporting file sizes to users. You can
@@ -356,9 +355,6 @@ module ActiveSupport
end
end
- DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
- -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}.freeze
-
# Pretty prints (formats and approximates) a number in a way it
# is more readable by humans (eg.: 1200000000 becomes "1.2
# Billion"). This is useful for numbers that can get very large
@@ -527,6 +523,5 @@ module ActiveSupport
false
end
private_module_and_instance_method :valid_float?
-
end
end
diff --git a/activesupport/lib/active_support/testing/declarative.rb b/activesupport/lib/active_support/testing/declarative.rb
index 1c05d45888..508e37254a 100644
--- a/activesupport/lib/active_support/testing/declarative.rb
+++ b/activesupport/lib/active_support/testing/declarative.rb
@@ -2,7 +2,7 @@ module ActiveSupport
module Testing
module Declarative
- def self.extended(klass)
+ def self.extended(klass) #:nodoc:
klass.class_eval do
unless method_defined?(:describe)
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index 50ec50ca52..27d444fd91 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -64,7 +64,7 @@ module ActiveSupport
end
end
- def self.included klass
+ def self.included(klass) #:nodoc:
klass.extend(Module.new {
def test_methods
ParallelEach.new super
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index 58835c0ac5..9dfa2cbf11 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -90,6 +90,12 @@ class ArrayExtToSentenceTests < ActiveSupport::TestCase
def test_one_non_string_element
assert_equal '1', [1].to_sentence
end
+
+ def test_does_not_modify_given_hash
+ options = { words_connector: ' ' }
+ assert_equal "one two, and three", ['one', 'two', 'three'].to_sentence(options)
+ assert_equal({ words_connector: ' ' }, options)
+ end
end
class ArrayExtToSTests < ActiveSupport::TestCase
diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb
index 8adff5de8d..bd1df0f858 100644
--- a/activesupport/test/file_update_checker_test.rb
+++ b/activesupport/test/file_update_checker_test.rb
@@ -48,6 +48,21 @@ class FileUpdateCheckerWithEnumerableTest < ActiveSupport::TestCase
assert_equal 1, i
end
+ def test_should_be_robust_to_handle_files_with_wrong_modified_time
+ i = 0
+ now = Time.now
+ time = Time.mktime(now.year + 1, now.month, now.day) # wrong mtime from the future
+ File.utime time, time, FILES[2]
+
+ checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
+
+ sleep(1)
+ FileUtils.touch(FILES[0..1])
+
+ assert checker.execute_if_updated
+ assert_equal 1, i
+ end
+
def test_should_cache_updated_result_until_execute
i = 0
checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 }
diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb
index 4f2027f4eb..ddbba444cf 100644
--- a/activesupport/test/i18n_test.rb
+++ b/activesupport/test/i18n_test.rb
@@ -97,4 +97,9 @@ class I18nTest < ActiveSupport::TestCase
I18n.backend.store_translations 'en', :support => { :array => { :two_words_connector => default_two_words_connector } }
I18n.backend.store_translations 'en', :support => { :array => { :last_word_connector => default_last_word_connector } }
end
+
+ def test_to_sentence_with_empty_i18n_store
+ I18n.backend.store_translations 'empty', {}
+ assert_equal 'a, b, and c', %w[a b c].to_sentence(locale: 'empty')
+ end
end
diff --git a/activesupport/test/number_helper_i18n_test.rb b/activesupport/test/number_helper_i18n_test.rb
new file mode 100644
index 0000000000..e07198027b
--- /dev/null
+++ b/activesupport/test/number_helper_i18n_test.rb
@@ -0,0 +1,114 @@
+require 'abstract_unit'
+require 'active_support/number_helper'
+
+module ActiveSupport
+ class NumberHelperI18nTest < ActiveSupport::TestCase
+ include ActiveSupport::NumberHelper
+
+ def setup
+ I18n.backend.store_translations 'ts',
+ :number => {
+ :format => { :precision => 3, :delimiter => ',', :separator => '.', :significant => false, :strip_insignificant_zeros => false },
+ :currency => { :format => { :unit => '&$', :format => '%u - %n', :negative_format => '(%u - %n)', :precision => 2 } },
+ :human => {
+ :format => {
+ :precision => 2,
+ :significant => true,
+ :strip_insignificant_zeros => true
+ },
+ :storage_units => {
+ :format => "%n %u",
+ :units => {
+ :byte => "b",
+ :kb => "k"
+ }
+ },
+ :decimal_units => {
+ :format => "%n %u",
+ :units => {
+ :deci => {:one => "Tenth", :other => "Tenths"},
+ :unit => "u",
+ :ten => {:one => "Ten", :other => "Tens"},
+ :thousand => "t",
+ :million => "m",
+ :billion =>"b",
+ :trillion =>"t" ,
+ :quadrillion =>"q"
+ }
+ }
+ },
+ :percentage => { :format => {:delimiter => '', :precision => 2, :strip_insignificant_zeros => true} },
+ :precision => { :format => {:delimiter => '', :significant => true} }
+ },
+ :custom_units_for_number_to_human => {:mili => "mm", :centi => "cm", :deci => "dm", :unit => "m", :ten => "dam", :hundred => "hm", :thousand => "km"}
+ end
+
+ def test_number_to_i18n_currency
+ assert_equal("&$ - 10.00", number_to_currency(10, :locale => 'ts'))
+ assert_equal("(&$ - 10.00)", number_to_currency(-10, :locale => 'ts'))
+ assert_equal("-10.00 - &$", number_to_currency(-10, :locale => 'ts', :format => "%n - %u"))
+ end
+
+ def test_number_to_currency_with_empty_i18n_store
+ I18n.backend.store_translations 'empty', {}
+
+ assert_equal("$10.00", number_to_currency(10, :locale => 'empty'))
+ assert_equal("-$10.00", number_to_currency(-10, :locale => 'empty'))
+ end
+
+ def test_number_to_currency_without_currency_negative_format
+ I18n.backend.store_translations 'no_negative_format', :number => {
+ :currency => { :format => { :unit => '@', :format => '%n %u' } }
+ }
+
+ assert_equal("-10.00 @", number_to_currency(-10, :locale => 'no_negative_format'))
+ end
+
+ def test_number_with_i18n_precision
+ #Delimiter was set to ""
+ assert_equal("10000", number_to_rounded(10000, :locale => 'ts'))
+
+ #Precision inherited and significant was set
+ assert_equal("1.00", number_to_rounded(1.0, :locale => 'ts'))
+ end
+
+ def test_number_with_i18n_delimiter
+ #Delimiter "," and separator "."
+ assert_equal("1,000,000.234", number_to_delimited(1000000.234, :locale => 'ts'))
+ end
+
+ def test_number_to_i18n_percentage
+ # to see if strip_insignificant_zeros is true
+ assert_equal("1%", number_to_percentage(1, :locale => 'ts'))
+ # precision is 2, significant should be inherited
+ assert_equal("1.24%", number_to_percentage(1.2434, :locale => 'ts'))
+ # no delimiter
+ assert_equal("12434%", number_to_percentage(12434, :locale => 'ts'))
+ end
+
+ def test_number_to_i18n_human_size
+ #b for bytes and k for kbytes
+ assert_equal("2 k", number_to_human_size(2048, :locale => 'ts'))
+ assert_equal("42 b", number_to_human_size(42, :locale => 'ts'))
+ end
+
+ def test_number_to_human_with_default_translation_scope
+ #Using t for thousand
+ assert_equal "2 t", number_to_human(2000, :locale => 'ts')
+ #Significant was set to true with precision 2, using b for billion
+ assert_equal "1.2 b", number_to_human(1234567890, :locale => 'ts')
+ #Using pluralization (Ten/Tens and Tenth/Tenths)
+ assert_equal "1 Tenth", number_to_human(0.1, :locale => 'ts')
+ assert_equal "1.3 Tenth", number_to_human(0.134, :locale => 'ts')
+ assert_equal "2 Tenths", number_to_human(0.2, :locale => 'ts')
+ assert_equal "1 Ten", number_to_human(10, :locale => 'ts')
+ assert_equal "1.2 Ten", number_to_human(12, :locale => 'ts')
+ assert_equal "2 Tens", number_to_human(20, :locale => 'ts')
+ end
+
+ def test_number_to_human_with_custom_translation_scope
+ #Significant was set to true with precision 2, with custom translated units
+ assert_equal "4.3 cm", number_to_human(0.0432, :locale => 'ts', :units => :custom_units_for_number_to_human)
+ end
+ end
+end
diff --git a/guides/source/4_0_release_notes.textile b/guides/source/4_0_release_notes.textile
index 1ed6006b6b..23c5220c24 100644
--- a/guides/source/4_0_release_notes.textile
+++ b/guides/source/4_0_release_notes.textile
@@ -70,7 +70,7 @@ h3. Railties
* Improved +rake routes+ output for redirects.
-* Load all environments available in config.paths["config/environments"].
+* Load all environments available in <tt>config.paths["config/environments"]</tt>.
* The application generator generates <tt>public/humans.txt</tt> with some basic data.
@@ -92,12 +92,11 @@ console do
end
</ruby>
-* Add convenience hide! method to Rails generators to hide current generator
- namespace from showing when running rails generate.
+* Add a convenience method <tt>hide!</tt> to Rails generators to hide the current generator namespace from showing when running <tt>rails generate</tt>.
* Scaffold now uses +content_tag_for+ in <tt>index.html.erb</tt>.
-* <tt>Rails::Plugin</tt> is removed. Instead of adding plugins to vendor/plugins use gems or bundler with path or git dependencies.
+* <tt>Rails::Plugin</tt> is removed. Instead of adding plugins to <tt>vendor/plugins</tt>, use gems or bundler with path or git dependencies.
h4(#railties_deprecations). Deprecations
@@ -117,15 +116,15 @@ h4. Action Controller
* +respond_to+ and +respond_with+ now raise <tt>ActionController::UnknownFormat</tt> instead of directly returning head 406. The exception is rescued and converted to 406 in the exception handling middleware.
-* JSONP now uses mimetype <tt>application/javascript</tt> instead of <tt>application/json</tt>.
+* JSONP now uses <tt>application/javascript</tt> instead of <tt>application/json</tt> as the MIME type.
* Session arguments passed to process calls in functional tests are now merged into the existing session, whereas previously they would replace the existing session. This change may break some existing tests if they are asserting the exact contents of the session but should not break existing tests that only assert individual keys.
-* Forms of persisted records use always PATCH (via the _method hack).
+* Forms of persisted records use always PATCH (via the +_method+ hack).
-* For resources, both PATCH and PUT are routed to the update action.
+* For resources, both PATCH and PUT are routed to the +update+ action.
-* Don't ignore +force_ssl+ in development. This is a change of behavior - use a :if condition to recreate the old behavior.
+* Don't ignore +force_ssl+ in development. This is a change of behavior - use an <tt>:if</tt> condition to recreate the old behavior.
<ruby>
class AccountsController < ApplicationController
@@ -139,52 +138,46 @@ end
h5(#actioncontroller_deprecations). Deprecations
-* Deprecated ActionController::Integration in favour of ActionDispatch::Integration
+* Deprecated <tt>ActionController::Integration</tt> in favour of <tt>ActionDispatch::Integration</tt>.
-* Deprecated ActionController::IntegrationTest in favour of ActionDispatch::IntegrationTest
+* Deprecated <tt>ActionController::IntegrationTest</tt> in favour of <tt>ActionDispatch::IntegrationTest</tt>.
-* Deprecated ActionController::PerformanceTest in favour of ActionDispatch::PerformanceTest
+* Deprecated <tt>ActionController::PerformanceTest</tt> in favour of <tt>ActionDispatch::PerformanceTest</tt>.
-* Deprecated ActionController::AbstractRequest in favour of ActionDispatch::Request
+* Deprecated <tt>ActionController::AbstractRequest</tt> in favour of <tt>ActionDispatch::Request</tt>.
-* Deprecated ActionController::Request in favour of ActionDispatch::Request
+* Deprecated <tt>ActionController::Request</tt> in favour of <tt>ActionDispatch::Request</tt>.
-* Deprecated ActionController::AbstractResponse in favour of ActionDispatch::Response
+* Deprecated <tt>ActionController::AbstractResponse</tt> in favour of <tt>ActionDispatch::Response</tt>.
-* Deprecated ActionController::Response in favour of ActionDispatch::Response
+* Deprecated <tt>ActionController::Response</tt> in favour of <tt>ActionDispatch::Response</tt>.
-* Deprecated ActionController::Routing in favour of ActionDispatch::Routing
+* Deprecated <tt>ActionController::Routing</tt> in favour of <tt>ActionDispatch::Routing</tt>.
h4. Action Dispatch
* Added <tt>ActionDispatch::SSL</tt> middleware that when included force all the requests to be under HTTPS protocol.
-* Copy literal route constraints to defaults so that url generation know about them. The copied constraints are :protocol, :subdomain, :domain, :host and :port.
+* Copy literal route constraints to defaults so that url generation know about them. The copied constraints are <tt>:protocol</tt>, <tt>:subdomain</tt>, <tt>:domain</tt>, <tt>:host</tt> and <tt>:port</tt>.
* Allows +assert_redirected_to+ to match against a regular expression.
-* Add backtrace to development routing error page.
+* Adds a backtrace to the routing error page in development.
* +assert_generates+, +assert_recognizes+, and +assert_routing+ all raise +Assertion+ instead of +RoutingError+.
* Allows the route helper root to take a string argument. For example, <tt>root 'pages#main'</tt> as a shortcut for <tt>root to: 'pages#main'</tt>.
-* Adds support for the PATCH verb:
- Request objects respond to patch?.
- Routes have a new patch method, and understand :patch in the
- existing places where a verb is configured, like :via.
- New method patch available in functional tests.
- If :patch is the default verb for updates, edits are
- tunneled as PATCH rather than as PUT, and routing acts accordingly.
- New method patch_via_redirect available in integration tests.
+* Adds support for the PATCH verb: Request objects respond to <tt>patch?</tt>. Routes now have a new +patch+ method, and understand +:patch+ in the existing places where a verb is configured, like <tt>:via</tt>. Functional tests have a new method +patch+ and integration tests have a new method +patch_via_redirect+.
+If <tt>:patch</tt> is the default verb for updates, edits are tunneled as <tt>PATCH</tt> rather than as <tt>PUT</tt> and routing acts accordingly.
* Integration tests support the OPTIONS method.
-* +expires_in+ accepts a +must_revalidate+ flag. If true, "must-revalidate" is added to the Cache-Control header.
+* +expires_in+ accepts a +must_revalidate+ flag. If true, "must-revalidate" is added to the <tt>Cache-Control</tt> header.
-* Default responder will now always use your overridden block in respond_with to render your response.
+* Default responder will now always use your overridden block in <tt>respond_with</tt> to render your response.
-* Turn off verbose mode of rack-cache, we still have X-Rack-Cache to check that info.
+* Turn off verbose mode of <tt>rack-cache</tt>, we still have <tt>X-Rack-Cache</tt> to check that info.
* Include mounted_helpers (helpers for accessing mounted engines) in <tt>ActionDispatch::IntegrationTest</tt> by default.
@@ -192,9 +185,9 @@ h5(#actiondispatch_deprecations). Deprecations
h4. Action View
-* Make current object and counter (when it applies) variables accessible when rendering templates with :object / :collection.
+* Make current object and counter (when it applies) variables accessible when rendering templates with <tt>:object</tt> or <tt>:collection</tt>.
-* Allow to lazy load +default_form_builder+ by passing a String instead of a constant.
+* Allow to lazy load +default_form_builder+ by passing a string instead of a constant.
* Add index method to +FormBuilder+ class.
@@ -204,9 +197,9 @@ h4. Action View
* Remove <tt>:mouseover</tt> option from +image_tag+ helper.
-* Templates without a handler extension now raises a deprecation warning but still defaults to ERb. In future releases, it will simply return the template content.
+* Templates without a handler extension now raises a deprecation warning but still defaults to +ERb+. In future releases, it will simply return the template content.
-* Add divider option to +grouped_options_for_select+ to generate a separator optgroup automatically, and deprecate prompt as third argument, in favor of using an options hash.
+* Add a +divider+ option to +grouped_options_for_select+ to generate a separator optgroup automatically, and deprecate prompt as third argument, in favor of using an options hash.
* Add +time_field+ and +time_field_tag+ helpers which render an <tt>input[type="time"]</tt> tag.
@@ -237,9 +230,9 @@ h4. Action View
* Remove +button_to_function+ and +link_to_function+ helpers.
-* truncate now always returns an escaped HTML-safe string. The option :escape can be used as false to not escape the result.
+* +truncate+ now always returns an escaped HTML-safe string. The option <tt>:escape</tt> can be used as +false+ to not escape the result.
-* truncate now accepts a block to show extra content when the text is truncated.
+* +truncate+ now accepts a block to show extra content when the text is truncated.
* Add +week_field+, +week_field_tag+, +month_field+, +month_field_tag+, +datetime_local_field+, +datetime_local_field_tag+, +datetime_field+ and +datetime_field_tag+ helpers.
@@ -253,7 +246,7 @@ h4. Action View
* Adds +image_url+, +javascript_url+, +stylesheet_url+, +audio_url+, +video_url+, and +font_url+ to assets tag helper. These URL helpers will return the full path to your assets. This is useful when you are going to reference this asset from external host.
-* Allow +value_method+ and +text_method+ arguments from +collection_select+ and +options_from_collection_for_select+ to receive an object that responds to :call, such as a proc, to evaluate the option in the current element context. This works the same way with +collection_radio_buttons+ and +collection_check_boxes+.
+* Allow +value_method+ and +text_method+ arguments from +collection_select+ and +options_from_collection_for_select+ to receive an object that responds to <tt>:call</tt> such as a proc, to evaluate the option in the current element context. This works the same way with +collection_radio_buttons+ and +collection_check_boxes+.
* Add +date_field+ and +date_field_tag+ helpers which render an <tt>input[type="date"]</tt> tag.
@@ -271,7 +264,7 @@ collection_check_boxes :post, :author_ids, Author.all, :id, :name
The label/check_box pairs can be customized with a block.
-* Add +collection_radio_buttons+ form helper, similar to collection_select:
+* Add +collection_radio_buttons+ form helper, similar to +collection_select+:
<ruby>
collection_radio_buttons :post, :author_id, Author.all, :id, :name
@@ -284,15 +277,15 @@ collection_radio_buttons :post, :author_id, Author.all, :id, :name
The label/radio_button pairs can be customized with a block.
-* +check_box+ with :form HTML5 attribute will now replicate the :form attribute to the hidden field as well.
+* +check_box+ with an HTML5 attribute +:form+ will now replicate the +:form+ attribute to the hidden field as well.
-* label form helper accepts :for => nil to not generate the attribute.
+* label form helper accepts <tt>:for => nil</tt> to not generate the attribute.
-* Add :format option to +number_to_percentage+.
+* Add <tt>:format</tt> option to +number_to_percentage+.
-* Add <tt>config.action_view.logger</tt> to configure logger for Action View.
+* Add <tt>config.action_view.logger</tt> to configure logger for +Action View+.
-* +check_box+ helper with :disabled => true will generate a disabled hidden field to conform with the HTML convention where disabled fields are not submitted with the form. This is a behavior change, previously the hidden tag had a value of the disabled checkbox.
+* +check_box+ helper with <tt>:disabled => true</tt> will generate a +disabled+ hidden field to conform with the HTML convention where disabled fields are not submitted with the form. This is a behavior change, previously the hidden tag had a value of the disabled checkbox.
* +favicon_link_tag+ helper will now use the favicon in <tt>app/assets</tt> by default.
@@ -308,7 +301,7 @@ h3. Active Record
* Allow blocks for count with <tt>ActiveRecord::Relation</tt>, to work similar as <tt>Array#count</tt>: <tt>Person.where("age > 26").count { |person| person.gender == 'female' }</tt>
-* Added support to <tt>CollectionAssociation#delete</tt> for passing fixnum or string values as record ids. This finds the records responding to the id and executes delete on them.
+* Added support to <tt>CollectionAssociation#delete</tt> for passing fixnum or string values as record ids. This finds the records responding to the ids and deletes them.
<ruby>
class Person < ActiveRecord::Base
@@ -327,11 +320,11 @@ person.pets.delete(2, 3) # => [#<Pet id: 2>, #<Pet id: 3>]
<ruby>store :settings, accessors: [ :color, :homepage ], coder: JSON</ruby>
-* mysql and mysql2 connections will set SQL_MODE=STRICT_ALL_TABLES by default to avoid silent data loss. This can be disabled by specifying strict: false in your database.yml.
+* +mysql+ and +mysql2+ connections will set <tt>SQL_MODE=STRICT_ALL_TABLES</tt> by default to avoid silent data loss. This can be disabled by specifying <tt>strict: false</tt> in <tt>config/database.yml</tt>.
-* Added default order to first to assure consistent results among diferent database engines. Introduced take as a replacement to the old behavior of first.
+* Added default order to <tt>ActiveRecord::Base#first</tt> to assure consistent results among diferent database engines. Introduced <tt>ActiveRecord::Base#take</tt> as a replacement to the old behavior.
-* Added an :index option to automatically create indexes for references and belongs_to statements in migrations. The references and belongs_to methods now support an index option that receives either a boolean value or an options hash that is identical to options available to the add_index method:
+* Added an <tt>:index</tt> option to automatically create indexes for +references+ and +belongs_to+ statements in migrations. This can be either a boolean or a hash that is identical to options available to the +add_index+ method:
<ruby>
create_table :messages do |t|
@@ -352,7 +345,7 @@ Generators have also been updated to use the new syntax.
* Added bang methods for mutating <tt>ActiveRecord::Relation</tt> objects. For example, while <tt>foo.where(:bar)</tt> will return a new object leaving foo unchanged, <tt>foo.where!(:bar)</tt> will mutate the foo object.
-* Added #find_by and #find_by! to mirror the functionality provided by dynamic finders in a way that allows dynamic input more easily:
+* Added <tt>#find_by</tt> and <tt>#find_by!</tt> to mirror the functionality provided by dynamic finders in a way that allows dynamic input more easily:
<ruby>
Post.find_by name: 'Spartacus', rating: 4
@@ -364,7 +357,7 @@ Post.find_by! name: 'Spartacus'
* Remove IdentityMap - IdentityMap has never graduated to be an "enabled-by-default" feature, due to some inconsistencies with associations, as described in this commit: https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6. Hence the removal from the codebase, until such issues are fixed.
-* Added a feature to dump/load internal state of SchemaCache instance because we want to boot rails more quickly when we have many models.
+* Added a feature to dump/load internal state of +SchemaCache+ instance because we want to boot more quickly when we have many models.
<ruby>
# execute rake task.
@@ -382,14 +375,14 @@ RAILS_ENV=production bundle exec rake db:schema:cache:clear
=> remove db/schema_cache.dump
</ruby>
-* Added support for partial indices to PostgreSQL adapter.
+* Added support for partial indices to +PostgreSQL+ adapter.
* The +add_index+ method now supports a +where+ option that receives a string with the partial index criteria.
-* Implemented <tt>ActiveRecord::Relation#none</tt> method which returns a chainable relation with zero records (an instance of the NullRelation class). Any subsequent condition chained to the returned relation will continue generating an empty relation and will not fire any query to the database.
-
* Added the <tt>ActiveRecord::NullRelation</tt> class implementing the null object pattern for the Relation class.
+* Implemented <tt>ActiveRecord::Relation#none</tt> method which returns a chainable relation with zero records (an instance of the +NullRelation+ class). Any subsequent condition chained to the returned relation will continue generating an empty relation and will not fire any query to the database.
+
* Added +create_join_table+ migration helper to create HABTM join tables.
<ruby>
@@ -401,7 +394,7 @@ create_join_table :products, :categories
# end
</ruby>
-* The primary key is always initialized in the @attributes hash to nil (unless another value has been specified).
+* The primary key is always initialized in the +@attributes+ hash to nil (unless another value has been specified).
* In previous releases, the following would generate a single query with an OUTER JOIN comments, rather than two separate queries:
diff --git a/guides/source/action_mailer_basics.textile b/guides/source/action_mailer_basics.textile
index ebe774fbef..7c61cc4a8d 100644
--- a/guides/source/action_mailer_basics.textile
+++ b/guides/source/action_mailer_basics.textile
@@ -457,6 +457,7 @@ The following configuration options are best made in one of the environment file
|+delivery_method+|Defines a delivery method. Possible values are <tt>:smtp</tt> (default), <tt>:sendmail</tt>, <tt>:file</tt> and <tt>:test</tt>.|
|+perform_deliveries+|Determines whether deliveries are actually carried out when the +deliver+ method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.|
|+deliveries+|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
+|+async+|Setting this flag will turn on asynchronous message sending, message rendering and delivery will be pushed to <tt>Rails.queue</tt> for processing.|
h4. Example Action Mailer Configuration
@@ -514,3 +515,33 @@ end
</ruby>
In the test we send the email and store the returned object in the +email+ variable. We then ensure that it was sent (the first assert), then, in the second batch of assertions, we ensure that the email does indeed contain what we expect.
+
+h3. Asynchronous
+
+You can turn on application-wide asynchronous message sending by adding to your <tt>config/application.rb</tt> file:
+
+<ruby>
+config.action_mailer.async = true
+</ruby>
+
+Alternatively you can turn on async within specific mailers:
+
+<ruby>
+class WelcomeMailer < ActionMailer::Base
+ self.async = true
+end
+</ruby>
+
+h4. Custom Queues
+
+If you need a different queue than <tt>Rails.queue</tt> for your mailer you can override <tt>ActionMailer::Base#queue</tt>:
+
+<ruby>
+class WelcomeMailer < ActionMailer::Base
+ def queue
+ MyQueue.new
+ end
+end
+</ruby>
+
+Your custom queue should expect a job that responds to <tt>#run</tt>.
diff --git a/guides/source/getting_started.textile b/guides/source/getting_started.textile
index a489469a45..07419d11b4 100644
--- a/guides/source/getting_started.textile
+++ b/guides/source/getting_started.textile
@@ -20,13 +20,7 @@ application from scratch. It does not assume that you have any prior experience
with Rails. However, to get the most out of it, you need to have some
prerequisites installed:
-* The "Ruby":http://www.ruby-lang.org/en/downloads language version 1.8.7 or higher
-
-TIP: Note that Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails
-3.0. Ruby Enterprise Edition have these fixed since release 1.8.7-2010.02
-though. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults
-on Rails 3.0, so if you want to use Rails 3 with 1.9.x jump on 1.9.2 or
-1.9.3 for smooth sailing.
+* The "Ruby":http://www.ruby-lang.org/en/downloads language version 1.9.3 or higher
* The "RubyGems":http://rubyforge.org/frs/?group_id=126 packaging system
** If you want to learn more about RubyGems, please read the "RubyGems User Guide":http://docs.rubygems.org/read/book/1
@@ -216,12 +210,12 @@ You need to do this because Rails will serve any static file in the +public+ dir
Next, you have to tell Rails where your actual home page is located.
-Open the file +config/routes.rb+ in your editor.
+Open the file +config/routes.rb+ in your editor.
<ruby>
Blog::Application.routes.draw do
get "welcome/index"
-
+
# The priority is based upon order of creation:
# first created -> highest priority.
# ...
diff --git a/guides/source/routing.textile b/guides/source/routing.textile
index 70b602a243..cffbf9bec4 100644
--- a/guides/source/routing.textile
+++ b/guides/source/routing.textile
@@ -851,24 +851,6 @@ end
This will create routing helpers such as +magazine_periodical_ads_url+ and +edit_magazine_periodical_ad_path+.
-h3. Breaking Up a Large Route File
-
-If you have a large route file that you would like to break up into multiple files, you can use the +#draw+ method in your router:
-
-<ruby>
-draw :admin
-</ruby>
-
-Then, create a file called +config/routes/admin.rb+. Name the file the same as the symbol passed to the +draw+ method. You can then use the normal routing DSL inside that file:
-
-<ruby>
-# in config/routes/admin.rb
-
-namespace :admin do
- resources :posts
-end
-</ruby>
-
h3. Inspecting and Testing Routes
Rails offers facilities for inspecting and testing your routes.
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index e19aa68407..7223270210 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,5 +1,15 @@
## Rails 4.0.0 (unreleased) ##
+* Allow scaffold/model/migration generators to accept a `polymorphic` modifier
+ for `references`/`belongs_to`, for instance
+
+ rails g model Product supplier:references{polymorphic}
+
+ will generate the model with `belongs_to :supplier, polymorphic: true`
+ association and appropriate migration.
+
+ *Aleksey Magusev*
+
* Set `config.active_record.migration_error` to `:page_load` for development *Richard Schneeman*
* Add runner to Rails::Railtie as a hook called just after runner starts. *José Valim & kennyj*
@@ -39,6 +49,9 @@
* Rails::Plugin has gone. Instead of adding plugins to vendor/plugins use gems or bundler with path or git dependencies. *Santiago Pastorino*
+* Set config.action_mailer.async = true to turn on asynchronous
+ message delivery *Brian Cardarella*
+
## Rails 3.2.2 (March 1, 2012) ##
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 32797ee657..d5ec2cbfd9 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -53,7 +53,6 @@ module Rails
autoload :Bootstrap, 'rails/application/bootstrap'
autoload :Configuration, 'rails/application/configuration'
autoload :Finisher, 'rails/application/finisher'
- autoload :Railties, 'rails/application/railties'
autoload :RoutesReloader, 'rails/application/routes_reloader'
class << self
@@ -80,9 +79,42 @@ module Rails
@routes_reloader = nil
@env_config = nil
@ordered_railties = nil
+ @railties = nil
@queue = nil
end
+ # Returns true if the application is initialized.
+ def initialized?
+ @initialized
+ end
+
+ # Implements call according to the Rack API. It simples
+ # dispatch the request to the underlying middleware stack.
+ def call(env)
+ env["ORIGINAL_FULLPATH"] = build_original_fullpath(env)
+ super(env)
+ end
+
+ # Reload application routes regardless if they changed or not.
+ def reload_routes!
+ routes_reloader.reload!
+ end
+
+ # Stores some of the Rails initial environment parameters which
+ # will be used by middlewares and engines to configure themselves.
+ def env_config
+ @env_config ||= super.merge({
+ "action_dispatch.parameter_filter" => config.filter_parameters,
+ "action_dispatch.secret_token" => config.secret_token,
+ "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
+ "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
+ "action_dispatch.logger" => Rails.logger,
+ "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner
+ })
+ end
+
+ ## Rails internal API
+
# This method is called just after an application inherits from Rails::Application,
# allowing the developer to load classes in lib and use them during application
# configuration.
@@ -106,18 +138,14 @@ module Rails
require environment if environment
end
- # Reload application routes regardless if they changed or not.
- def reload_routes!
- routes_reloader.reload!
- end
-
def routes_reloader #:nodoc:
@routes_reloader ||= RoutesReloader.new
end
- # Returns an array of file paths appended with a hash of directories-extensions
- # suitable for ActiveSupport::FileUpdateChecker API.
- def watchable_args
+ # Returns an array of file paths appended with a hash of
+ # directories-extensions suitable for ActiveSupport::FileUpdateChecker
+ # API.
+ def watchable_args #:nodoc:
files, dirs = config.watchable_files.dup, config.watchable_dirs.dup
ActiveSupport::Dependencies.autoload_paths.each do |path|
@@ -138,45 +166,64 @@ module Rails
self
end
- def initialized?
- @initialized
+ def initializers #:nodoc:
+ Bootstrap.initializers_for(self) +
+ railties_initializers(super) +
+ Finisher.initializers_for(self)
end
- # Load the application and its railties tasks and invoke the registered hooks.
- # Check <tt>Rails::Railtie.rake_tasks</tt> for more info.
- def load_tasks(app=self)
- initialize_tasks
- super
+ def config #:nodoc:
+ @config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd))
+ end
+
+ def queue #:nodoc:
+ @queue ||= build_queue
+ end
+
+ def build_queue #:nodoc:
+ config.queue.new
+ end
+
+ def to_app #:nodoc:
self
end
- # Load the application console and invoke the registered hooks.
- # Check <tt>Rails::Railtie.console</tt> for more info.
- def load_console(app=self)
- initialize_console
+ def helpers_paths #:nodoc:
+ config.helpers_paths
+ end
+
+ def railties #:nodoc:
+ @railties ||= Rails::Railtie.subclasses.map(&:instance) +
+ Rails::Engine.subclasses.map(&:instance)
+ end
+
+ protected
+
+ alias :build_middleware_stack :app
+
+ def run_tasks_blocks(app) #:nodoc:
+ railties.each { |r| r.run_tasks_blocks(app) }
+ super
+ require "rails/tasks"
+ task :environment do
+ $rails_rake_task = true
+ require_environment!
+ end
+ end
+
+ def run_generators_blocks(app) #:nodoc:
+ railties.each { |r| r.run_generators_blocks(app) }
super
- self
end
- # Load the application runner and invoke the registered hooks.
- # Check <tt>Rails::Railtie.runner</tt> for more info.
- def load_runner(app=self)
- initialize_runner
+ def run_runner_blocks(app) #:nodoc:
+ railties.each { |r| r.run_runner_blocks(app) }
super
- self
end
- # Stores some of the Rails initial environment parameters which
- # will be used by middlewares and engines to configure themselves.
- def env_config
- @env_config ||= super.merge({
- "action_dispatch.parameter_filter" => config.filter_parameters,
- "action_dispatch.secret_token" => config.secret_token,
- "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
- "action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
- "action_dispatch.logger" => Rails.logger,
- "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner
- })
+ def run_console_blocks(app) #:nodoc:
+ railties.each { |r| r.run_console_blocks(app) }
+ super
end
# Returns the ordered railties for this application considering railties_order.
@@ -192,7 +239,7 @@ module Rails
end
end
- all = (railties.all - order)
+ all = (railties - order)
all.push(self) unless (all + order).include?(self)
order.push(:all) unless order.include?(:all)
@@ -202,46 +249,23 @@ module Rails
end
end
- def initializers #:nodoc:
- Bootstrap.initializers_for(self) +
- super +
- Finisher.initializers_for(self)
- end
-
- def config #:nodoc:
- @config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd))
- end
-
- def queue #:nodoc:
- @queue ||= build_queue
- end
-
- def build_queue # :nodoc:
- config.queue.new
- end
-
- def to_app
- self
- end
-
- def helpers_paths #:nodoc:
- config.helpers_paths
- end
-
- def call(env)
- env["ORIGINAL_FULLPATH"] = build_original_fullpath(env)
- super(env)
+ def railties_initializers(current) #:nodoc:
+ initializers = []
+ ordered_railties.each do |r|
+ if r == self
+ initializers += current
+ else
+ initializers += r.initializers
+ end
+ end
+ initializers
end
- protected
-
- alias :build_middleware_stack :app
-
- def reload_dependencies?
+ def reload_dependencies? #:nodoc:
config.reload_classes_only_on_change != true || reloaders.map(&:updated?).any?
end
- def default_middleware_stack
+ def default_middleware_stack #:nodoc:
ActionDispatch::MiddlewareStack.new.tap do |middleware|
if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
require "action_dispatch/http/rack_cache"
@@ -296,26 +320,7 @@ module Rails
end
end
- def initialize_tasks #:nodoc:
- self.class.rake_tasks do
- require "rails/tasks"
- task :environment do
- $rails_rake_task = true
- require_environment!
- end
- end
- end
-
- def initialize_console #:nodoc:
- require "pp"
- require "rails/console/app"
- require "rails/console/helpers"
- end
-
- def initialize_runner #:nodoc:
- end
-
- def build_original_fullpath(env)
+ def build_original_fullpath(env) #:nodoc:
path_info = env["PATH_INFO"]
query_string = env["QUERY_STRING"]
script_name = env["SCRIPT_NAME"]
diff --git a/railties/lib/rails/application/railties.rb b/railties/lib/rails/application/railties.rb
deleted file mode 100644
index f20a9689de..0000000000
--- a/railties/lib/rails/application/railties.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'rails/engine/railties'
-
-module Rails
- class Application < Engine
- class Railties < Rails::Engine::Railties
- def all(&block)
- @all ||= railties + engines
- @all.each(&block) if block
- @all
- end
- end
- end
-end
diff --git a/railties/lib/rails/application/route_inspector.rb b/railties/lib/rails/application/routes_inspector.rb
index 942c4f4789..6b2caf8277 100644
--- a/railties/lib/rails/application/route_inspector.rb
+++ b/railties/lib/rails/application/routes_inspector.rb
@@ -62,7 +62,7 @@ module Rails
##
# This class is just used for displaying route information when someone
# executes `rake routes`. People should not use this class.
- class RouteInspector # :nodoc:
+ class RoutesInspector # :nodoc:
def initialize
@engines = Hash.new
end
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
index 19f616ec50..6f9a200aa9 100644
--- a/railties/lib/rails/application/routes_reloader.rb
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -3,13 +3,12 @@ require "active_support/core_ext/module/delegation"
module Rails
class Application
class RoutesReloader
- attr_reader :route_sets, :paths, :external_routes
+ attr_reader :route_sets, :paths
delegate :execute_if_updated, :execute, :updated?, :to => :updater
def initialize
- @paths = []
- @route_sets = []
- @external_routes = []
+ @paths = []
+ @route_sets = []
end
def reload!
@@ -24,11 +23,7 @@ module Rails
def updater
@updater ||= begin
- dirs = @external_routes.each_with_object({}) do |dir, hash|
- hash[dir.to_s] = %w(rb)
- end
-
- updater = ActiveSupport::FileUpdateChecker.new(paths, dirs) { reload! }
+ updater = ActiveSupport::FileUpdateChecker.new(paths) { reload! }
updater.execute
updater
end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 806b553b81..383c159d3d 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -2,7 +2,6 @@ require 'rails/railtie'
require 'active_support/core_ext/module/delegation'
require 'pathname'
require 'rbconfig'
-require 'rails/engine/railties'
module Rails
# <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
@@ -338,27 +337,6 @@ module Rails
# config.railties_order = [Blog::Engine, :main_app, :all]
class Engine < Railtie
autoload :Configuration, "rails/engine/configuration"
- autoload :Railties, "rails/engine/railties"
-
- def initialize
- @_all_autoload_paths = nil
- @_all_load_paths = nil
- @app = nil
- @config = nil
- @env_config = nil
- @helpers = nil
- @railties = nil
- @routes = nil
- super
- end
-
- def load_generators(app=self)
- initialize_generators
- railties.all { |r| r.load_generators(app) }
- Rails::Generators.configure!(app.config.generators)
- super
- self
- end
class << self
attr_accessor :called_from, :isolated
@@ -408,7 +386,7 @@ module Rails
end
unless mod.respond_to?(:railtie_routes_url_helpers)
- define_method(:railtie_routes_url_helpers) { railtie.routes_url_helpers }
+ define_method(:railtie_routes_url_helpers) { railtie.routes.url_helpers }
end
end
end
@@ -417,34 +395,65 @@ module Rails
# Finds engine with given path
def find(path)
expanded_path = File.expand_path path
- Rails::Engine::Railties.engines.find { |engine|
- File.expand_path(engine.root) == expanded_path
- }
+ Rails::Engine.subclasses.each do |klass|
+ engine = klass.instance
+ return engine if File.expand_path(engine.root) == expanded_path
+ end
+ nil
end
end
delegate :middleware, :root, :paths, :to => :config
delegate :engine_name, :isolated?, :to => "self.class"
- def load_tasks(app=self)
- railties.all { |r| r.load_tasks(app) }
+ def initialize
+ @_all_autoload_paths = nil
+ @_all_load_paths = nil
+ @app = nil
+ @config = nil
+ @env_config = nil
+ @helpers = nil
+ @routes = nil
super
- paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
+ # Load console and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.console</tt> for more info.
def load_console(app=self)
- railties.all { |r| r.load_console(app) }
- super
+ require "pp"
+ require "rails/console/app"
+ require "rails/console/helpers"
+ run_console_blocks(app)
+ self
end
+ # Load Rails runner and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.runner</tt> for more info.
def load_runner(app=self)
- railties.all { |r| r.load_runner(app) }
- super
+ run_runner_blocks(app)
+ self
end
- def eager_load!
- railties.all(&:eager_load!)
+ # Load Rake, railties tasks and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.rake_tasks</tt> for more info.
+ def load_tasks(app=self)
+ require "rake"
+ run_tasks_blocks(app)
+ self
+ end
+ # Load rails generators and invoke the registered hooks.
+ # Check <tt>Rails::Railtie.generators</tt> for more info.
+ def load_generators(app=self)
+ require "rails/generators"
+ run_generators_blocks(app)
+ Rails::Generators.configure!(app.config.generators)
+ self
+ end
+
+ # Eager load the application by loading all ruby
+ # files inside eager_load paths.
+ def eager_load!
config.eager_load_paths.each do |load_path|
matcher = /\A#{Regexp.escape(load_path)}\/(.*)\.rb\Z/
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
@@ -453,10 +462,7 @@ module Rails
end
end
- def railties
- @railties ||= self.class::Railties.new(config)
- end
-
+ # Returns a module with all the helpers defined for the engine.
def helpers
@helpers ||= begin
helpers = Module.new
@@ -468,14 +474,12 @@ module Rails
end
end
+ # Returns all registered helpers paths.
def helpers_paths
paths["app/helpers"].existent
end
- def routes_url_helpers
- routes.url_helpers
- end
-
+ # Returns the underlying rack application for this engine.
def app
@app ||= begin
config.middleware = config.middleware.merge_into(default_middleware_stack)
@@ -483,45 +487,33 @@ module Rails
end
end
+ # Returns the endpoint for this engine. If none is registered,
+ # defaults to an ActionDispatch::Routing::RouteSet.
def endpoint
self.class.endpoint || routes
end
+ # Define the Rack API for this engine.
def call(env)
app.call(env.merge!(env_config))
end
+ # Defines additional Rack env configuration that is added on each call.
def env_config
@env_config ||= {
'action_dispatch.routes' => routes
}
end
+ # Defines the routes for this engine. If a block is given to
+ # routes, it is appended to the engine.
def routes
- @routes ||= ActionDispatch::Routing::RouteSet.new.tap do |routes|
- routes.draw_paths.concat paths["config/routes"].paths
- end
-
+ @routes ||= ActionDispatch::Routing::RouteSet.new
@routes.append(&Proc.new) if block_given?
@routes
end
- def ordered_railties
- railties.all + [self]
- end
-
- def initializers
- initializers = []
- ordered_railties.each do |r|
- if r == self
- initializers += super
- else
- initializers += r.initializers
- end
- end
- initializers
- end
-
+ # Define the configuration object for the engine.
def config
@config ||= Engine::Configuration.new(find_root_with_flag("lib"))
end
@@ -560,12 +552,10 @@ module Rails
initializer :add_routing_paths do |app|
paths = self.paths["config/routes.rb"].existent
- external_paths = self.paths["config/routes"].paths
if routes? || paths.any?
app.routes_reloader.paths.unshift(*paths)
app.routes_reloader.route_sets << routes
- app.routes_reloader.external_routes.unshift(*external_paths)
end
end
@@ -626,7 +616,6 @@ module Rails
else
Rake::Task["app:railties:install:migrations"].invoke
end
-
end
end
end
@@ -634,19 +623,20 @@ module Rails
protected
- def initialize_generators
- require "rails/generators"
+ def run_tasks_blocks(*) #:nodoc:
+ super
+ paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
- def routes?
+ def routes? #:nodoc:
@routes
end
- def has_migrations?
+ def has_migrations? #:nodoc:
paths["db/migrate"].existent.any?
end
- def find_root_with_flag(flag, default=nil)
+ def find_root_with_flag(flag, default=nil) #:nodoc:
root_path = self.class.called_from
while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}")
@@ -660,19 +650,19 @@ module Rails
Pathname.new File.realpath root
end
- def default_middleware_stack
+ def default_middleware_stack #:nodoc:
ActionDispatch::MiddlewareStack.new
end
- def _all_autoload_once_paths
+ def _all_autoload_once_paths #:nodoc:
config.autoload_once_paths
end
- def _all_autoload_paths
+ def _all_autoload_paths #:nodoc:
@_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
end
- def _all_load_paths
+ def _all_load_paths #:nodoc:
@_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
end
end
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index e31df807a6..6b18b1e249 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -53,7 +53,6 @@ module Rails
paths.add "config/initializers", :glob => "**/*.rb"
paths.add "config/locales", :glob => "*.{rb,yml}"
paths.add "config/routes.rb"
- paths.add "config/routes", :glob => "**/*.rb"
paths.add "db"
paths.add "db/migrate"
paths.add "db/seeds.rb"
diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb
deleted file mode 100644
index 033d9c4180..0000000000
--- a/railties/lib/rails/engine/railties.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module Rails
- class Engine < Railtie
- class Railties
- # TODO Write tests for this behavior extracted from Application
- def initialize(config)
- @config = config
- end
-
- def all(&block)
- @all ||= []
- @all.each(&block) if block
- @all
- end
-
- def self.railties
- @railties ||= ::Rails::Railtie.subclasses.map(&:instance)
- end
-
- def self.engines
- @engines ||= ::Rails::Engine.subclasses.map(&:instance)
- end
-
- delegate :railties, :engines, :to => "self.class"
- end
- end
-end
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index 25d0161e4c..d480fc12b5 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -35,7 +35,7 @@ module Rails
private
- # parse possible attribute options like :limit for string/text/binary/integer or :precision/:scale for decimals
+ # parse possible attribute options like :limit for string/text/binary/integer, :precision/:scale for decimals or :polymorphic for references/belongs_to
# when declaring options curly brackets should be used
def parse_type_and_options(type)
case type
@@ -43,6 +43,8 @@ module Rails
return $1, :limit => $2.to_i
when /decimal\{(\d+)[,.-](\d+)\}/
return :decimal, :precision => $1.to_i, :scale => $2.to_i
+ when /(references|belongs_to)\{polymorphic\}/
+ return $1, :polymorphic => true
else
return type, {}
end
@@ -92,13 +94,21 @@ module Rails
end
def index_name
- reference? ? "#{name}_id" : name
+ if reference?
+ polymorphic? ? %w(id type).map { |t| "#{name}_#{t}" } : "#{name}_id"
+ else
+ name
+ end
end
def reference?
self.class.reference?(type)
end
+ def polymorphic?
+ self.attr_options.has_key?(:polymorphic)
+ end
+
def has_index?
@has_index
end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
index 3ddc86ae0a..6c0ef31725 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb.tt
@@ -1,5 +1,5 @@
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :reset_session instead.
- protect_from_forgery :with => :exception
-end \ No newline at end of file
+ protect_from_forgery with: :exception
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
index bba96a7431..e0539aa8bb 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
@@ -2,7 +2,7 @@
<html>
<head>
<title><%= camelized %></title>
- <%%= stylesheet_link_tag "application", :media => "all" %>
+ <%%= stylesheet_link_tag "application", media: "all" %>
<%%= javascript_include_tag "application" %>
<%%= csrf_meta_tags %>
</head>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index d816f973e6..5915d20010 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -13,7 +13,7 @@ require "action_mailer/railtie"
if defined?(Bundler)
# If you precompile assets before deploying to production, use this line.
- Bundler.require(*Rails.groups(:assets => %w(development test)))
+ Bundler.require(*Rails.groups(assets: %w(development test)))
# If you want your assets lazily compiled in production, use this line.
# Bundler.require(:default, :assets, Rails.env)
end
@@ -67,5 +67,8 @@ module <%= app_const_base %>
# Version of your assets, change this if you want to expire all your assets.
config.assets.version = '1.0'
<% end -%>
+
+ # Enable app-wide asynchronous ActionMailer
+ # config.action_mailer.async = true
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
index 303e47877f..85acac6725 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
@@ -1,18 +1,18 @@
<%= app_const %>.routes.draw do
# The priority is based upon order of creation:
# first created -> highest priority.
-
+
# You can have the root of your site routed with "root"
# just remember to delete public/index.html.
- # root :to => 'welcome#index'
+ # root to: 'welcome#index'
# Sample of regular route:
# get 'products/:id' => 'catalog#view'
# Keep in mind you can assign values other than :controller and :action
# Sample of named route:
- # get 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
- # This route can be invoked with purchase_url(:id => product.id)
+ # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
+ # This route can be invoked with purchase_url(id: product.id)
# Sample resource route (maps HTTP verbs to controller actions automatically):
# resources :products
@@ -39,7 +39,7 @@
# resources :products do
# resources :comments
# resources :sales do
- # get 'recent', :on => :collection
+ # get 'recent', on: :collection
# end
# end
@@ -52,4 +52,4 @@
# See how all your routes lay out with "rake routes"
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
index 2a849b7f2b..9342a57b20 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
+++ b/railties/lib/rails/generators/rails/app/templates/test/performance/browsing_test.rb
@@ -3,8 +3,8 @@ require 'rails/performance_test_help'
class BrowsingTest < ActionDispatch::PerformanceTest
# Refer to the documentation for all available options
- # self.profile_options = { :runs => 5, :metrics => [:wall_time, :memory],
- # :output => 'tmp/performance', :formats => [:flat] }
+ # self.profile_options = { runs: 5, metrics: [:wall_time, :memory],
+ # output: 'tmp/performance', formats: [:flat] }
def test_homepage
get '/'
diff --git a/railties/lib/rails/info_controller.rb b/railties/lib/rails/info_controller.rb
index 5081074395..bacdcbf3aa 100644
--- a/railties/lib/rails/info_controller.rb
+++ b/railties/lib/rails/info_controller.rb
@@ -1,4 +1,4 @@
-require 'rails/application/route_inspector'
+require 'rails/application/routes_inspector'
class Rails::InfoController < ActionController::Base
self.view_paths = File.join(File.dirname(__FILE__), 'templates')
@@ -15,7 +15,7 @@ class Rails::InfoController < ActionController::Base
end
def routes
- inspector = Rails::Application::RouteInspector.new
+ inspector = Rails::Application::RoutesInspector.new
@info = inspector.format(_routes.routes).join("\n")
end
diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb
index a61673fab8..1a0b6d1e1a 100644
--- a/railties/lib/rails/initializable.rb
+++ b/railties/lib/rails/initializable.rb
@@ -2,7 +2,7 @@ require 'tsort'
module Rails
module Initializable
- def self.included(base)
+ def self.included(base) #:nodoc:
base.extend ClassMethods
end
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 6cd9c7bc95..9b4a69f19f 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -114,7 +114,7 @@ module Rails
class Path
include Enumerable
- attr_reader :path, :root
+ attr_reader :path
attr_accessor :glob
def initialize(root, current, paths, options = {})
@@ -180,14 +180,6 @@ module Rails
@paths
end
- def paths
- raise "You need to set a path root" unless @root.path
-
- map do |p|
- File.join @root.path, p
- end
- end
-
# Expands all paths against the root and return all unique values.
def expanded
raise "You need to set a path root" unless @root.path
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index a06be59759..1cb99463cc 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -181,33 +181,35 @@ module Rails
def eager_load!
end
- def load_console(app=self)
+ def railtie_namespace
+ @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
+ end
+
+ protected
+
+ def run_console_blocks(app) #:nodoc:
self.class.console.each { |block| block.call(app) }
end
- def load_runner(app=self)
+ def run_generators_blocks(app) #:nodoc:
+ self.class.generators.each { |block| block.call(app) }
+ end
+
+ def run_runner_blocks(app) #:nodoc:
self.class.runner.each { |block| block.call(app) }
end
- def load_tasks(app=self)
- require 'rake'
+ def run_tasks_blocks(app) #:nodoc:
extend Rake::DSL
- self.class.rake_tasks.each { |block| self.instance_exec(app, &block) }
+ self.class.rake_tasks.each { |block| instance_exec(app, &block) }
- # load also tasks from all superclasses
+ # Load also tasks from all superclasses
klass = self.class.superclass
+
while klass.respond_to?(:rake_tasks)
- klass.rake_tasks.each { |t| self.instance_exec(app, &t) }
+ klass.rake_tasks.each { |t| instance_exec(app, &t) }
klass = klass.superclass
end
end
-
- def load_generators(app=self)
- self.class.generators.each { |block| block.call(app) }
- end
-
- def railtie_namespace
- @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
- end
end
end
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index 5778b22f18..4ade825616 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -1,7 +1,7 @@
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
task :routes => :environment do
all_routes = Rails.application.routes.routes
- require 'rails/application/route_inspector'
- inspector = Rails::Application::RouteInspector.new
+ require 'rails/application/routes_inspector'
+ inspector = Rails::Application::RoutesInspector.new
puts inspector.format(all_routes, ENV['CONTROLLER']).join "\n"
end
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index 207739c4bc..6d28947e83 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
s.rdoc_options << '--exclude' << '.'
s.add_dependency('rake', '>= 0.8.7')
- s.add_dependency('thor', '>= 0.15.3', '< 2.0')
+ s.add_dependency('thor', '>= 0.15.4', '< 2.0')
s.add_dependency('rdoc', '~> 3.4')
s.add_dependency('activesupport', version)
s.add_dependency('actionpack', version)
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index c1aa98e481..c813defe93 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -69,11 +69,11 @@ module ApplicationTests
Rails.env = "development"
assert_equal [:default, "development"], Rails.groups
- assert_equal [:default, "development", :assets], Rails.groups(:assets => [:development])
- assert_equal [:default, "development", :another, :assets], Rails.groups(:another, :assets => %w(development))
+ assert_equal [:default, "development", :assets], Rails.groups(assets: [:development])
+ assert_equal [:default, "development", :another, :assets], Rails.groups(:another, assets: %w(development))
Rails.env = "test"
- assert_equal [:default, "test"], Rails.groups(:assets => [:development])
+ assert_equal [:default, "test"], Rails.groups(assets: [:development])
ENV["RAILS_GROUPS"] = "javascripts,stylesheets"
assert_equal [:default, "test", "javascripts", "stylesheets"], Rails.groups
@@ -567,7 +567,7 @@ module ApplicationTests
app_file 'app/controllers/application_controller.rb', <<-RUBY
class ApplicationController < ActionController::Base
- protect_from_forgery :with => :reset_session # as we are testing API here
+ protect_from_forgery with: :reset_session # as we are testing API here
end
RUBY
diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb
index e0893f53be..4029984ce9 100644
--- a/railties/test/application/paths_test.rb
+++ b/railties/test/application/paths_test.rb
@@ -50,8 +50,6 @@ module ApplicationTests
assert_path @paths["config/locales"], "config/locales/en.yml"
assert_path @paths["config/environment"], "config/environment.rb"
assert_path @paths["config/environments"], "config/environments/development.rb"
- assert_path @paths["config/routes.rb"], "config/routes.rb"
- assert_path @paths["config/routes"], "config/routes"
assert_equal root("app", "controllers"), @paths["app/controllers"].expanded.first
end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index e0ee349550..a450f90dbf 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -144,6 +144,24 @@ module ApplicationTests
assert_no_match(/Errors running/, output)
end
+ def test_db_test_clone_when_using_sql_format
+ add_to_config "config.active_record.schema_format = :sql"
+ output = Dir.chdir(app_path) do
+ `rails generate scaffold user username:string;
+ bundle exec rake db:migrate db:test:clone 2>&1 --trace`
+ end
+ assert_match(/Execute db:test:clone_structure/, output)
+ end
+
+ def test_db_test_prepare_when_using_sql_format
+ add_to_config "config.active_record.schema_format = :sql"
+ output = Dir.chdir(app_path) do
+ `rails generate scaffold user username:string;
+ bundle exec rake db:migrate db:test:prepare 2>&1 --trace`
+ end
+ assert_match(/Execute db:test:load_structure/, output)
+ end
+
def test_rake_dump_structure_should_respect_db_structure_env_variable
Dir.chdir(app_path) do
# ensure we have a schema_migrations table to dump
diff --git a/railties/test/application/route_inspect_test.rb b/railties/test/application/routes_inspect_test.rb
index 3b8c874b5b..eacbf3992e 100644
--- a/railties/test/application/route_inspect_test.rb
+++ b/railties/test/application/routes_inspect_test.rb
@@ -1,5 +1,5 @@
require 'minitest/autorun'
-require 'rails/application/route_inspector'
+require 'rails/application/routes_inspector'
require 'action_controller'
require 'rails/engine'
@@ -7,7 +7,7 @@ module ApplicationTests
class RouteInspectTest < ActiveSupport::TestCase
def setup
@set = ActionDispatch::Routing::RouteSet.new
- @inspector = Rails::Application::RouteInspector.new
+ @inspector = Rails::Application::RoutesInspector.new
app = ActiveSupport::OrderedOptions.new
app.config = ActiveSupport::OrderedOptions.new
app.config.assets = ActiveSupport::OrderedOptions.new
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index d1373ba202..396b1849d8 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -178,90 +178,7 @@ module ApplicationTests
assert_equal 'WIN', last_response.body
end
- test "routes drawing from config/routes" do
- app_file 'config/routes.rb', <<-RUBY
- AppTemplate::Application.routes.draw do
- draw :external
- end
- RUBY
-
- app_file 'config/routes/external.rb', <<-RUBY
- get ':controller/:action'
- RUBY
-
- controller :success, <<-RUBY
- class SuccessController < ActionController::Base
- def index
- render :text => "success!"
- end
- end
- RUBY
-
- app 'development'
- get '/success/index'
- assert_equal 'success!', last_response.body
- end
-
{"development" => "baz", "production" => "bar"}.each do |mode, expected|
- test "reloads routes when external configuration is changed in #{mode}" do
- controller :foo, <<-RUBY
- class FooController < ApplicationController
- def bar
- render :text => "bar"
- end
-
- def baz
- render :text => "baz"
- end
- end
- RUBY
-
- app_file 'config/routes.rb', <<-RUBY
- AppTemplate::Application.routes.draw do
- draw :external
- end
- RUBY
-
- app_file 'config/routes/external.rb', <<-RUBY
- get 'foo', :to => 'foo#bar'
- RUBY
-
- app(mode)
-
- get '/foo'
- assert_equal 'bar', last_response.body
-
- app_file 'config/routes/external.rb', <<-RUBY
- get 'foo', :to => 'foo#baz'
- RUBY
-
- sleep 0.1
-
- get '/foo'
- assert_equal expected, last_response.body
-
- app_file 'config/routes.rb', <<-RUBY
- AppTemplate::Application.routes.draw do
- draw :external
- draw :other_external
- end
- RUBY
-
- app_file 'config/routes/other_external.rb', <<-RUBY
- get 'win', :to => 'foo#baz'
- RUBY
-
- sleep 0.1
-
- get '/win'
-
- if mode == "development"
- assert_equal expected, last_response.body
- else
- assert_equal 404, last_response.status
- end
- end
-
test "reloads routes when configuration is changed in #{mode}" do
controller :foo, <<-RUBY
class FooController < ApplicationController
diff --git a/railties/test/engine_test.rb b/railties/test/engine_test.rb
index 68406dce4c..addf49cdb6 100644
--- a/railties/test/engine_test.rb
+++ b/railties/test/engine_test.rb
@@ -11,14 +11,4 @@ class EngineTest < ActiveSupport::TestCase
assert !engine.routes?
end
-
- it "does not add more paths to routes on each call" do
- engine = Class.new(Rails::Engine)
-
- engine.routes
- length = engine.routes.draw_paths.length
-
- engine.routes
- assert_equal length, engine.routes.draw_paths.length
- end
end
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
index 6e3fc84781..6ab1cd58c7 100644
--- a/railties/test/generators/generated_attribute_test.rb
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -102,22 +102,28 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
def test_reference_is_true
%w(references belongs_to).each do |attribute_type|
- assert_equal(
- true,
- create_generated_attribute(attribute_type).reference?
- )
+ assert create_generated_attribute(attribute_type).reference?
end
end
def test_reference_is_false
%w(foo bar baz).each do |attribute_type|
- assert_equal(
- false,
- create_generated_attribute(attribute_type).reference?
- )
+ assert !create_generated_attribute(attribute_type).reference?
end
end
+ def test_polymorphic_reference_is_true
+ %w(references belongs_to).each do |attribute_type|
+ assert create_generated_attribute("#{attribute_type}{polymorphic}").polymorphic?
+ end
+ end
+
+ def test_polymorphic_reference_is_false
+ %w(foo bar baz).each do |attribute_type|
+ assert !create_generated_attribute("#{attribute_type}{polymorphic}").polymorphic?
+ end
+ end
+
def test_blank_type_defaults_to_string_raises_exception
assert_equal :string, create_generated_attribute(nil, 'title').type
assert_equal :string, create_generated_attribute("", 'title').type
@@ -126,5 +132,6 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
def test_handles_index_names_for_references
assert_equal "post", create_generated_attribute('string', 'post').index_name
assert_equal "post_id", create_generated_attribute('references', 'post').index_name
+ assert_equal ["post_id", "post_type"], create_generated_attribute('references{polymorphic}', 'post').index_name
end
end
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index fd3b8c8a17..ec33bd7c6b 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -166,7 +166,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
def test_add_migration_with_attributes_index_declaration_and_attribute_options
- run_generator ["product", "title:string{40}:index", "content:string{255}", "price:decimal{5,2}:index", "discount:decimal{5,2}:uniq"]
+ run_generator ["product", "title:string{40}:index", "content:string{255}", "price:decimal{5,2}:index", "discount:decimal{5,2}:uniq", "supplier:references{polymorphic}"]
assert_migration "db/migrate/create_products.rb" do |content|
assert_method :change, content do |up|
@@ -174,6 +174,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_match(/t.string :title, limit: 40/, up)
assert_match(/t.string :content, limit: 255/, up)
assert_match(/t.decimal :price, precision: 5, scale: 2/, up)
+ assert_match(/t.references :supplier, polymorphic: true/, up)
end
assert_match(/add_index :products, :title/, content)
assert_match(/add_index :products, :price/, content)
@@ -193,15 +194,25 @@ class ModelGeneratorTest < Rails::Generators::TestCase
end
def test_model_with_references_attribute_generates_belongs_to_associations
- run_generator ["product", "name:string", "supplier_id:references"]
+ run_generator ["product", "name:string", "supplier:references"]
assert_file "app/models/product.rb", /belongs_to :supplier/
end
def test_model_with_belongs_to_attribute_generates_belongs_to_associations
- run_generator ["product", "name:string", "supplier_id:belongs_to"]
+ run_generator ["product", "name:string", "supplier:belongs_to"]
assert_file "app/models/product.rb", /belongs_to :supplier/
end
+ def test_model_with_polymorphic_references_attribute_generates_belongs_to_associations
+ run_generator ["product", "name:string", "supplier:references{polymorphic}"]
+ assert_file "app/models/product.rb", /belongs_to :supplier, polymorphic: true/
+ end
+
+ def test_model_with_polymorphic_belongs_to_attribute_generates_belongs_to_associations
+ run_generator ["product", "name:string", "supplier:belongs_to{polymorphic}"]
+ assert_file "app/models/product.rb", /belongs_to :supplier, polymorphic: true/
+ end
+
def test_migration_with_timestamps
run_generator
assert_migration "db/migrate/create_accounts.rb", /t.timestamps/
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 800b1c90f0..6071cd3f39 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -273,24 +273,18 @@ end
# Create a scope and build a fixture rails app
Module.new do
extend TestHelpers::Paths
+
# Build a rails app
- if File.exist?(app_template_path)
- FileUtils.rm_rf(app_template_path)
- end
+ FileUtils.rm_rf(app_template_path)
FileUtils.mkdir(app_template_path)
environment = File.expand_path('../../../../load_paths', __FILE__)
- if File.exist?("#{environment}.rb")
- require_environment = "-r #{environment}"
- end
+ require_environment = "-r #{environment}"
`#{Gem.ruby} #{require_environment} #{RAILS_FRAMEWORK_ROOT}/railties/bin/rails new #{app_template_path}`
+
File.open("#{app_template_path}/config/boot.rb", 'w') do |f|
- if require_environment
- f.puts "Dir.chdir('#{File.dirname(environment)}') do"
- f.puts " require '#{environment}'"
- f.puts "end"
- end
+ f.puts "require '#{environment}'"
f.puts "require 'rails/all'"
end
end unless defined?(RAILS_ISOLATED_ENGINE)
diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb
index 5d6b6f9f72..d334034e7d 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -29,7 +29,6 @@ class PathsTest < ActiveSupport::TestCase
test "creating a root level path" do
@root.add "app"
assert_equal ["/foo/bar/app"], @root["app"].to_a
- assert_equal ["/foo/bar/app"], @root["app"].paths
end
test "creating a root level path with options" do
@@ -192,7 +191,6 @@ class PathsTest < ActiveSupport::TestCase
@root["app"] = "/app"
@root["app"].glob = "*.rb"
assert_equal "*.rb", @root["app"].glob
- assert_equal ["/foo/bar/app"], @root["app"].paths
end
test "it should be possible to override a path's default glob without assignment" do
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 52c7fae6c6..63814f7a04 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1098,7 +1098,7 @@ YAML
assert_equal "// App's bar js\n;", last_response.body.strip
# ensure that railties are not added twice
- railties = Rails.application.ordered_railties.map(&:class)
+ railties = Rails.application.send(:ordered_railties).map(&:class)
assert_equal railties, railties.uniq
end