aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile1
-rw-r--r--activerecord/CHANGELOG.md8
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb2
-rw-r--r--activerecord/lib/active_record/core.rb13
-rw-r--r--activerecord/lib/active_record/migration.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/test/cases/base_test.rb57
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb2
-rw-r--r--guides/source/form_helpers.md26
10 files changed, 106 insertions, 9 deletions
diff --git a/Gemfile b/Gemfile
index 45deca8315..9abc8a5d66 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,6 +8,7 @@ gem 'bcrypt-ruby', '~> 3.0.0'
gem 'jquery-rails', github: 'rails/jquery-rails'
gem 'turbolinks'
gem 'coffee-rails', '~> 4.0.0.beta1'
+gem 'thor', github: 'wycats/thor'
# This needs to be with require false to avoid
# it being automatically loaded by sprockets
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 94d86f6550..3fe8d7ec2e 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,11 @@
## Rails 4.0.0 (unreleased) ##
+* Allow ActiveRecord::Base.connection_handler to have thread affinity and be
+ settable, this effectively allows ActiveRecord to be used in a multi threaded
+ setup with multiple connections to multiple dbs.
+
+ *Sam Saffron*
+
* `rename_column` preserves auto_increment in mysql migrations.
Fixes #3493.
@@ -8,7 +14,7 @@
* PostgreSQL geometric type point is supported by ActiveRecord. Fixes #7324.
*Martin Schuerrer*
-
+
* Add suport for concurrent indexing in PostgreSQL adapter via the
`algorithm: :concurrently` option
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 769e005c8f..22405c5d74 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -334,7 +334,7 @@ module ActiveRecord
private
# Returns a Hash of the Arel::Attributes and attribute values that have been
- # type casted for use in an Arel insert/update method.
+ # typecasted for use in an Arel insert/update method.
def arel_attributes_with_values(attribute_names)
attrs = {}
arel_table = self.class.arel_table
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 013f9b92b9..1ed05c1bd4 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -77,8 +77,17 @@ module ActiveRecord
mattr_accessor :disable_implicit_join_references, instance_writer: false
self.disable_implicit_join_references = false
- class_attribute :connection_handler, instance_writer: false
- self.connection_handler = ConnectionAdapters::ConnectionHandler.new
+ class_attribute :default_connection_handler, instance_writer: false
+
+ def self.connection_handler
+ Thread.current[:active_record_connection_handler] || self.default_connection_handler
+ end
+
+ def self.connection_handler=(handler)
+ Thread.current[:active_record_connection_handler] = handler
+ end
+
+ self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
end
module ClassMethods
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 27d37766b7..d3edcf3cdb 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -102,7 +102,7 @@ module ActiveRecord
# table definition.
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
# * <tt>change_table(name, options)</tt>: Allows to make column alterations to
- # the table called +name+. It makes the table object availabe to a block that
+ # the table called +name+. It makes the table object available to a block that
# can then add/remove columns, indexes or foreign keys to it.
# * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
# to +new_name+.
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 037097d2dd..51f6d39201 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -603,7 +603,7 @@ module ActiveRecord
"\n" \
" Post.includes(:comments).where(\"comments.title = 'foo'\")\n" \
"\n" \
- "Currently, Active Record recognises the table in the string, and knows to JOIN the " \
+ "Currently, Active Record recognizes the table in the string, and knows to JOIN the " \
"comments table to the query, rather than loading comments in a separate query. " \
"However, doing this without writing a full-blown SQL parser is inherently flawed. " \
"Since we don't want to write an SQL parser, we are removing this functionality. " \
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 14520381c9..e2685d0478 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -37,7 +37,7 @@ module ActiveRecord
end
# Finds the first record matching the specified conditions. There
- # is no implied ording so if order matters, you should specify it
+ # is no implied ordering so if order matters, you should specify it
# yourself.
#
# If no record is found, returns <tt>nil</tt>.
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index f9524bc6bd..69eef500f5 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1578,4 +1578,61 @@ class BasicsTest < ActiveRecord::TestCase
klass = Class.new(ActiveRecord::Base)
assert_equal ['foo'], klass.all.merge!(select: 'foo').select_values
end
+
+ test "connection_handler can be overriden" do
+ klass = Class.new(ActiveRecord::Base)
+ orig_handler = klass.connection_handler
+ new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
+ thread_connection_handler = nil
+
+ t = Thread.new do
+ klass.connection_handler = new_handler
+ thread_connection_handler = klass.connection_handler
+ end
+ t.join
+
+ assert_equal klass.connection_handler, orig_handler
+ assert_equal thread_connection_handler, new_handler
+ end
+
+ test "new threads get default the default connection handler" do
+ klass = Class.new(ActiveRecord::Base)
+ orig_handler = klass.connection_handler
+ handler = nil
+
+ t = Thread.new do
+ handler = klass.connection_handler
+ end
+ t.join
+
+ assert_equal handler, orig_handler
+ assert_equal klass.connection_handler, orig_handler
+ assert_equal klass.default_connection_handler, orig_handler
+ end
+
+ test "changing a connection handler in a main thread does not poison the other threads" do
+ klass = Class.new(ActiveRecord::Base)
+ orig_handler = klass.connection_handler
+ new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
+ after_handler = nil
+ is_set = false
+
+ t = Thread.new do
+ klass.connection_handler = new_handler
+ is_set = true
+ Thread.stop
+ after_handler = klass.connection_handler
+ end
+
+ while(!is_set)
+ Thread.pass
+ end
+
+ klass.connection_handler = orig_handler
+ t.wakeup
+ t.join
+
+ assert_equal after_handler, new_handler
+ assert_equal orig_handler, klass.connection_handler
+ end
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 29b45944aa..57457359b1 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -54,7 +54,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert !t2.save, "Shouldn't save t2 as unique"
assert_equal ["has already been taken"], t2.errors[:title]
- t2.title = "Now Im really also unique"
+ t2.title = "Now I am really also unique"
assert t2.save, "Should now save t2 as unique"
end
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index b8681d493a..817a732051 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -906,7 +906,21 @@ If the associated object is already saved, `fields_for` autogenerates a hidden i
### The Controller
-You do not need to write any specific controller code to use nested attributes. Create and update records as you would with a simple form.
+As usual you need to
+[whitelist the parameters](action_controller_overview.html#strong-parameters) in
+the controller before you pass them to the model:
+
+```ruby
+def create
+ @person = Person.new(person_params)
+ # ...
+end
+
+private
+def person_params
+ params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street])
+end
+```
### Removing Objects
@@ -937,6 +951,16 @@ If the hash of attributes for an object contains the key `_destroy` with a value
<% end %>
```
+Don't forget to update the whitelisted params in your controller to also include
+the `_destroy` field:
+
+```ruby
+def person_params
+ params.require(:person).
+ permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy])
+end
+```
+
### Preventing Empty Records
It is often useful to ignore sets of fields that the user has not filled in. You can control this by passing a `:reject_if` proc to `accepts_nested_attributes_for`. This proc will be called with each hash of attributes submitted by the form. If the proc returns `false` then Active Record will not build an associated object for that hash. The example below only tries to build an address if the `kind` attribute is set.