aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md61
-rw-r--r--guides/source/4_1_release_notes.md6
-rw-r--r--guides/source/upgrading_ruby_on_rails.md67
3 files changed, 134 insertions, 0 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 458b9d77c2..926270ddda 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,64 @@
+* Default scopes are no longer overriden by chained conditions.
+
+ Before this change when you defined a `default_scope` in a model
+ it was overriden by chained conditions in the same field. Now it
+ is merged like any other scope.
+
+ Before:
+
+ class User < ActiveRecord::Base
+ default_scope { where state: 'pending' }
+ scope :active, -> { where state: 'active' }
+ scope :inactive, -> { where state: 'inactive' }
+ end
+
+ User.all
+ # => SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
+
+ User.active
+ # => SELECT "users".* FROM "users" WHERE "users"."status" = 'active'
+
+ User.where(state: 'inactive')
+ # => SELECT "users".* FROM "users" WHERE "users"."status" = 'inactive'
+
+ After:
+
+ class User < ActiveRecord::Base
+ default_scope { where state: 'pending' }
+ scope :active, -> { where state: 'active' }
+ scope :inactive, -> { where state: 'inactive' }
+ end
+
+ User.all
+ # => SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
+
+ User.active
+ # => SELECT "users".* FROM "users" WHERE "users"."status" = 'pending' AND "users"."status" = 'active'
+
+ User.where(state: 'inactive')
+ # => SELECT "users".* FROM "users" WHERE "users"."status" = 'pending' AND "users"."status" = 'inactive'
+
+ To get the previous behavior it is needed to explicitly remove the
+ `default_scope` condition using `unscoped`, `unscope`, `rewhere` or
+ `except`.
+
+ Example:
+
+ class User < ActiveRecord::Base
+ default_scope { where state: 'pending' }
+ scope :active, -> { unescope(where: :state).where(state: 'active') }
+ scope :inactive, -> { rewhere state: 'inactive' }
+ end
+
+ User.all
+ # => SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
+
+ User.active
+ # => SELECT "users".* FROM "users" WHERE "users"."status" = 'active'
+
+ User.inactive
+ # => SELECT "users".* FROM "users" WHERE "users"."status" = 'inactive'
+
* Perform necessary deeper encoding when hstore is inside an array.
Fixes #11135.
diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md
index 8fcfc71351..a0b143d74c 100644
--- a/guides/source/4_1_release_notes.md
+++ b/guides/source/4_1_release_notes.md
@@ -453,6 +453,12 @@ for detailed changes.
### Notable changes
+* Default scopes are no longer overriden by chained conditions.
+
+ Before this change when you defined a `default_scope` in a model
+ it was overriden by chained conditions in the same field. Now it
+ is merged like any other scope. [More Details](upgrading_ruby_on_rails.html#changes-on-default-scopes).
+
* Added `ActiveRecord::Base.to_param` for convenient "pretty" URLs derived from
a model's attribute or
method. ([Pull Request](https://github.com/rails/rails/pull/12891))
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 8aae3bbc1a..74cfa23649 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -262,6 +262,73 @@ authors = Author.where(name: 'Hank Moody').to_a
authors.compact!
```
+### Changes on Default Scopes
+
+Default scopes are no longer overriden by chained conditions.
+
+In previous versions when you defined a `default_scope` in a model
+it was overriden by chained conditions in the same field. Now it
+is merged like any other scope.
+
+Before:
+
+```ruby
+class User < ActiveRecord::Base
+ default_scope { where state: 'pending' }
+ scope :active, -> { where state: 'active' }
+ scope :inactive, -> { where state: 'inactive' }
+end
+
+User.all
+# => SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
+
+User.active
+# => SELECT "users".* FROM "users" WHERE "users"."status" = 'active'
+
+User.where(state: 'inactive')
+# => SELECT "users".* FROM "users" WHERE "users"."status" = 'inactive'
+```
+
+After:
+
+```ruby
+class User < ActiveRecord::Base
+ default_scope { where state: 'pending' }
+ scope :active, -> { where state: 'active' }
+ scope :inactive, -> { where state: 'inactive' }
+end
+
+User.all
+# => SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
+
+User.active
+# => SELECT "users".* FROM "users" WHERE "users"."status" = 'pending' AND "users"."status" = 'active'
+
+User.where(state: 'inactive')
+# => SELECT "users".* FROM "users" WHERE "users"."status" = 'pending' AND "users"."status" = 'inactive'
+```
+
+To get the previous behavior it is needed to explicitly remove the
+`default_scope` condition using `unscoped`, `unscope`, `rewhere` or
+`except`.
+
+```ruby
+class User < ActiveRecord::Base
+ default_scope { where state: 'pending' }
+ scope :active, -> { unescope(where: :state).where(state: 'active') }
+ scope :inactive, -> { rewhere state: 'inactive' }
+end
+
+User.all
+# => SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
+
+User.active
+# => SELECT "users".* FROM "users" WHERE "users"."status" = 'active'
+
+User.inactive
+# => SELECT "users".* FROM "users" WHERE "users"."status" = 'inactive'
+```
+
Upgrading from Rails 3.2 to Rails 4.0
-------------------------------------