aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile13
-rw-r--r--actionmailer/lib/action_mailer/delivery_methods.rb2
-rw-r--r--actionmailer/test/delivery_methods_test.rb3
-rw-r--r--actionpack/CHANGELOG.md7
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb2
-rw-r--r--actionpack/lib/action_controller/base.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb24
-rw-r--r--actionpack/test/controller/filters_test.rb9
-rw-r--r--actionpack/test/controller/mime_responds_test.rb1
-rw-r--r--actionpack/test/controller/new_base/render_streaming_test.rb2
-rw-r--r--actionpack/test/controller/render_test.rb4
-rw-r--r--actionview/actionview.gemspec2
-rw-r--r--activerecord/CHANGELOG.md41
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb3
-rw-r--r--[-rwxr-xr-x]activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb10
-rw-r--r--[-rwxr-xr-x]activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb0
-rw-r--r--[-rwxr-xr-x]activerecord/lib/active_record/connection_adapters/mysql_adapter.rb0
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb4
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb3
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb10
-rw-r--r--activerecord/lib/active_record/railties/databases.rake5
-rw-r--r--activerecord/lib/active_record/reflection.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb70
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb16
-rw-r--r--[-rwxr-xr-x]activerecord/test/cases/base_test.rb0
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb10
-rw-r--r--activerecord/test/cases/nested_attributes_with_callbacks_test.rb144
-rw-r--r--activerecord/test/cases/quoting_test.rb19
-rw-r--r--activerecord/test/cases/relations_test.rb4
-rw-r--r--activerecord/test/cases/transactions_test.rb5
-rw-r--r--activerecord/test/models/author.rb3
-rw-r--r--activerecord/test/models/auto_id.rb4
-rw-r--r--activerecord/test/models/car.rb3
-rw-r--r--activerecord/test/models/citation.rb3
-rw-r--r--activerecord/test/models/club.rb1
-rw-r--r--activerecord/test/models/company.rb2
-rw-r--r--activerecord/test/models/man.rb1
-rw-r--r--activerecord/test/models/member.rb1
-rw-r--r--activerecord/test/models/mixed_case_monkey.rb2
-rw-r--r--activerecord/test/models/movie.rb4
-rw-r--r--activerecord/test/models/post.rb1
-rw-r--r--activerecord/test/models/project.rb1
-rw-r--r--activerecord/test/models/topic.rb1
-rw-r--r--activesupport/CHANGELOG.md16
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/filters.rb10
-rw-r--r--activesupport/lib/active_support/deprecation/behaviors.rb23
-rw-r--r--activesupport/lib/active_support/duration.rb14
-rw-r--r--activesupport/lib/active_support/ordered_options.rb2
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb5
-rw-r--r--activesupport/test/deprecation_test.rb16
-rw-r--r--activesupport/test/transliterate_test.rb6
-rw-r--r--activesupport/test/xml_mini_test.rb12
-rw-r--r--guides/source/4_0_release_notes.md13
-rw-r--r--guides/source/_welcome.html.erb2
-rw-r--r--guides/source/action_view_overview.md66
-rw-r--r--guides/source/active_record_querying.md8
-rw-r--r--guides/source/active_support_core_extensions.md77
-rw-r--r--guides/source/asset_pipeline.md2
-rw-r--r--guides/source/association_basics.md6
-rw-r--r--guides/source/development_dependencies_install.md8
-rw-r--r--guides/source/engines.md8
-rw-r--r--guides/source/form_helpers.md2
-rw-r--r--guides/source/getting_started.md9
-rw-r--r--guides/source/i18n.md13
-rw-r--r--guides/source/initialization.md2
-rw-r--r--guides/source/testing.md90
-rw-r--r--guides/source/upgrading_ruby_on_rails.md2
-rw-r--r--guides/source/working_with_javascript_in_rails.md2
-rw-r--r--railties/CHANGELOG.md22
-rw-r--r--railties/lib/rails/api/task.rb12
-rw-r--r--railties/lib/rails/app_rails_loader.rb2
-rw-r--r--railties/lib/rails/engine.rb9
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb36
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb2
-rw-r--r--railties/test/application/rake/notes_test.rb4
-rw-r--r--railties/test/generators/controller_generator_test.rb5
86 files changed, 771 insertions, 217 deletions
diff --git a/Gemfile b/Gemfile
index 0ab41ec445..cff6ae4dec 100644
--- a/Gemfile
+++ b/Gemfile
@@ -61,12 +61,13 @@ platforms :ruby do
end
platforms :jruby do
- gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.7'
-
- group :db do
- gem 'activerecord-jdbcmysql-adapter', '>= 1.2.7'
- gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.7'
- end
+ git 'git://github.com/jruby/activerecord-jdbc-adapter.git' do
+ gem 'activerecord-jdbcsqlite3-adapter'
+ group :db do
+ gem 'activerecord-jdbcmysql-adapter'
+ gem 'activerecord-jdbcpostgresql-adapter'
+ end
+ end
end
# gems that are necessary for ActiveRecord tests with Oracle database
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb
index aedcd81e52..9a1a27c8ed 100644
--- a/actionmailer/lib/action_mailer/delivery_methods.rb
+++ b/actionmailer/lib/action_mailer/delivery_methods.rb
@@ -64,7 +64,7 @@ module ActionMailer
raise "Delivery method cannot be nil"
when Symbol
if klass = delivery_methods[method]
- mail.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {}))
+ mail.delivery_method(klass,(send(:"#{method}_settings") || {}).merge!(options || {}))
else
raise "Invalid delivery method #{method.inspect}"
end
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 20412c7bb2..61a037ea18 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -152,9 +152,6 @@ class MailDeliveryTest < ActiveSupport::TestCase
assert_equal "overridden", delivery_method_instance.settings[:user_name]
assert_equal "somethingobtuse", delivery_method_instance.settings[:password]
assert_equal delivery_method_instance.settings.merge(overridden_options), delivery_method_instance.settings
-
- # make sure that overriding delivery method options per mail instance doesn't affect the Base setting
- assert_equal settings, ActionMailer::Base.smtp_settings
end
test "non registered delivery methods raises errors" do
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index d848095e01..4e090ed526 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Fix an issue where :if and :unless controller action procs were being run
+ before checking for the correct action in the :only and :unless options.
+
+ Fixes #11799
+
+ *Nicholas Jakobsen*
+
* Fix an issue where `assert_dom_equal` and `assert_dom_not_equal` were
ignoring the passed failure message argument.
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 21c6191691..d6c941832f 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -38,7 +38,7 @@ module AbstractController
def _normalize_callback_option(options, from, to) # :nodoc:
if from = options[from]
from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ")
- options[to] = Array(options[to]) << from
+ options[to] = Array(options[to]).unshift(from)
end
end
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 1faecc850b..67d261db77 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -59,7 +59,7 @@ module ActionController
# <input type="text" name="post[address]" value="hyacintvej">
#
# A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
- # If the address input had been named <tt>post[address][street]</tt>, the params would have included
+ # If the address input had been named "post[address][street]", the params would have included
# <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
#
# == Sessions
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 943fc15026..0e5dc1fc6c 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -514,12 +514,11 @@ module ActionDispatch
@recall = recall.dup
@set = set
- normalize_recall!
normalize_options!
normalize_controller_action_id!
use_relative_controller!
normalize_controller!
- normalize_action!
+ handle_nil_action!
end
def controller
@@ -538,11 +537,6 @@ module ActionDispatch
end
end
- # Set 'index' as default action for recall
- def normalize_recall!
- @recall[:action] ||= 'index'
- end
-
def normalize_options!
# If an explicit :controller was given, always make :action explicit
# too, so that action expiry works as expected for things like
@@ -558,8 +552,8 @@ module ActionDispatch
options[:controller] = options[:controller].to_s
end
- if options.key?(:action)
- options[:action] = (options[:action] || 'index').to_s
+ if options[:action]
+ options[:action] = options[:action].to_s
end
end
@@ -569,6 +563,8 @@ module ActionDispatch
# :controller, :action or :id is not found, don't pull any
# more keys from the recall.
def normalize_controller_action_id!
+ @recall[:action] ||= 'index' if current_controller
+
use_recall_for(:controller) or return
use_recall_for(:action) or return
use_recall_for(:id)
@@ -590,11 +586,13 @@ module ActionDispatch
@options[:controller] = controller.sub(%r{^/}, '') if controller
end
- # Move 'index' action from options to recall
- def normalize_action!
- if @options[:action] == 'index'
- @recall[:action] = @options.delete(:action)
+ # This handles the case of action: nil being explicitly passed.
+ # It is identical to action: "index"
+ def handle_nil_action!
+ if options.has_key?(:action) && options[:action].nil?
+ options[:action] = 'index'
end
+ recall[:action] = options.delete(:action) if options[:action] == 'index'
end
# Generates a path from routes, returns [path, params].
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index 4c82625e8e..3b5d7ef446 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -208,6 +208,10 @@ class FilterTest < ActionController::TestCase
before_filter(ConditionalClassFilter, :ensure_login, Proc.new {|c| c.instance_variable_set(:"@ran_proc_filter1", true)}, :except => :show_without_filter) { |c| c.instance_variable_set(:"@ran_proc_filter2", true)}
end
+ class OnlyConditionalOptionsFilter < ConditionalFilterController
+ before_filter :ensure_login, :only => :index, :if => Proc.new {|c| c.instance_variable_set(:"@ran_conditional_index_proc", true) }
+ end
+
class ConditionalOptionsFilter < ConditionalFilterController
before_filter :ensure_login, :if => Proc.new { |c| true }
before_filter :clean_up_tmp, :if => Proc.new { |c| false }
@@ -649,6 +653,11 @@ class FilterTest < ActionController::TestCase
assert !assigns["ran_class_filter"]
end
+ def test_running_only_condition_and_conditional_options
+ test_process(OnlyConditionalOptionsFilter, "show")
+ assert_not assigns["ran_conditional_index_proc"]
+ end
+
def test_running_before_and_after_condition_filters
test_process(BeforeAndAfterConditionController)
assert_equal %w( ensure_login clean_up_tmp), assigns["ran_filter"]
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index 2d89969fd3..a9c62899b5 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -646,7 +646,6 @@ class RespondWithControllerTest < ActionController::TestCase
Mime::Type.register_alias('text/html', :iphone)
Mime::Type.register_alias('text/html', :touch)
Mime::Type.register('text/x-mobile', :mobile)
- Customer.send(:undef_method, :to_json) if Customer.method_defined?(:to_json)
end
def teardown
diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb
index 2b36a399bb..f4bdd3e1d4 100644
--- a/actionpack/test/controller/new_base/render_streaming_test.rb
+++ b/actionpack/test/controller/new_base/render_streaming_test.rb
@@ -92,7 +92,7 @@ module RenderStreaming
io.rewind
assert_match "(undefined method `invalid!' for nil:NilClass)", io.read
ensure
- ActionView::Base.logger = _old
+ ActionController::Base.logger = _old
end
end
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 98b34a872b..fd835795c0 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -775,10 +775,6 @@ class RenderTest < ActionController::TestCase
@request.host = "www.nextangle.com"
end
- def teardown
- ActionView::Base.logger = nil
- end
-
# :ported:
def test_simple_show
get :hello_world
diff --git a/actionview/actionview.gemspec b/actionview/actionview.gemspec
index cdac074973..87cb568300 100644
--- a/actionview/actionview.gemspec
+++ b/actionview/actionview.gemspec
@@ -5,7 +5,7 @@ Gem::Specification.new do |s|
s.name = 'actionview'
s.version = version
s.summary = 'Rendering framework putting the V in MVC (part of Rails).'
- s.description = 'Simple, battle-tested conventions and helpers for building web pages.'
+ s.description = ''
s.required_ruby_version = '>= 1.9.3'
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f53cb6ad51..904ed0e362 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,44 @@
+* Assign inet/cidr attribute with `nil` value for invalid address.
+
+ Example:
+
+ record = User.new
+
+ record.logged_in_from_ip # is type of an inet or a cidr
+
+ # Before:
+ record.logged_in_from_ip = 'bad ip address' # raise exception
+
+ # After:
+ record.logged_in_from_ip = 'bad ip address' # do not raise exception
+ record.logged_in_from_ip # => nil
+ record.logged_in_from_ip_before_type_cast # => 'bad ip address'
+
+ *Paul Nikitochkin*
+
+* `add_to_target` now accepts a second optional `skip_callbacks` argument
+
+ If truthy, it will skip the :before_add and :after_add callbacks.
+
+ *Ben Woosley*
+
+* Fix interactions between `:before_add` callbacks and nested attributes
+ assignment of `has_many` associations, when the association was not
+ yet loaded:
+
+ - A `:before_add` callback was being called when a nested attributes
+ assignment assigned to an existing record.
+
+ - Nested Attributes assignment did not affect the record in the
+ association target when a `:before_add` callback triggered the
+ loading of the association
+
+ *Jörg Schray*
+
+* Allow enable_extension migration method to be revertible.
+
+ *Eric Tipton*
+
* Type cast hstore values on write, so that the value is consistent
with reading from the database.
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 8ce02afef8..228c500f0a 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -290,7 +290,7 @@ module ActiveRecord
# Returns true if the collection is empty.
#
- # If the collection has been loaded
+ # If the collection has been loaded
# it is equivalent to <tt>collection.size.zero?</tt>. If the
# collection has not been loaded, it is equivalent to
# <tt>collection.exists?</tt>. If the collection has not already been
@@ -366,8 +366,8 @@ module ActiveRecord
target
end
- def add_to_target(record)
- callback(:before_add, record)
+ def add_to_target(record, skip_callbacks = false)
+ callback(:before_add, record) unless skip_callbacks
yield(record) if block_given?
if association_scope.distinct_value && index = @target.index(record)
@@ -376,7 +376,7 @@ module ActiveRecord
@target << record
end
- callback(:after_add, record)
+ callback(:after_add, record) unless skip_callbacks
set_inverse_instance(record)
record
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 97e1bc5379..32244b1755 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -39,8 +39,8 @@ module ActiveRecord
# Returns an array of the values of the first column in a select:
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
def select_values(arel, name = nil)
- result = select_rows(to_sql(arel, []), name)
- result.map { |v| v[0] }
+ select_rows(to_sql(arel, []), name)
+ .map { |v| v[0] }
end
# Returns an array of arrays containing the field values.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index d18b9c991f..c8fa4ef9af 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -15,7 +15,6 @@ module ActiveRecord
return "'#{quote_string(value)}'" unless column
case column.type
- when :binary then "'#{quote_string(column.string_to_binary(value))}'"
when :integer then value.to_i.to_s
when :float then value.to_f.to_s
else
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 0be4b5cb19..063b19871a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -16,9 +16,6 @@ module ActiveRecord
# +columns+ attribute of said TableDefinition object, in order to be used
# for generating a number of table creation or table changing SQL statements.
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key) #:nodoc:
- def string_to_binary(value)
- value
- end
def primary_key?
primary_key || type.to_sym == :primary_key
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 daa9d902fe..4b11ea795c 100755..100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -246,8 +246,8 @@ module ActiveRecord
# QUOTING ==================================================
def quote(value, column = nil)
- if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
- s = column.class.string_to_binary(value).unpack("H*")[0]
+ if value.kind_of?(String) && column && column.type == :binary
+ s = value.unpack("H*")[0]
"x'#{s}'"
elsif value.kind_of?(BigDecimal)
value.to_s("F")
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index cc02b313e1..bccfa41ad1 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -119,17 +119,7 @@ module ActiveRecord
type_cast(default)
end
- # Used to convert from Strings to BLOBs
- def string_to_binary(value)
- self.class.string_to_binary(value)
- end
-
class << self
- # Used to convert from Strings to BLOBs
- def string_to_binary(value)
- value
- end
-
# Used to convert from BLOBs to Strings
def binary_to_string(value)
value
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 92796c996e..92796c996e 100755..100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 15b5452850..15b5452850 100755..100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index 56dc9ea813..ef7b976d4f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -100,7 +100,11 @@ module ActiveRecord
if string.nil?
nil
elsif String === string
- IPAddr.new(string)
+ begin
+ IPAddr.new(string)
+ rescue ArgumentError
+ nil
+ end
else
string
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index e1475416eb..df489a5b1f 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -226,8 +226,8 @@ module ActiveRecord
# QUOTING ==================================================
def quote(value, column = nil)
- if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
- s = column.class.string_to_binary(value).unpack("H*")[0]
+ if value.kind_of?(String) && column && column.type == :binary
+ s = value.unpack("H*")[0]
"x'#{s}'"
else
super
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index 9782a48055..01c73be849 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -73,7 +73,7 @@ module ActiveRecord
[:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
:rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
:change_column_default, :add_reference, :remove_reference, :transaction,
- :drop_join_table, :drop_table, :execute_block,
+ :drop_join_table, :drop_table, :execute_block, :enable_extension,
:change_column, :execute, :remove_columns, # irreversible methods need to be here too
].each do |method|
class_eval <<-EOV, __FILE__, __LINE__ + 1
@@ -100,6 +100,7 @@ module ActiveRecord
add_column: :remove_column,
add_timestamps: :remove_timestamps,
add_reference: :remove_reference,
+ enable_extension: :disable_extension
}.each do |cmd, inv|
[[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
class_eval <<-EOV, __FILE__, __LINE__ + 1
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index e53e8553ad..df28451bb7 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -465,19 +465,17 @@ module ActiveRecord
association.build(attributes.except(*UNASSIGNABLE_KEYS))
end
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
- unless association.loaded? || call_reject_if(association_name, attributes)
+ unless call_reject_if(association_name, attributes)
# Make sure we are operating on the actual object which is in the association's
# proxy_target array (either by finding it, or adding it if not found)
- target_record = association.target.detect { |record| record == existing_record }
-
+ # Take into account that the proxy_target may have changed due to callbacks
+ target_record = association.target.detect { |record| record.id.to_s == attributes['id'].to_s }
if target_record
existing_record = target_record
else
- association.add_to_target(existing_record)
+ association.add_to_target(existing_record, :skip_callbacks)
end
- end
- if !call_reject_if(association_name, attributes)
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
end
else
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 8a311039d5..daccab762f 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -320,11 +320,14 @@ db_namespace = namespace :db do
# desc "Recreate the test database from an existent schema.rb file"
task :load_schema => 'db:test:purge' do
begin
+ should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
ActiveRecord::Schema.verbose = false
db_namespace["schema:load"].invoke
ensure
- ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
+ if should_reconnect
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
+ end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 73d154e03e..f428f160cf 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -394,7 +394,7 @@ module ActiveRecord
# returns either nil or the inverse association name that it finds.
def automatic_inverse_of
if can_find_inverse_of_automatically?(self)
- inverse_name = active_record.name.downcase.to_sym
+ inverse_name = ActiveSupport::Inflector.underscore(active_record.name).to_sym
begin
reflection = klass.reflect_on_association(inverse_name)
@@ -413,7 +413,7 @@ module ActiveRecord
end
# Checks if the inverse reflection that is returned from the
- # +set_automatic_inverse_of+ method is a valid reflection. We must
+ # +automatic_inverse_of+ method is a valid reflection. We must
# make sure that the reflection's active_record name matches up
# with the current reflection's klass name.
#
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index 489efac932..b8dd35c4c5 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -66,7 +66,7 @@ class PostgresqlByteaTest < ActiveRecord::TestCase
def test_write_value
data = "\u001F"
record = ByteaDataType.create(payload: data)
- refute record.new_record?
+ assert_not record.new_record?
assert_equal(data, record.payload)
end
@@ -74,14 +74,14 @@ class PostgresqlByteaTest < ActiveRecord::TestCase
data = File.read(File.join(File.dirname(__FILE__), '..', '..', '..', 'assets', 'example.log'))
assert(data.size > 1)
record = ByteaDataType.create(payload: data)
- refute record.new_record?
+ assert_not record.new_record?
assert_equal(data, record.payload)
assert_equal(data, ByteaDataType.where(id: record.id).first.payload)
end
def test_write_nil
record = ByteaDataType.create(payload: nil)
- refute record.new_record?
+ assert_not record.new_record?
assert_equal(nil, record.payload)
assert_equal(nil, ByteaDataType.where(id: record.id).first.payload)
end
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index e946b8bb22..75b6f4f8ce 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -311,11 +311,11 @@ _SQL
def test_update_tstzrange
skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
new_tstzrange = Time.parse('2010-01-01 14:30:00 CDT')...Time.parse('2011-02-02 14:30:00 CET')
- assert @first_range.tstz_range = new_tstzrange
+ @first_range.tstz_range = new_tstzrange
assert @first_range.save
assert @first_range.reload
assert_equal new_tstzrange, @first_range.tstz_range
- assert @first_range.tstz_range = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2010-01-01 13:30:00 +0000')
+ @first_range.tstz_range = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2010-01-01 13:30:00 +0000')
assert @first_range.save
assert @first_range.reload
assert_nil @first_range.tstz_range
@@ -335,11 +335,11 @@ _SQL
skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
tz = ::ActiveRecord::Base.default_timezone
new_tsrange = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 2, 2, 14, 30, 0)
- assert @first_range.ts_range = new_tsrange
+ @first_range.ts_range = new_tsrange
assert @first_range.save
assert @first_range.reload
assert_equal new_tsrange, @first_range.ts_range
- assert @first_range.ts_range = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2010, 1, 1, 14, 30, 0)
+ @first_range.ts_range = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2010, 1, 1, 14, 30, 0)
assert @first_range.save
assert @first_range.reload
assert_nil @first_range.ts_range
@@ -357,11 +357,11 @@ _SQL
def test_update_numrange
skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
new_numrange = BigDecimal.new('0.5')...BigDecimal.new('1')
- assert @first_range.num_range = new_numrange
+ @first_range.num_range = new_numrange
assert @first_range.save
assert @first_range.reload
assert_equal new_numrange, @first_range.num_range
- assert @first_range.num_range = BigDecimal.new('0.5')...BigDecimal.new('0.5')
+ @first_range.num_range = BigDecimal.new('0.5')...BigDecimal.new('0.5')
assert @first_range.save
assert @first_range.reload
assert_nil @first_range.num_range
@@ -379,11 +379,11 @@ _SQL
def test_update_daterange
skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
new_daterange = Date.new(2012, 2, 3)...Date.new(2012, 2, 10)
- assert @first_range.date_range = new_daterange
+ @first_range.date_range = new_daterange
assert @first_range.save
assert @first_range.reload
assert_equal new_daterange, @first_range.date_range
- assert @first_range.date_range = Date.new(2012, 2, 3)...Date.new(2012, 2, 3)
+ @first_range.date_range = Date.new(2012, 2, 3)...Date.new(2012, 2, 3)
assert @first_range.save
assert @first_range.reload
assert_nil @first_range.date_range
@@ -401,11 +401,11 @@ _SQL
def test_update_int4range
skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
new_int4range = 6...10
- assert @first_range.int4_range = new_int4range
+ @first_range.int4_range = new_int4range
assert @first_range.save
assert @first_range.reload
assert_equal new_int4range, @first_range.int4_range
- assert @first_range.int4_range = 3...3
+ @first_range.int4_range = 3...3
assert @first_range.save
assert @first_range.reload
assert_nil @first_range.int4_range
@@ -423,11 +423,11 @@ _SQL
def test_update_int8range
skip "PostgreSQL 9.2 required for range datatypes" unless @connection.supports_ranges?
new_int8range = 60000...10000000
- assert @first_range.int8_range = new_int8range
+ @first_range.int8_range = new_int8range
assert @first_range.save
assert @first_range.reload
assert_equal new_int8range, @first_range.int8_range
- assert @first_range.int8_range = 39999...39999
+ @first_range.int8_range = 39999...39999
assert @first_range.save
assert @first_range.reload
assert_nil @first_range.int8_range
@@ -435,10 +435,10 @@ _SQL
def test_update_tsvector
new_text_vector = "'new' 'text' 'vector'"
- assert @first_tsvector.text_vector = new_text_vector
+ @first_tsvector.text_vector = new_text_vector
assert @first_tsvector.save
assert @first_tsvector.reload
- assert @first_tsvector.text_vector = new_text_vector
+ @first_tsvector.text_vector = new_text_vector
assert @first_tsvector.save
assert @first_tsvector.reload
assert_equal new_text_vector, @first_tsvector.text_vector
@@ -479,11 +479,11 @@ _SQL
def test_update_integer_array
new_value = [32800,95000,29350,17000]
- assert @first_array.commission_by_quarter = new_value
+ @first_array.commission_by_quarter = new_value
assert @first_array.save
assert @first_array.reload
assert_equal new_value, @first_array.commission_by_quarter
- assert @first_array.commission_by_quarter = new_value
+ @first_array.commission_by_quarter = new_value
assert @first_array.save
assert @first_array.reload
assert_equal new_value, @first_array.commission_by_quarter
@@ -491,11 +491,11 @@ _SQL
def test_update_text_array
new_value = ['robby','robert','rob','robbie']
- assert @first_array.nicknames = new_value
+ @first_array.nicknames = new_value
assert @first_array.save
assert @first_array.reload
assert_equal new_value, @first_array.nicknames
- assert @first_array.nicknames = new_value
+ @first_array.nicknames = new_value
assert @first_array.save
assert @first_array.reload
assert_equal new_value, @first_array.nicknames
@@ -503,7 +503,7 @@ _SQL
def test_update_money
new_value = BigDecimal.new('123.45')
- assert @first_money.wealth = new_value
+ @first_money.wealth = new_value
assert @first_money.save
assert @first_money.reload
assert_equal new_value, @first_money.wealth
@@ -512,8 +512,8 @@ _SQL
def test_update_number
new_single = 789.012
new_double = 789012.345
- assert @first_number.single = new_single
- assert @first_number.double = new_double
+ @first_number.single = new_single
+ @first_number.double = new_double
assert @first_number.save
assert @first_number.reload
assert_equal new_single, @first_number.single
@@ -521,7 +521,7 @@ _SQL
end
def test_update_time
- assert @first_time.time_interval = '2 years 3 minutes'
+ @first_time.time_interval = '2 years 3 minutes'
assert @first_time.save
assert @first_time.reload
assert_equal '2 years 00:03:00', @first_time.time_interval
@@ -531,9 +531,9 @@ _SQL
new_inet_address = '10.1.2.3/32'
new_cidr_address = '10.0.0.0/8'
new_mac_address = 'bc:de:f0:12:34:56'
- assert @first_network_address.cidr_address = new_cidr_address
- assert @first_network_address.inet_address = new_inet_address
- assert @first_network_address.mac_address = new_mac_address
+ @first_network_address.cidr_address = new_cidr_address
+ @first_network_address.inet_address = new_inet_address
+ @first_network_address.mac_address = new_mac_address
assert @first_network_address.save
assert @first_network_address.reload
assert_equal @first_network_address.cidr_address, new_cidr_address
@@ -544,8 +544,8 @@ _SQL
def test_update_bit_string
new_bit_string = '11111111'
new_bit_string_varying = '0xFF'
- assert @first_bit_string.bit_string = new_bit_string
- assert @first_bit_string.bit_string_varying = new_bit_string_varying
+ @first_bit_string.bit_string = new_bit_string
+ @first_bit_string.bit_string_varying = new_bit_string_varying
assert @first_bit_string.save
assert @first_bit_string.reload
assert_equal new_bit_string, @first_bit_string.bit_string
@@ -558,9 +558,23 @@ _SQL
assert_raise(ActiveRecord::StatementInvalid) { assert @first_bit_string.save }
end
+ def test_invalid_network_address
+ @first_network_address.cidr_address = 'invalid addr'
+ assert_nil @first_network_address.cidr_address
+ assert_equal 'invalid addr', @first_network_address.cidr_address_before_type_cast
+ assert @first_network_address.save
+
+ @first_network_address.reload
+
+ @first_network_address.inet_address = 'invalid addr'
+ assert_nil @first_network_address.inet_address
+ assert_equal 'invalid addr', @first_network_address.inet_address_before_type_cast
+ assert @first_network_address.save
+ end
+
def test_update_oid
new_value = 567890
- assert @first_oid.obj_id = new_value
+ @first_oid.obj_id = new_value
assert @first_oid.save
assert @first_oid.reload
assert_equal new_value, @first_oid.obj_id
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 724d1cbbf8..941e851aae 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -654,7 +654,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
sarah = Person.create!(:first_name => 'Sarah', :primary_contact_id => people(:susan).id, :gender => 'F', :number1_fan_id => 1)
john = Person.create!(:first_name => 'John', :primary_contact_id => sarah.id, :gender => 'M', :number1_fan_id => 1)
assert_equal sarah.agents, [john]
- assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
+ assert_equal people(:susan).agents.map(&:agents).flatten, people(:susan).agents_of_agents
end
def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 71cf1237e8..2477e60e51 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -9,10 +9,24 @@ require 'models/rating'
require 'models/comment'
require 'models/car'
require 'models/bulb'
+require 'models/mixed_case_monkey'
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars
+ def test_has_one_and_belongs_to_should_find_inverse_automatically_on_multiple_word_name
+ monkey_reflection = MixedCaseMonkey.reflect_on_association(:man)
+ man_reflection = Man.reflect_on_association(:mixed_case_monkey)
+
+ assert_respond_to monkey_reflection, :has_inverse?
+ assert monkey_reflection.has_inverse?, "The monkey reflection should have an inverse"
+ assert_equal man_reflection, monkey_reflection.inverse_of, "The monkey reflection's inverse should be the man reflection"
+
+ assert_respond_to man_reflection, :has_inverse?
+ assert man_reflection.has_inverse?, "The man reflection should have an inverse"
+ assert_equal monkey_reflection, man_reflection.inverse_of, "The man reflection's inverse should be the monkey reflection"
+ end
+
def test_has_one_and_belongs_to_should_find_inverse_automatically
car_reflection = Car.reflect_on_association(:bulb)
bulb_reflection = Bulb.reflect_on_association(:car)
@@ -406,7 +420,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
interest = Interest.create!(man: man)
man.interests.find(interest.id)
- refute man.interests.loaded?
+ assert_not man.interests.loaded?
end
def test_raise_record_not_found_error_when_invalid_ids_are_passed
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index c91cf89f6d..c91cf89f6d 100755..100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 2cad8a6d96..1b205d372f 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -242,6 +242,16 @@ module ActiveRecord
add = @recorder.inverse_of :remove_belongs_to, [:table, :user]
assert_equal [:add_reference, [:table, :user], nil], add
end
+
+ def test_invert_enable_extension
+ disable = @recorder.inverse_of :enable_extension, ['uuid-ossp']
+ assert_equal [:disable_extension, ['uuid-ossp'], nil], disable
+ end
+
+ def test_invert_disable_extension
+ enable = @recorder.inverse_of :disable_extension, ['uuid-ossp']
+ assert_equal [:enable_extension, ['uuid-ossp'], nil], enable
+ end
end
end
end
diff --git a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
new file mode 100644
index 0000000000..43a69928b6
--- /dev/null
+++ b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
@@ -0,0 +1,144 @@
+require "cases/helper"
+require "models/pirate"
+require "models/bird"
+
+class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
+ Pirate.has_many(:birds_with_add_load,
+ :class_name => "Bird",
+ :before_add => proc { |p,b|
+ @@add_callback_called << b
+ p.birds_with_add_load.to_a
+ })
+ Pirate.has_many(:birds_with_add,
+ :class_name => "Bird",
+ :before_add => proc { |p,b| @@add_callback_called << b })
+
+ Pirate.accepts_nested_attributes_for(:birds_with_add_load,
+ :birds_with_add,
+ :allow_destroy => true)
+
+ def setup
+ @@add_callback_called = []
+ @pirate = Pirate.new.tap do |pirate|
+ pirate.catchphrase = "Don't call me!"
+ pirate.birds_attributes = [{:name => 'Bird1'},{:name => 'Bird2'}]
+ pirate.save!
+ end
+ @birds = @pirate.birds.to_a
+ end
+
+ def bird_to_update
+ @birds[0]
+ end
+
+ def bird_to_destroy
+ @birds[1]
+ end
+
+ def existing_birds_attributes
+ @birds.map do |bird|
+ bird.attributes.slice("id","name")
+ end
+ end
+
+ def new_birds
+ @pirate.birds_with_add.to_a - @birds
+ end
+
+ def new_bird_attributes
+ [{'name' => "New Bird"}]
+ end
+
+ def destroy_bird_attributes
+ [{'id' => bird_to_destroy.id.to_s, "_destroy" => true}]
+ end
+
+ def update_new_and_destroy_bird_attributes
+ [{'id' => @birds[0].id.to_s, 'name' => 'New Name'},
+ {'name' => "New Bird"},
+ {'id' => bird_to_destroy.id.to_s, "_destroy" => true}]
+ end
+
+ # Characterizing when :before_add callback is called
+ test ":before_add called for new bird when not loaded" do
+ assert_not @pirate.birds_with_add.loaded?
+ @pirate.birds_with_add_attributes = new_bird_attributes
+ assert_new_bird_with_callback_called
+ end
+
+ test ":before_add called for new bird when loaded" do
+ @pirate.birds_with_add.load_target
+ @pirate.birds_with_add_attributes = new_bird_attributes
+ assert_new_bird_with_callback_called
+ end
+
+ def assert_new_bird_with_callback_called
+ assert_equal(1, new_birds.size)
+ assert_equal(new_birds, @@add_callback_called)
+ end
+
+ test ":before_add not called for identical assignment when not loaded" do
+ assert_not @pirate.birds_with_add.loaded?
+ @pirate.birds_with_add_attributes = existing_birds_attributes
+ assert_callbacks_not_called
+ end
+
+ test ":before_add not called for identical assignment when loaded" do
+ @pirate.birds_with_add.load_target
+ @pirate.birds_with_add_attributes = existing_birds_attributes
+ assert_callbacks_not_called
+ end
+
+ test ":before_add not called for destroy assignment when not loaded" do
+ assert_not @pirate.birds_with_add.loaded?
+ @pirate.birds_with_add_attributes = destroy_bird_attributes
+ assert_callbacks_not_called
+ end
+
+ test ":before_add not called for deletion assignment when loaded" do
+ @pirate.birds_with_add.load_target
+ @pirate.birds_with_add_attributes = destroy_bird_attributes
+ assert_callbacks_not_called
+ end
+
+ def assert_callbacks_not_called
+ assert_empty new_birds
+ assert_empty @@add_callback_called
+ end
+
+ # Ensuring that the records in the association target are updated,
+ # whether the association is loaded before or not
+ test "Assignment updates records in target when not loaded" do
+ assert_not @pirate.birds_with_add.loaded?
+ @pirate.birds_with_add_attributes = update_new_and_destroy_bird_attributes
+ assert_assignment_affects_records_in_target(:birds_with_add)
+ end
+
+ test "Assignment updates records in target when loaded" do
+ @pirate.birds_with_add.load_target
+ @pirate.birds_with_add_attributes = update_new_and_destroy_bird_attributes
+ assert_assignment_affects_records_in_target(:birds_with_add)
+ end
+
+ test("Assignment updates records in target when not loaded" +
+ " and callback loads target") do
+ assert_not @pirate.birds_with_add_load.loaded?
+ @pirate.birds_with_add_load_attributes = update_new_and_destroy_bird_attributes
+ assert_assignment_affects_records_in_target(:birds_with_add_load)
+ end
+
+ test("Assignment updates records in target when loaded" +
+ " and callback loads target") do
+ @pirate.birds_with_add_load.load_target
+ @pirate.birds_with_add_load_attributes = update_new_and_destroy_bird_attributes
+ assert_assignment_affects_records_in_target(:birds_with_add_load)
+ end
+
+ def assert_assignment_affects_records_in_target(association_name)
+ association = @pirate.send(association_name)
+ assert association.detect {|b| b == bird_to_update }.name_changed?,
+ 'Update record not updated'
+ assert association.detect {|b| b == bird_to_destroy }.marked_for_destruction?,
+ 'Destroy record not marked for destruction'
+ end
+end
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 85fbe01416..44b2064110 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -184,25 +184,6 @@ module ActiveRecord
assert_equal "'lo\\\\l'", @quoter.quote('lo\l', FakeColumn.new(:binary))
end
- def test_quote_binary_with_string_to_binary
- col = Class.new(FakeColumn) {
- def string_to_binary(value)
- 'foo'
- end
- }.new(:binary)
- assert_equal "'foo'", @quoter.quote('lo\l', col)
- end
-
- def test_quote_as_mb_chars_binary_column_with_string_to_binary
- col = Class.new(FakeColumn) {
- def string_to_binary(value)
- 'foo'
- end
- }.new(:binary)
- string = ActiveSupport::Multibyte::Chars.new('lo\l')
- assert_equal "'foo'", @quoter.quote(string, col)
- end
-
def test_string_with_crazy_column
assert_equal "'lo\\\\l'", @quoter.quote('lo\l', FakeColumn.new(:foo))
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index bf9d395b2d..e1a760d240 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -170,6 +170,10 @@ class RelationTest < ActiveRecord::TestCase
assert_equal topics(:fourth).title, topics.first.title
end
+ def test_order_with_hash_and_symbol_generates_the_same_sql
+ assert_equal Topic.order(:id).to_sql, Topic.order(:id => :asc).to_sql
+ end
+
def test_raising_exception_on_invalid_hash_params
assert_raise(ArgumentError) { Topic.order(:name, "id DESC", :id => :DeSc) }
end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index abfc90474c..a5cb22aaf6 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -453,11 +453,6 @@ class TransactionTest < ActiveRecord::TestCase
raise ActiveRecord::Rollback
end
end
-
- ensure
- Topic.reset_column_information # reset the column information to get correct reading
- Topic.connection.remove_column('topics', 'stuff') if Topic.column_names.include?('stuff')
- Topic.reset_column_information # reset the column information again for other tests
end
def test_transactions_state_from_rollback
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 7dad8041f3..feb828de31 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -8,7 +8,9 @@ class Author < ActiveRecord::Base
has_many :posts_sorted_by_id_limited, -> { order('posts.id').limit(1) }, :class_name => "Post"
has_many :posts_with_categories, -> { includes(:categories) }, :class_name => "Post"
has_many :posts_with_comments_and_categories, -> { includes(:comments, :categories).order("posts.id") }, :class_name => "Post"
+ has_many :posts_containing_the_letter_a, :class_name => "Post"
has_many :posts_with_special_categorizations, :class_name => 'PostWithSpecialCategorization'
+ has_many :posts_with_extension, :class_name => "Post"
has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post'
has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post'
has_many :comments, through: :posts do
@@ -30,6 +32,7 @@ class Author < ActiveRecord::Base
has_many :welcome_posts, -> { where(:title => 'Welcome to the weblog') }, :class_name => 'Post'
has_many :comments_desc, -> { order('comments.id DESC') }, :through => :posts, :source => :comments
+ has_many :limited_comments, -> { limit(1) }, :through => :posts, :source => :comments
has_many :funky_comments, :through => :posts, :source => :comments
has_many :ordered_uniq_comments, -> { distinct.order('comments.id') }, :through => :posts, :source => :comments
has_many :ordered_uniq_comments_desc, -> { distinct.order('comments.id DESC') }, :through => :posts, :source => :comments
diff --git a/activerecord/test/models/auto_id.rb b/activerecord/test/models/auto_id.rb
index 82c6544bd5..d720e2be5e 100644
--- a/activerecord/test/models/auto_id.rb
+++ b/activerecord/test/models/auto_id.rb
@@ -1,4 +1,4 @@
class AutoId < ActiveRecord::Base
- self.table_name = "auto_id_tests"
- self.primary_key = "auto_id"
+ def self.table_name () "auto_id_tests" end
+ def self.primary_key () "auto_id" end
end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index 6d257dbe7e..a14a9febba 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -1,9 +1,12 @@
class Car < ActiveRecord::Base
+
has_many :bulbs
has_many :funky_bulbs, class_name: 'FunkyBulb', dependent: :destroy
has_many :foo_bulbs, -> { where(:name => 'foo') }, :class_name => "Bulb"
+ has_many :frickinawesome_bulbs, -> { where :frickinawesome => true }, :class_name => "Bulb"
has_one :bulb
+ has_one :frickinawesome_bulb, -> { where :frickinawesome => true }, :class_name => "Bulb"
has_many :tyres
has_many :engines, :dependent => :destroy
diff --git a/activerecord/test/models/citation.rb b/activerecord/test/models/citation.rb
index 3d87eb795c..545aa8110d 100644
--- a/activerecord/test/models/citation.rb
+++ b/activerecord/test/models/citation.rb
@@ -1,3 +1,6 @@
class Citation < ActiveRecord::Base
belongs_to :reference_of, :class_name => "Book", :foreign_key => :book2_id
+
+ belongs_to :book1, :class_name => "Book", :foreign_key => :book1_id
+ belongs_to :book2, :class_name => "Book", :foreign_key => :book2_id
end
diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb
index 566e0873f1..816c5e6937 100644
--- a/activerecord/test/models/club.rb
+++ b/activerecord/test/models/club.rb
@@ -2,6 +2,7 @@ class Club < ActiveRecord::Base
has_one :membership
has_many :memberships, :inverse_of => false
has_many :members, :through => :memberships
+ has_many :current_memberships
has_one :sponsor
has_one :sponsored_member, :through => :sponsor, :source => :sponsorable, :source_type => "Member"
belongs_to :category
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 8104c607b5..c5d4ec0833 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -49,6 +49,7 @@ class Firm < Company
has_many :clients_like_ms, -> { where("name = 'Microsoft'").order("id") }, :class_name => "Client"
has_many :clients_like_ms_with_hash_conditions, -> { where(:name => 'Microsoft').order("id") }, :class_name => "Client"
has_many :plain_clients, :class_name => 'Client'
+ has_many :readonly_clients, -> { readonly }, :class_name => 'Client'
has_many :clients_using_primary_key, :class_name => 'Client',
:primary_key => 'name', :foreign_key => 'firm_name'
has_many :clients_using_primary_key_with_delete_all, :class_name => 'Client',
@@ -166,6 +167,7 @@ class ExclusivelyDependentFirm < Company
has_one :account, :foreign_key => "firm_id", :dependent => :delete
has_many :dependent_sanitized_conditional_clients_of_firm, -> { order("id").where("name = 'BigShot Inc.'") }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
has_many :dependent_conditional_clients_of_firm, -> { order("id").where("name = ?", 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
+ has_many :dependent_hash_conditional_clients_of_firm, -> { order("id").where(:name => 'BigShot Inc.') }, :foreign_key => "client_of", :class_name => "Client", :dependent => :delete_all
end
class SpecialClient < Client
diff --git a/activerecord/test/models/man.rb b/activerecord/test/models/man.rb
index 4bff92dc98..f4d127730c 100644
--- a/activerecord/test/models/man.rb
+++ b/activerecord/test/models/man.rb
@@ -6,4 +6,5 @@ class Man < ActiveRecord::Base
# These are "broken" inverse_of associations for the purposes of testing
has_one :dirty_face, :class_name => 'Face', :inverse_of => :dirty_man
has_many :secret_interests, :class_name => 'Interest', :inverse_of => :secret_man
+ has_one :mixed_case_monkey
end
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 72095f9236..cc47c7bc18 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -2,6 +2,7 @@ class Member < ActiveRecord::Base
has_one :current_membership
has_one :selected_membership
has_one :membership
+ has_many :fellow_members, :through => :club, :source => :members
has_one :club, :through => :current_membership
has_one :selected_club, :through => :selected_membership, :source => :club
has_one :favourite_club, -> { where "memberships.favourite = ?", true }, :through => :membership, :source => :club
diff --git a/activerecord/test/models/mixed_case_monkey.rb b/activerecord/test/models/mixed_case_monkey.rb
index 763baefd91..4d37371777 100644
--- a/activerecord/test/models/mixed_case_monkey.rb
+++ b/activerecord/test/models/mixed_case_monkey.rb
@@ -1,3 +1,5 @@
class MixedCaseMonkey < ActiveRecord::Base
self.primary_key = 'monkeyID'
+
+ belongs_to :man
end
diff --git a/activerecord/test/models/movie.rb b/activerecord/test/models/movie.rb
index c441be2bef..6384b4c801 100644
--- a/activerecord/test/models/movie.rb
+++ b/activerecord/test/models/movie.rb
@@ -1,3 +1,5 @@
class Movie < ActiveRecord::Base
- self.primary_key = "movieid"
+ def self.primary_key
+ "movieid"
+ end
end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 77564ffad4..93a7a2073c 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -122,6 +122,7 @@ class Post < ActiveRecord::Base
has_many :secure_readers
has_many :readers_with_person, -> { includes(:person) }, :class_name => "Reader"
has_many :people, :through => :readers
+ has_many :secure_people, :through => :secure_readers
has_many :single_people, :through => :readers
has_many :people_with_callbacks, :source=>:person, :through => :readers,
:before_add => lambda {|owner, reader| log(:added, :before, reader.first_name) },
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index 7f42a4b1f8..c094b726b4 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -1,6 +1,7 @@
class Project < ActiveRecord::Base
has_and_belongs_to_many :developers, -> { distinct.order 'developers.name desc, developers.id desc' }
has_and_belongs_to_many :readonly_developers, -> { readonly }, :class_name => "Developer"
+ has_and_belongs_to_many :selected_developers, -> { distinct.select "developers.*" }, :class_name => "Developer"
has_and_belongs_to_many :non_unique_developers, -> { order 'developers.name desc, developers.id desc' }, :class_name => 'Developer'
has_and_belongs_to_many :limited_developers, -> { limit 1 }, :class_name => "Developer"
has_and_belongs_to_many :developers_named_david, -> { where("name = 'David'").distinct }, :class_name => "Developer"
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 40c8e97fc2..17035bf338 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -34,6 +34,7 @@ class Topic < ActiveRecord::Base
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
has_many :approved_replies, -> { approved }, class_name: 'Reply', foreign_key: "parent_id", counter_cache: 'replies_count'
+ has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title"
has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index be97b744c8..81c6246c5c 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,19 @@
+* Add String#remove(pattern) as a short-hand for the common pattern of String#gsub(pattern, '')
+
+ *DHH*
+
+* Adds a new deprecation behaviour that raises an exception. Throwing this
+ line into +config/environments/development.rb+
+
+ ActiveSupport::Deprecation.behavior = :raise
+
+ will cause the application to raise an +ActiveSupport::DeprecationException+
+ on deprecations.
+
+ Use this for aggressive deprecation cleanups.
+
+ *Xavier Noria*
+
* Remove 'cow' => 'kine' irregular inflection from default inflections.
*Andrew White*
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index af048d0c85..c60e833441 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -129,7 +129,7 @@ class Date
options.fetch(:day, day)
)
end
-
+
# Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there.
def compare_with_coercion(other)
if other.is_a?(Time)
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index fbf2877117..8930376ac8 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -235,6 +235,7 @@ module ActiveSupport
value.map! { |i| deep_to_h(i) }
value.length > 1 ? value : value.first
end
+
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 48190e1e66..534bbe3c42 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -47,7 +47,7 @@ class Object
end
# Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
- # does not implement the tried method.
+ # does not implemented the tried method.
def try!(*a, &b)
if a.empty? && block_given?
yield self
diff --git a/activesupport/lib/active_support/core_ext/string/filters.rb b/activesupport/lib/active_support/core_ext/string/filters.rb
index a4cacaf789..49c0df6026 100644
--- a/activesupport/lib/active_support/core_ext/string/filters.rb
+++ b/activesupport/lib/active_support/core_ext/string/filters.rb
@@ -20,6 +20,16 @@ class String
self
end
+ # Returns a new string with all occurrences of the pattern removed. Short-hand for String#gsub(pattern, '').
+ def remove(pattern)
+ gsub pattern, ''
+ end
+
+ # Alters the string by removing all occurrences of the pattern. Short-hand for String#gsub!(pattern, '').
+ def remove!(pattern)
+ gsub! pattern, ''
+ end
+
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
#
# 'Once upon a time in a world far far away'.truncate(27)
diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb
index 90db180124..328b8c320a 100644
--- a/activesupport/lib/active_support/deprecation/behaviors.rb
+++ b/activesupport/lib/active_support/deprecation/behaviors.rb
@@ -1,14 +1,24 @@
require "active_support/notifications"
module ActiveSupport
+ class DeprecationException < StandardError
+ end
+
class Deprecation
# Default warning behaviors per Rails.env.
DEFAULT_BEHAVIORS = {
- :stderr => Proc.new { |message, callstack|
+ raise: ->(message, callstack) {
+ e = DeprecationException.new(message)
+ e.set_backtrace(callstack)
+ raise e
+ },
+
+ stderr: ->(message, callstack) {
$stderr.puts(message)
$stderr.puts callstack.join("\n ") if debug
},
- :log => Proc.new { |message, callstack|
+
+ log: ->(message, callstack) {
logger =
if defined?(Rails) && Rails.logger
Rails.logger
@@ -19,11 +29,13 @@ module ActiveSupport
logger.warn message
logger.debug callstack.join("\n ") if debug
},
- :notify => Proc.new { |message, callstack|
+
+ notify: ->(message, callstack) {
ActiveSupport::Notifications.instrument("deprecation.rails",
:message => message, :callstack => callstack)
},
- :silence => Proc.new { |message, callstack| }
+
+ silence: ->(message, callstack) {},
}
module Behavior
@@ -40,6 +52,7 @@ module ActiveSupport
#
# Available behaviors:
#
+ # [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
# [+stderr+] Log all deprecation warnings to +$stderr+.
# [+log+] Log all deprecation warnings to +Rails.logger+.
# [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
@@ -52,7 +65,7 @@ module ActiveSupport
# ActiveSupport::Deprecation.behavior = :stderr
# ActiveSupport::Deprecation.behavior = [:stderr, :log]
# ActiveSupport::Deprecation.behavior = MyCustomHandler
- # ActiveSupport::Deprecation.behavior = proc { |message, callstack|
+ # ActiveSupport::Deprecation.behavior = ->(message, callstack) {
# # custom stuff
# }
def behavior=(behavior)
diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb
index 2cb1f408b6..00727ef73c 100644
--- a/activesupport/lib/active_support/duration.rb
+++ b/activesupport/lib/active_support/duration.rb
@@ -70,13 +70,13 @@ module ActiveSupport
alias :until :ago
def inspect #:nodoc:
- consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
- parts = [:years, :months, :days, :minutes, :seconds].map do |length|
- n = consolidated[length]
- "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
- end.compact
- parts = ["0 seconds"] if parts.empty?
- parts.to_sentence(:locale => :en)
+ val_for = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
+ [:years, :months, :days, :minutes, :seconds].
+ select {|unit| val_for[unit].nonzero?}.
+ tap {|units| units << :seconds if units.empty?}.
+ map {|unit| [unit, val_for[unit]]}.
+ map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
+ to_sentence(:locale => :en)
end
def as_json(options = nil) #:nodoc:
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index e03bb4ca0f..a33e2c58a9 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -41,7 +41,7 @@ module ActiveSupport
end
# +InheritableOptions+ provides a constructor to build an +OrderedOptions+
- # hash inherited from the another hash.
+ # hash inherited from another hash.
#
# Use this if you already have some hash and you want to create a new one based on it.
#
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 12126fb9ef..2537af0394 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -277,6 +277,11 @@ class StringInflectionsTest < ActiveSupport::TestCase
def test_truncate_should_not_be_html_safe
assert !"Hello World!".truncate(12).html_safe?
end
+
+ def test_remove
+ assert_equal "Summer", "Fast Summer".remove(/Fast /)
+ assert_equal "Summer", "Fast Summer".remove!(/Fast /)
+ end
def test_constantize
run_constantize_tests_on do |string|
diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb
index 9616e42f44..9674851b9d 100644
--- a/activesupport/test/deprecation_test.rb
+++ b/activesupport/test/deprecation_test.rb
@@ -98,6 +98,22 @@ class DeprecationTest < ActiveSupport::TestCase
assert_match(/foo=nil/, @b)
end
+ def test_raise_behaviour
+ ActiveSupport::Deprecation.behavior = :raise
+
+ message = 'Revise this deprecated stuff now!'
+ callstack = %w(foo bar baz)
+
+ begin
+ ActiveSupport::Deprecation.behavior.first.call(message, callstack)
+ rescue ActiveSupport::DeprecationException => e
+ assert_equal message, e.message
+ assert_equal callstack, e.backtrace
+ else
+ flunk 'the :raise deprecation behaviour should raise the expected exception'
+ end
+ end
+
def test_default_stderr_behavior
ActiveSupport::Deprecation.behavior = :stderr
behavior = ActiveSupport::Deprecation.behavior.first
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
index e0f85f4e7c..ce91c443e1 100644
--- a/activesupport/test/transliterate_test.rb
+++ b/activesupport/test/transliterate_test.rb
@@ -3,6 +3,7 @@ require 'abstract_unit'
require 'active_support/inflector/transliterate'
class TransliterateTest < ActiveSupport::TestCase
+
def test_transliterate_should_not_change_ascii_chars
(0..127).each do |byte|
char = [byte].pack("U")
@@ -23,13 +24,12 @@ class TransliterateTest < ActiveSupport::TestCase
def test_transliterate_should_work_with_custom_i18n_rules_and_uncomposed_utf8
char = [117, 776].pack("U*") # "ü" as ASCII "u" plus COMBINING DIAERESIS
I18n.backend.store_translations(:de, :i18n => {:transliterate => {:rule => {"ü" => "ue"}}})
- default_locale, I18n.locale = I18n.locale, :de
+ I18n.locale = :de
assert_equal "ue", ActiveSupport::Inflector.transliterate(char)
- ensure
- I18n.locale = default_locale
end
def test_transliterate_should_allow_a_custom_replacement_char
assert_equal "a*b", ActiveSupport::Inflector.transliterate("a索b", "*")
end
+
end
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index d992028323..a025279e16 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -106,11 +106,7 @@ module XmlMiniTest
module Nokogiri end
setup do
- @xml, @default_backend = ActiveSupport::XmlMini, ActiveSupport::XmlMini.backend
- end
-
- teardown do
- ActiveSupport::XmlMini.backend = @default_backend
+ @xml = ActiveSupport::XmlMini
end
test "#with_backend should switch backend and then switch back" do
@@ -139,11 +135,7 @@ module XmlMiniTest
module LibXML end
setup do
- @xml, @default_backend = ActiveSupport::XmlMini, ActiveSupport::XmlMini.backend
- end
-
- teardown do
- ActiveSupport::XmlMini.backend = @default_backend
+ @xml = ActiveSupport::XmlMini
end
test "#with_backend should be thread-safe" do
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index 8be7a86d20..ff4d30b4c0 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -173,6 +173,19 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ
* `Object#try` will now return nil instead of raise a NoMethodError if the receiving object does not implement the method, but you can still get the old behavior by using the new `Object#try!`.
+* `String#to_date` now raises `Argument Error: invalid date` instead of `NoMethodError: undefined method 'div' for nil:NilClass`
+ when given an invalid date. It is now the same as `Date.parse`, and it accepts more invalid dates than 3.x, such as:
+
+ ```
+ # ActiveSupport 3.x
+ "asdf".to_date # => NoMethodError: undefined method `div' for nil:NilClass
+ "333".to_date # => NoMethodError: undefined method `div' for nil:NilClass
+
+ # ActiveSupport 4
+ "asdf".to_date # => ArgumentError: invalid date
+ "333".to_date # => Fri, 29 Nov 2013
+ ```
+
### Deprecations
* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from MiniTest instead.
diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb
index 0a0a958e30..9210c40c17 100644
--- a/guides/source/_welcome.html.erb
+++ b/guides/source/_welcome.html.erb
@@ -15,7 +15,7 @@
</p>
<% end %>
<p>
- The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.14/">http://guides.rubyonrails.org/v3.2.14/</a>.
+ The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.13/">http://guides.rubyonrails.org/v3.2.13/</a>.
</p>
<p>
The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>.
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index 6fce5a1dc2..f7d2016784 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1520,6 +1520,72 @@ number_with_precision(111.2345) # => 111.235
number_with_precision(111.2345, 2) # => 111.23
```
+### SanitizeHelper
+
+The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
+
+#### sanitize
+
+This sanitize helper will html encode all tags and strip all attributes that aren’t specifically allowed.
+
+```ruby
+sanitize @article.body
+```
+
+If either the :attributes or :tags options are passed, only the mentioned tags and attributes are allowed and nothing else.
+
+```ruby
+sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style)
+```
+
+To change defaults for multiple uses, for example adding table tags to the default:
+
+```ruby
+class Application < Rails::Application
+ config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
+end
+```
+
+#### sanitize_css(style)
+
+Sanitizes a block of CSS code.
+
+#### strip_links(html)
+Strips all link tags from text leaving just the link text.
+
+```ruby
+strip_links("<a href="http://rubyonrails.org">Ruby on Rails</a>")
+# => Ruby on Rails
+```
+
+```ruby
+strip_links("emails to <a href="mailto:me@email.com">me@email.com</a>.")
+# => emails to me@email.com.
+```
+
+```ruby
+strip_links('Blog: <a href="http://myblog.com/">Visit</a>.')
+# => Blog: Visit.
+```
+
+#### strip_tags(html)
+
+Strips all HTML tags from the html, including comments.
+This uses the html-scanner tokenizer and so its HTML parsing ability is limited by that of html-scanner.
+
+```ruby
+strip_tags("Strip <i>these</i> tags!")
+# => Strip these tags!
+```
+
+```ruby
+strip_tags("<b>Bold</b> no more! <a href='more.html'>See more</a>")
+# => Bold no more! See more
+```
+
+NB: The output may still contain unescaped ‘<’, ‘>’, ‘&’ characters and confuse browsers.
+
+
Localized Views
---------------
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 7fe9b8b4af..ce571c6f96 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -514,13 +514,7 @@ SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
Post.where.not(author: author)
```
-In other words, this query can be generated by calling `where` with no argument,
-then immediately chain with `not` passing `where` conditions. This will generate
-SQL code like this:
-
-```sql
-SELECT * FROM posts WHERE (author_id != 1)
-```
+In other words, this query can be generated by calling `where` with no argument, then immediately chain with `not` passing `where` conditions.
Ordering
--------
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 1915252122..ca023f7f66 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -96,13 +96,12 @@ INFO: The predicate for strings uses the Unicode-aware character class `[:space:
WARNING: Note that numbers are not mentioned. In particular, 0 and 0.0 are **not** blank.
-For example, this method from `ActionController::HttpAuthentication::Token::ControllerMethods` uses `blank?` for checking whether a token is present:
+For example, this method from `ActionDispatch::Session::AbstractStore` uses `blank?` for checking whether a session key is present:
```ruby
-def authenticate(controller, &login_procedure)
- token, options = token_and_options(controller.request)
- unless token.blank?
- login_procedure.call(token, options)
+def ensure_session_key!
+ if @key.blank?
+ raise ArgumentError, 'A key is required...'
end
end
```
@@ -421,9 +420,9 @@ NOTE: Defined in `active_support/core_ext/object/with_options.rb`.
### JSON support
-Active Support provides a better implemention of `to_json` than the `json` gem ordinarily provides for Ruby objects. This is because some classes, like `Hash` and `OrderedHash` needs special handling in order to provide a proper JSON representation.
+Active Support provides a better implemention of `to_json` than the +json+ gem ordinarily provides for Ruby objects. This is because some classes, like +Hash+ and +OrderedHash+ needs special handling in order to provide a proper JSON representation.
-Active Support also provides an implementation of `as_json` for the `Process::Status` class.
+Active Support also provides an implementation of `as_json` for the <tt>Process::Status</tt> class.
NOTE: Defined in `active_support/core_ext/object/to_json.rb`.
@@ -1249,6 +1248,18 @@ Calling `to_s` on a safe string returns a safe string, but coercion with `to_str
Calling `dup` or `clone` on safe strings yields safe strings.
+### `remove`
+
+The method `remove` will remove all occurrences of the pattern:
+
+```ruby
+"Hello World".remove(/Hello /) => "World"
+```
+
+There's also the destructive version `String#remove!`.
+
+NOTE: Defined in `active_support/core_ext/string/filters.rb`.
+
### `squish`
The method `squish` strips leading and trailing whitespace, and substitutes runs of whitespace with a single space each:
@@ -1988,7 +1999,7 @@ Produce a string representation of a number in human-readable words:
1234567890123456.to_s(:human) # => "1.23 Quadrillion"
```
-NOTE: Defined in `active_support/core_ext/numeric/conversions.rb`.
+NOTE: Defined in `active_support/core_ext/numeric/formatting.rb`.
Extensions to `Integer`
-----------------------
@@ -2046,7 +2057,7 @@ BigDecimal.new(5.00, 6).to_s # => "5.0"
### `to_formatted_s`
-The method `to_formatted_s` provides a default specifier of "F". This means that a simple call to `to_formatted_s` or `to_s` will result in floating point representation instead of engineering notation:
+Te method `to_formatted_s` provides a default specifier of "F". This means that a simple call to `to_formatted_s` or `to_s` will result in floating point representation instead of engineering notation:
```ruby
BigDecimal.new(5.00, 6).to_formatted_s # => "5.0"
@@ -2433,7 +2444,7 @@ dup[1][2] = 4
array[1][2] == nil # => true
```
-NOTE: Defined in `active_support/core_ext/object/deep_dup.rb`.
+NOTE: Defined in `active_support/core_ext/array/deep_dup.rb`.
### Grouping
@@ -2659,7 +2670,45 @@ hash[:b][:e] == nil # => true
hash[:b][:d] == [3, 4] # => true
```
-NOTE: Defined in `active_support/core_ext/object/deep_dup.rb`.
+NOTE: Defined in `active_support/core_ext/hash/deep_dup.rb`.
+
+### Diffing
+
+The method `diff` returns a hash that represents a diff of the receiver and the argument with the following logic:
+
+* Pairs `key`, `value` that exist in both hashes do not belong to the diff hash.
+
+* If both hashes have `key`, but with different values, the pair in the receiver wins.
+
+* The rest is just merged.
+
+```ruby
+{a: 1}.diff(a: 1)
+# => {}, first rule
+
+{a: 1}.diff(a: 2)
+# => {:a=>1}, second rule
+
+{a: 1}.diff(b: 2)
+# => {:a=>1, :b=>2}, third rule
+
+{a: 1, b: 2, c: 3}.diff(b: 1, c: 3, d: 4)
+# => {:a=>1, :b=>2, :d=>4}, all rules
+
+{}.diff({}) # => {}
+{a: 1}.diff({}) # => {:a=>1}
+{}.diff(a: 1) # => {:a=>1}
+```
+
+An important property of this diff hash is that you can retrieve the original hash by applying `diff` twice:
+
+```ruby
+hash.diff(hash2).diff(hash2) == hash
+```
+
+Diffing hashes may be useful for error messages related to expected option hashes for example.
+
+NOTE: Defined in `active_support/core_ext/hash/diff.rb`.
### Working with Keys
@@ -3794,13 +3843,13 @@ def default_helper_module!
module_path = module_name.underscore
helper module_path
rescue MissingSourceFile => e
- raise e unless e.is_missing? "helpers/#{module_path}_helper"
+ raise e unless e.is_missing? "#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
```
-NOTE: Defined in `actionpack/lib/abstract_controller/helpers.rb`.
+NOTE: Defined in `active_support/core_ext/name_error.rb`.
Extensions to `LoadError`
-------------------------
@@ -3823,4 +3872,4 @@ rescue NameError => e
end
```
-NOTE: Defined in `actionpack/lib/abstract_controller/helpers.rb`.
+NOTE: Defined in `active_support/core_ext/load_error.rb`.
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 639a00817b..862742679c 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -905,7 +905,7 @@ Customizing the Pipeline
### CSS Compression
There is currently one option for compressing CSS, YUI. The [YUI CSS
-compressor]((http://yui.github.io/yuicompressor/css.html) provides
+compressor](http://yui.github.io/yuicompressor/css.html) provides
minification.
The following line enables YUI compression, and requires the `yui-compressor`
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 9b80a65a44..e133e71d42 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1137,6 +1137,12 @@ Controls what happens to the associated object when its owner is destroyed:
* `:restrict_with_exception` causes an exception to be raised if there is an associated record
* `:restrict_with_error` causes an error to be added to the owner if there is an associated object
+It's necessary not to set or leave `:nullify` option for those associations
+that have `NOT NULL` database constraints. If you don't set `dependent` to
+destroy such associations you won't be able to change the associated object
+because initial associated object foreign key will be set to unallowed `NULL`
+value.
+
##### `:foreign_key`
By convention, Rails assumes that the column used to hold the foreign key on the other model is the name of this model with the suffix `_id` added. The `:foreign_key` option lets you set the name of the foreign key directly:
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index ec25e09222..034fef71c8 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -74,7 +74,7 @@ ports.
If you have any problems with these libraries, you can install them manually by compiling the source code. Just follow the instructions at the [Red Hat/CentOS section of the Nokogiri tutorials](http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos) .
-Also, SQLite3 and its development files for the `sqlite3` gem — in Ubuntu you're done with just
+Also, SQLite3 and its development files for the `sqlite3-ruby` gem — in Ubuntu you're done with just
```bash
$ sudo apt-get install sqlite3 libsqlite3-dev
@@ -86,6 +86,7 @@ And if you are on Fedora or CentOS, you're done with
$ sudo yum install sqlite3 sqlite3-devel
```
+<<<<<<< HEAD
If you are on Arch Linux, you will need to run:
```bash
@@ -100,6 +101,8 @@ For FreeBSD users, you're done with:
Or compile the `databases/sqlite3` port.
+=======
+>>>>>>> ec8ef1e1055c4e1598da13f49d30261f07f4a9b4
Get a recent version of [Bundler](http://gembundler.com/)
```bash
@@ -166,6 +169,7 @@ $ sudo yum install mysql-server mysql-devel
$ sudo yum install postgresql-server postgresql-devel
```
+<<<<<<< HEAD
If you are running Arch Linux, MySQL isn't supported anymore so you will need to
use MariaDB instead (see [this announcement](https://www.archlinux.org/news/mariadb-replaces-mysql-in-repositories/)):
@@ -185,6 +189,8 @@ Or install them through ports (they are located under the `databases` folder).
If you run into troubles during the installation of MySQL, please see
[the MySQL documentation](http://dev.mysql.com/doc/refman/5.1/en/freebsd-installation.html).
+=======
+>>>>>>> ec8ef1e1055c4e1598da13f49d30261f07f4a9b4
After that, run:
```bash
diff --git a/guides/source/engines.md b/guides/source/engines.md
index a77be917a2..9106b6382d 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -525,6 +525,14 @@ First, the `author_name` text field needs to be added to the `app/views/blorgh/p
</div>
```
+Next, we need to update our `Blorgh::PostController#post_params` method to permit the new form parameter:
+
+```ruby
+def post_params
+ params.require(:post).permit(:title, :text, :author_name)
+end
+```
+
The `Blorgh::Post` model should then have some code to convert the `author_name` field into an actual `User` object and associate it as that post's `author` before the post is saved. It will also need to have an `attr_accessor` setup for this field so that the setter and getter methods are defined for it.
To do all this, you'll need to add the `attr_accessor` for `author_name`, the association for the author and the `before_save` call into `app/models/blorgh/post.rb`. The `author` association will be hard-coded to the `User` class for the time being.
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index 11e8db9e88..3a18fb81d8 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -605,7 +605,7 @@ The object in the `params` hash is an instance of a subclass of IO. Depending on
```ruby
def upload
uploaded_io = params[:person][:picture]
- File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'w') do |file|
+ File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|
file.write(uploaded_io.read)
end
end
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 784a61adc1..021aa070fb 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -310,6 +310,10 @@ end
A controller is simply a class that is defined to inherit from `ApplicationController`. It's inside this class that you'll define methods that will become the actions for this controller. These actions will perform CRUD operations on the posts within our system.
+NOTE: There are `public`, `private` and `protected` methods in `Ruby`
+(for more details you can check on [Programming Ruby](http://www.ruby-doc.org/docs/ProgrammingRuby/)).
+But only `public` methods can be actions for controllers.
+
If you refresh <http://localhost:3000/posts/new> now, you'll get a new error:
![Unknown action new for PostsController!](images/getting_started/unknown_action_new_for_posts.png)
@@ -594,8 +598,9 @@ end
```
A couple of things to note. We use `Post.find` to find the post we're
-interested in. We also use an instance variable (prefixed by `@`) to
-hold a reference to the post object. We do this because Rails will pass all instance
+interested in, passing in `params[:id]` to get the `:id` parameter from the
+request. We also use an instance variable (prefixed by `@`) to hold a
+reference to the post object. We do this because Rails will pass all instance
variables to the view.
Now, create a new file `app/views/posts/show.html.erb` with the following
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index facfb96d98..e4214fd74e 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -731,6 +731,19 @@ en:
Then `User.model_name.human` will return "Dude" and `User.human_attribute_name("login")` will return "Handle".
+You can also set a plural form for model names, adding as following:
+
+```ruby
+en:
+ activerecord:
+ models:
+ user:
+ one: Dude
+ other: Dudes
+```
+
+Then `User.model_name.human(:count => 2)` will return "Dudes". With `:count => 1` or without params will return "Dude".
+
#### Error Message Scopes
Active Record validation error messages can also be translated easily. Active Record gives you a couple of namespaces where you can place your message translations in order to provide different messages and translation for certain models, attributes, and/or validations. It also transparently takes single table inheritance into account.
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index 26259408b4..c78eef5bf0 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -40,7 +40,7 @@ This file is as follows:
```ruby
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__)
-require File.expand_path('../../config/boot', __FILE__)
+require_relative '../config/boot'
require 'rails/commands'
```
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 62c9835fa4..0258202c18 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -66,18 +66,34 @@ Here's a sample YAML fixture file:
```yaml
# lo & behold! I am a YAML comment!
david:
- name: David Heinemeier Hansson
- birthday: 1979-10-15
- profession: Systems development
+ name: David Heinemeier Hansson
+ birthday: 1979-10-15
+ profession: Systems development
steve:
- name: Steve Ross Kellock
- birthday: 1974-09-27
- profession: guy with keyboard
+ name: Steve Ross Kellock
+ birthday: 1974-09-27
+ profession: guy with keyboard
```
Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank space. You can place comments in a fixture file by using the # character in the first column. Keys which resemble YAML keywords such as 'yes' and 'no' are quoted so that the YAML Parser correctly interprets them.
+If you are working with [associations](/association_basics.html), you can simply
+define a reference node between two different fixtures. Here's an example with
+a belongs_to/has_many association:
+
+```yaml
+# In fixtures/categories.yml
+about:
+ name: About
+
+# In fixtures/articles.yml
+one:
+ title: Welcome to Rails!
+ body: Hello world!
+ category: about
+```
+
#### ERB'in It Up
ERB allows you to embed Ruby code within templates. The YAML fixture format is pre-processed with ERB when Rails loads fixtures. This allows you to use Ruby to help you generate some sample data. For example, the following code generates a thousand users:
@@ -357,28 +373,28 @@ Here's an extract of the assertions you can use with `minitest`, the default tes
| Assertion | Purpose |
| ---------------------------------------------------------------- | ------- |
| `assert( test, [msg] )` | Ensures that `test` is true.|
-| `refute( test, [msg] )` | Ensures that `test` is false.|
+| `assert_not( test, [msg] )` | Ensures that `test` is false.|
| `assert_equal( expected, actual, [msg] )` | Ensures that `expected == actual` is true.|
-| `refute_equal( expected, actual, [msg] )` | Ensures that `expected != actual` is true.|
+| `assert_not_equal( expected, actual, [msg] )` | Ensures that `expected != actual` is true.|
| `assert_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is true.|
-| `refute_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is false.|
+| `assert_not_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is false.|
| `assert_nil( obj, [msg] )` | Ensures that `obj.nil?` is true.|
-| `refute_nil( obj, [msg] )` | Ensures that `obj.nil?` is false.|
+| `assert_not_nil( obj, [msg] )` | Ensures that `obj.nil?` is false.|
| `assert_match( regexp, string, [msg] )` | Ensures that a string matches the regular expression.|
-| `refute_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.|
+| `assert_no_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.|
| `assert_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.|
-| `refute_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.|
+| `assert_not_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.|
| `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.|
| `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.|
| `assert_nothing_raised( exception1, exception2, ... ) { block }` | Ensures that the given block doesn't raise one of the given exceptions.|
| `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.|
-| `refute_instance_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class`.|
+| `assert_not_instance_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class`.|
| `assert_kind_of( class, obj, [msg] )` | Ensures that `obj` is or descends from `class`.|
-| `refute_kind_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class` and is not descending from it.|
+| `assert_not_kind_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class` and is not descending from it.|
| `assert_respond_to( obj, symbol, [msg] )` | Ensures that `obj` responds to `symbol`.|
-| `refute_respond_to( obj, symbol, [msg] )` | Ensures that `obj` does not respond to `symbol`.|
+| `assert_not_respond_to( obj, symbol, [msg] )` | Ensures that `obj` does not respond to `symbol`.|
| `assert_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is true.|
-| `refute_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.|
+| `assert_not_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.|
| `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true. This one is weird eh?|
| `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.|
@@ -990,6 +1006,47 @@ class UserControllerTest < ActionController::TestCase
end
```
+Testing helpers
+---------------
+
+In order to test helpers, all you need to do is check that the output of the
+helper method matches what you'd expect. Tests related to the helpers are
+located under the `test/helpers` directory. Rails provides a generator which
+generates both the helper and the test file:
+
+```bash
+$ rails generate helper User
+ create app/helpers/user_helper.rb
+ invoke test_unit
+ create test/helpers/user_helper_test.rb
+```
+
+The generated test file contains the following code:
+
+```ruby
+require 'test_helper'
+
+class UserHelperTest < ActionView::TestCase
+end
+```
+
+A helper is just a simple module where you can define methods which are
+available into your views. To test the output of the helper's methods, you just
+have to use a mixin like this:
+
+```ruby
+class UserHelperTest < ActionView::TestCase
+ include UserHelper
+
+ test "should return the user name" do
+ # ...
+ end
+end
+```
+
+Moreover, since the test class extends from `ActionView::TestCase`, you have
+access to Rails' helper methods such as `link_to` or `pluralize`.
+
Other Testing Approaches
------------------------
@@ -998,6 +1055,7 @@ The built-in `test/unit` based testing is not the only way to test Rails applica
* [NullDB](http://avdi.org/projects/nulldb/), a way to speed up testing by avoiding database use.
* [Factory Girl](https://github.com/thoughtbot/factory_girl/tree/master), a replacement for fixtures.
* [Machinist](https://github.com/notahat/machinist/tree/master), another replacement for fixtures.
+* [Fixture Builder](https://github.com/rdy/fixture_builder), a tool that compiles Ruby factories into fixtures before a test run.
* [MiniTest::Spec Rails](https://github.com/metaskills/minitest-spec-rails), use the MiniTest::Spec DSL within your rails tests.
* [Shoulda](http://www.thoughtbot.com/projects/shoulda), an extension to `test/unit` with additional helpers, macros, and assertions.
* [RSpec](http://relishapp.com/rspec), a behavior-driven development framework
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 73c783085e..391551938a 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -320,7 +320,7 @@ config.assets.js_compressor = :uglifier
### sass-rails
-* `asset-url` with two arguments is deprecated. For example: `asset-url("rails.png", image)` becomes `asset-url("rails.png")`
+* `asset_url` with two arguments is deprecated. For example: `asset-url("rails.png", image)` becomes `asset-url("rails.png")`
Upgrading from Rails 3.1 to Rails 3.2
-------------------------------------
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index bd0c796673..301e0e7e6c 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -185,7 +185,7 @@ $(document).ready ->
```
Obviously, you'll want to be a bit more sophisticated than that, but it's a
-start.
+start. You can see more about the events [in the jquery-ujs wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
### form_tag
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 7a89e5c219..cc9722e59c 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,25 @@
+* rake notes now searches *.less files
+
+ *Josh Crowder*
+
+* Generate nested route for namespaced controller generated using
+ `rails g controller`.
+ Fixes #11532.
+
+ Example:
+
+ rails g controller admin/dashboard index
+
+ # Before:
+ get "dashboard/index"
+
+ # After:
+ namespace :admin do
+ get "dashboard/index"
+ end
+
+ *Prathamesh Sonpatki*
+
* Fix the event name of action_dispatch requests.
*Rafael Mendonça França*
diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb
index 3e32576040..1f9568fb5f 100644
--- a/railties/lib/rails/api/task.rb
+++ b/railties/lib/rails/api/task.rb
@@ -16,7 +16,8 @@ module Rails
:include => %w(
README.rdoc
lib/active_record/**/*.rb
- )
+ ),
+ :exclude => 'lib/active_record/vendor/*'
},
'activemodel' => {
@@ -32,22 +33,23 @@ module Rails
lib/abstract_controller/**/*.rb
lib/action_controller/**/*.rb
lib/action_dispatch/**/*.rb
- )
+ ),
+ :exclude => 'lib/action_controller/vendor/*'
},
'actionview' => {
:include => %w(
README.rdoc
lib/action_view/**/*.rb
- ),
- :exclude => 'lib/action_view/vendor/*'
+ )
},
'actionmailer' => {
:include => %w(
README.rdoc
lib/action_mailer/**/*.rb
- )
+ ),
+ :exclude => 'lib/action_mailer/vendor/*'
},
'railties' => {
diff --git a/railties/lib/rails/app_rails_loader.rb b/railties/lib/rails/app_rails_loader.rb
index fbb83fa10e..4a17803f1c 100644
--- a/railties/lib/rails/app_rails_loader.rb
+++ b/railties/lib/rails/app_rails_loader.rb
@@ -2,7 +2,7 @@ require 'pathname'
module Rails
module AppRailsLoader
- RUBY = Gem.ruby
+ RUBY = File.join(*RbConfig::CONFIG.values_at("bindir", "ruby_install_name")) + RbConfig::CONFIG["EXEEXT"]
EXECUTABLES = ['bin/rails', 'script/rails']
BUNDLER_WARNING = <<EOS
Looks like your app's ./bin/rails is a stub that was generated by Bundler.
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index be8af5c46c..8000fc3b1e 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -351,13 +351,8 @@ module Rails
Rails::Railtie::Configuration.eager_load_namespaces << base
base.called_from = begin
- call_stack = if Kernel.respond_to?(:caller_locations)
- caller_locations.map(&:path)
- else
- # Remove the line number from backtraces making sure we don't leave anything behind
- caller.map { |p| p.sub(/:\d+.*/, '') }
- end
-
+ # Remove the line number from backtraces making sure we don't leave anything behind
+ call_stack = caller.map { |p| p.sub(/:\d+.*/, '') }
File.dirname(call_stack.detect { |p| p !~ %r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack] })
end
end
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index bae54623c6..822f35fb42 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -10,11 +10,45 @@ module Rails
def add_routes
actions.reverse.each do |action|
- route %{get "#{file_name}/#{action}"}
+ route generate_routing_code(action)
end
end
hook_for :template_engine, :test_framework, :helper, :assets
+
+ private
+
+ # This method creates nested route entry for namespaced resources.
+ # For eg. rails g controller foo/bar/baz index
+ # Will generate -
+ # namespace :foo do
+ # namespace :bar do
+ # get "baz/index"
+ # end
+ # end
+ def generate_routing_code(action)
+ depth = class_path.length
+ # Create 'namespace' ladder
+ # namespace :foo do
+ # namespace :bar do
+ namespace_ladder = class_path.each_with_index.map do |ns, i|
+ %{#{" " * i * 2}namespace :#{ns} do\n }
+ end.join
+
+ # Create route
+ # get "baz/index"
+ route = %{#{" " * depth * 2}get "#{file_name}/#{action}"\n}
+
+ # Create `end` ladder
+ # end
+ # end
+ end_ladder = (1..depth).reverse_each.map do |i|
+ "#{" " * i * 2}end\n"
+ end.join
+
+ # Combine the 3 parts to generate complete route entry
+ namespace_ladder + route + end_ladder
+ end
end
end
end
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 290634290f..b806b922b7 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -82,7 +82,7 @@ class SourceAnnotationExtractor
case item
when /\.(builder|rb|coffee|rake)$/
/#\s*(#{tag}):?\s*(.*)$/
- when /\.(css|scss|sass|js)$/
+ when /\.(css|scss|sass|less|js)$/
/\/\/\s*(#{tag}):?\s*(.*)$/
when /\.erb$/
/<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index 9a92c5f6ff..01d751e822 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -25,6 +25,7 @@ module ApplicationTests
app_file "app/assets/stylesheets/application.css", "// TODO: note in css"
app_file "app/assets/stylesheets/application.css.scss", "// TODO: note in scss"
app_file "app/assets/stylesheets/application.css.sass", "// TODO: note in sass"
+ app_file "app/assets/stylesheets/application.css.less", "// TODO: note in less"
app_file "app/controllers/application_controller.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in ruby"
app_file "lib/tasks/task.rake", "# TODO: note in rake"
@@ -48,9 +49,10 @@ module ApplicationTests
assert_match(/note in css/, output)
assert_match(/note in scss/, output)
assert_match(/note in sass/, output)
+ assert_match(/note in less/, output)
assert_match(/note in rake/, output)
- assert_equal 10, lines.size
+ assert_equal 11, lines.size
lines.each do |line|
assert_equal 4, line[0].size
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index 5205deafd9..9c664a903a 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -82,4 +82,9 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
assert_instance_method :bar, controller
end
end
+
+ def test_namespaced_routes_are_created_in_routes
+ run_generator ["admin/dashboard", "index"]
+ assert_file "config/routes.rb", /namespace :admin do\n\s+get "dashboard\/index"\n/
+ end
end