activerecord 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -543
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +20 -29
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +55 -44
  7. data/lib/active_record/aggregations.rb +40 -34
  8. data/lib/active_record/associations.rb +204 -276
  9. data/lib/active_record/associations/alias_tracker.rb +1 -1
  10. data/lib/active_record/associations/association.rb +30 -35
  11. data/lib/active_record/associations/association_scope.rb +40 -40
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -2
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +35 -57
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +92 -88
  21. data/lib/active_record/associations/collection_proxy.rb +913 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
  23. data/lib/active_record/associations/has_many_association.rb +35 -9
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -14
  25. data/lib/active_record/associations/has_one_association.rb +33 -13
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +2 -2
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
  29. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  30. data/lib/active_record/associations/join_helper.rb +1 -11
  31. data/lib/active_record/associations/preloader.rb +14 -17
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/singular_association.rb +11 -11
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +133 -153
  41. data/lib/active_record/attribute_methods.rb +196 -93
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  43. data/lib/active_record/attribute_methods/dirty.rb +31 -28
  44. data/lib/active_record/attribute_methods/primary_key.rb +38 -30
  45. data/lib/active_record/attribute_methods/query.rb +5 -4
  46. data/lib/active_record/attribute_methods/read.rb +62 -91
  47. data/lib/active_record/attribute_methods/serialization.rb +97 -66
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
  49. data/lib/active_record/attribute_methods/write.rb +32 -39
  50. data/lib/active_record/autosave_association.rb +56 -70
  51. data/lib/active_record/base.rb +53 -450
  52. data/lib/active_record/callbacks.rb +53 -18
  53. data/lib/active_record/coders/yaml_column.rb +11 -9
  54. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
  55. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  56. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
  57. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
  58. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
  65. data/lib/active_record/connection_adapters/column.rb +46 -24
  66. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  67. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  68. data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
  69. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  70. data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
  73. data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
  74. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  75. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
  76. data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
  77. data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
  79. data/lib/active_record/connection_handling.rb +98 -0
  80. data/lib/active_record/core.rb +428 -0
  81. data/lib/active_record/counter_cache.rb +106 -108
  82. data/lib/active_record/dynamic_matchers.rb +110 -63
  83. data/lib/active_record/errors.rb +25 -8
  84. data/lib/active_record/explain.rb +8 -58
  85. data/lib/active_record/explain_subscriber.rb +6 -3
  86. data/lib/active_record/fixture_set/file.rb +56 -0
  87. data/lib/active_record/fixtures.rb +146 -148
  88. data/lib/active_record/inheritance.rb +77 -59
  89. data/lib/active_record/integration.rb +5 -5
  90. data/lib/active_record/locale/en.yml +8 -1
  91. data/lib/active_record/locking/optimistic.rb +38 -42
  92. data/lib/active_record/locking/pessimistic.rb +4 -4
  93. data/lib/active_record/log_subscriber.rb +19 -9
  94. data/lib/active_record/migration.rb +318 -153
  95. data/lib/active_record/migration/command_recorder.rb +90 -31
  96. data/lib/active_record/migration/join_table.rb +15 -0
  97. data/lib/active_record/model_schema.rb +69 -92
  98. data/lib/active_record/nested_attributes.rb +113 -148
  99. data/lib/active_record/null_relation.rb +65 -0
  100. data/lib/active_record/persistence.rb +188 -97
  101. data/lib/active_record/query_cache.rb +18 -36
  102. data/lib/active_record/querying.rb +19 -15
  103. data/lib/active_record/railtie.rb +91 -36
  104. data/lib/active_record/railties/console_sandbox.rb +0 -2
  105. data/lib/active_record/railties/controller_runtime.rb +2 -2
  106. data/lib/active_record/railties/databases.rake +90 -309
  107. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  108. data/lib/active_record/readonly_attributes.rb +7 -3
  109. data/lib/active_record/reflection.rb +72 -56
  110. data/lib/active_record/relation.rb +241 -157
  111. data/lib/active_record/relation/batches.rb +25 -22
  112. data/lib/active_record/relation/calculations.rb +143 -121
  113. data/lib/active_record/relation/delegation.rb +96 -18
  114. data/lib/active_record/relation/finder_methods.rb +117 -183
  115. data/lib/active_record/relation/merger.rb +133 -0
  116. data/lib/active_record/relation/predicate_builder.rb +90 -42
  117. data/lib/active_record/relation/query_methods.rb +666 -136
  118. data/lib/active_record/relation/spawn_methods.rb +43 -150
  119. data/lib/active_record/result.rb +33 -6
  120. data/lib/active_record/sanitization.rb +24 -50
  121. data/lib/active_record/schema.rb +19 -12
  122. data/lib/active_record/schema_dumper.rb +31 -39
  123. data/lib/active_record/schema_migration.rb +36 -0
  124. data/lib/active_record/scoping.rb +0 -124
  125. data/lib/active_record/scoping/default.rb +48 -45
  126. data/lib/active_record/scoping/named.rb +74 -103
  127. data/lib/active_record/serialization.rb +6 -2
  128. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  129. data/lib/active_record/store.rb +119 -15
  130. data/lib/active_record/tasks/database_tasks.rb +158 -0
  131. data/lib/active_record/tasks/mysql_database_tasks.rb +138 -0
  132. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  133. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  134. data/lib/active_record/test_case.rb +61 -38
  135. data/lib/active_record/timestamp.rb +8 -9
  136. data/lib/active_record/transactions.rb +65 -51
  137. data/lib/active_record/validations.rb +17 -15
  138. data/lib/active_record/validations/associated.rb +20 -14
  139. data/lib/active_record/validations/presence.rb +65 -0
  140. data/lib/active_record/validations/uniqueness.rb +93 -52
  141. data/lib/active_record/version.rb +4 -4
  142. data/lib/rails/generators/active_record.rb +3 -5
  143. data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
  144. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  145. data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
  146. data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
  147. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  148. metadata +53 -46
  149. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  150. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  151. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  152. data/lib/active_record/dynamic_finder_match.rb +0 -68
  153. data/lib/active_record/dynamic_scope_match.rb +0 -23
  154. data/lib/active_record/fixtures/file.rb +0 -65
  155. data/lib/active_record/identity_map.rb +0 -162
  156. data/lib/active_record/observer.rb +0 -121
  157. data/lib/active_record/session_store.rb +0 -360
  158. data/lib/rails/generators/active_record/migration.rb +0 -15
  159. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  160. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  161. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  162. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -24,6 +24,7 @@ module ActiveRecord
24
24
 
25
25
  def dump(stream)
26
26
  header(stream)
27
+ extensions(stream)
27
28
  tables(stream)
28
29
  trailer(stream)
29
30
  stream
@@ -38,7 +39,7 @@ module ActiveRecord
38
39
  end
39
40
 
40
41
  def header(stream)
41
- define_params = @version ? ":version => #{@version}" : ""
42
+ define_params = @version ? "version: #{@version}" : ""
42
43
 
43
44
  if stream.respond_to?(:external_encoding) && stream.external_encoding
44
45
  stream.puts "# encoding: #{stream.external_encoding.name}"
@@ -55,7 +56,7 @@ module ActiveRecord
55
56
  # from scratch. The latter is a flawed and unsustainable approach (the more migrations
56
57
  # you'll amass, the slower it'll run and the greater likelihood for issues).
57
58
  #
58
- # It's strongly recommended to check this file into your version control system.
59
+ # It's strongly recommended that you check this file into your version control system.
59
60
 
60
61
  ActiveRecord::Schema.define(#{define_params}) do
61
62
 
@@ -66,6 +67,18 @@ HEADER
66
67
  stream.puts "end"
67
68
  end
68
69
 
70
+ def extensions(stream)
71
+ return unless @connection.supports_extensions?
72
+ extensions = @connection.extensions
73
+ if extensions.any?
74
+ stream.puts " # These are extensions that must be enabled in order to support this database"
75
+ extensions.each do |extension|
76
+ stream.puts " enable_extension #{extension.inspect}"
77
+ end
78
+ stream.puts
79
+ end
80
+ end
81
+
69
82
  def tables(stream)
70
83
  @connection.tables.sort.each do |tbl|
71
84
  next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
@@ -95,42 +108,30 @@ HEADER
95
108
  tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
96
109
  if columns.detect { |c| c.name == pk }
97
110
  if pk != 'id'
98
- tbl.print %Q(, :primary_key => "#{pk}")
111
+ tbl.print %Q(, primary_key: "#{pk}")
99
112
  end
100
113
  else
101
- tbl.print ", :id => false"
114
+ tbl.print ", id: false"
102
115
  end
103
- tbl.print ", :force => true"
116
+ tbl.print ", force: true"
104
117
  tbl.puts " do |t|"
105
118
 
106
119
  # then dump all non-primary key columns
107
120
  column_specs = columns.map do |column|
108
121
  raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
109
122
  next if column.name == pk
110
- spec = {}
111
- spec[:name] = column.name.inspect
112
-
113
- # AR has an optimization which handles zero-scale decimals as integers. This
114
- # code ensures that the dumper still dumps the column as a decimal.
115
- spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
116
- 'decimal'
117
- else
118
- column.type.to_s
119
- end
120
- spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
121
- spec[:precision] = column.precision.inspect if column.precision
122
- spec[:scale] = column.scale.inspect if column.scale
123
- spec[:null] = 'false' unless column.null
124
- spec[:default] = default_string(column.default) if column.has_default?
125
- (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
126
- spec
123
+ @connection.column_spec(column, @types)
127
124
  end.compact
128
125
 
129
126
  # find all migration keys used in this table
130
- keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map{ |k| k.keys }.flatten
127
+ keys = @connection.migration_keys
131
128
 
132
129
  # figure out the lengths for each column based on above keys
133
- lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
130
+ lengths = keys.map { |key|
131
+ column_specs.map { |spec|
132
+ spec[key] ? spec[key].length + 2 : 0
133
+ }.max
134
+ }
134
135
 
135
136
  # the string we're going to sprintf our values against, with standardized column widths
136
137
  format_string = lengths.map{ |len| "%-#{len}s" }
@@ -166,32 +167,23 @@ HEADER
166
167
  stream
167
168
  end
168
169
 
169
- def default_string(value)
170
- case value
171
- when BigDecimal
172
- value.to_s
173
- when Date, DateTime, Time
174
- "'" + value.to_s(:db) + "'"
175
- else
176
- value.inspect
177
- end
178
- end
179
-
180
170
  def indexes(table, stream)
181
171
  if (indexes = @connection.indexes(table)).any?
182
172
  add_index_statements = indexes.map do |index|
183
173
  statement_parts = [
184
174
  ('add_index ' + remove_prefix_and_suffix(index.table).inspect),
185
175
  index.columns.inspect,
186
- (':name => ' + index.name.inspect),
176
+ ('name: ' + index.name.inspect),
187
177
  ]
188
- statement_parts << ':unique => true' if index.unique
178
+ statement_parts << 'unique: true' if index.unique
189
179
 
190
180
  index_lengths = (index.lengths || []).compact
191
- statement_parts << (':length => ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
181
+ statement_parts << ('length: ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
192
182
 
193
183
  index_orders = (index.orders || {})
194
- statement_parts << (':order => ' + index.orders.inspect) unless index_orders.empty?
184
+ statement_parts << ('order: ' + index.orders.inspect) unless index_orders.empty?
185
+
186
+ statement_parts << ('where: ' + index.where.inspect) if index.where
195
187
 
196
188
  ' ' + statement_parts.join(', ')
197
189
  end
@@ -0,0 +1,36 @@
1
+ require 'active_record/scoping/default'
2
+ require 'active_record/scoping/named'
3
+ require 'active_record/base'
4
+
5
+ module ActiveRecord
6
+ class SchemaMigration < ActiveRecord::Base
7
+
8
+ def self.table_name
9
+ "#{Base.table_name_prefix}schema_migrations#{Base.table_name_suffix}"
10
+ end
11
+
12
+ def self.index_name
13
+ "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
14
+ end
15
+
16
+ def self.create_table
17
+ unless connection.table_exists?(table_name)
18
+ connection.create_table(table_name, :id => false) do |t|
19
+ t.column :version, :string, :null => false
20
+ end
21
+ connection.add_index table_name, :version, :unique => true, :name => index_name
22
+ end
23
+ end
24
+
25
+ def self.drop_table
26
+ if connection.table_exists?(table_name)
27
+ connection.remove_index table_name, :name => index_name
28
+ connection.drop_table(table_name)
29
+ end
30
+ end
31
+
32
+ def version
33
+ super.to_i
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,3 @@
1
- require 'active_support/concern'
2
-
3
1
  module ActiveRecord
4
2
  module Scoping
5
3
  extend ActiveSupport::Concern
@@ -10,118 +8,6 @@ module ActiveRecord
10
8
  end
11
9
 
12
10
  module ClassMethods
13
- # with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be
14
- # <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
15
- # <tt>:create</tt> parameters are an attributes hash.
16
- #
17
- # class Article < ActiveRecord::Base
18
- # def self.create_with_scope
19
- # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
20
- # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
21
- # a = create(1)
22
- # a.blog_id # => 1
23
- # end
24
- # end
25
- # end
26
- #
27
- # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
28
- # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
29
- #
30
- # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
31
- # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
32
- # array of strings format for your joins.
33
- #
34
- # class Article < ActiveRecord::Base
35
- # def self.find_with_scope
36
- # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
37
- # with_scope(:find => limit(10)) do
38
- # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
39
- # end
40
- # with_scope(:find => where(:author_id => 3)) do
41
- # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
42
- # end
43
- # end
44
- # end
45
- # end
46
- #
47
- # You can ignore any previous scopings by using the <tt>with_exclusive_scope</tt> method.
48
- #
49
- # class Article < ActiveRecord::Base
50
- # def self.find_with_exclusive_scope
51
- # with_scope(:find => where(:blog_id => 1).limit(1)) do
52
- # with_exclusive_scope(:find => limit(10)) do
53
- # all # => SELECT * from articles LIMIT 10
54
- # end
55
- # end
56
- # end
57
- # end
58
- #
59
- # *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
60
- def with_scope(scope = {}, action = :merge, &block)
61
- # If another Active Record class has been passed in, get its current scope
62
- scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
63
-
64
- previous_scope = self.current_scope
65
-
66
- if scope.is_a?(Hash)
67
- # Dup first and second level of hash (method and params).
68
- scope = scope.dup
69
- scope.each do |method, params|
70
- scope[method] = params.dup unless params == true
71
- end
72
-
73
- scope.assert_valid_keys([ :find, :create ])
74
- relation = construct_finder_arel(scope[:find] || {})
75
- relation.default_scoped = true unless action == :overwrite
76
-
77
- if previous_scope && previous_scope.create_with_value && scope[:create]
78
- scope_for_create = if action == :merge
79
- previous_scope.create_with_value.merge(scope[:create])
80
- else
81
- scope[:create]
82
- end
83
-
84
- relation = relation.create_with(scope_for_create)
85
- else
86
- scope_for_create = scope[:create]
87
- scope_for_create ||= previous_scope.create_with_value if previous_scope
88
- relation = relation.create_with(scope_for_create) if scope_for_create
89
- end
90
-
91
- scope = relation
92
- end
93
-
94
- scope = previous_scope.merge(scope) if previous_scope && action == :merge
95
-
96
- self.current_scope = scope
97
- begin
98
- yield
99
- ensure
100
- self.current_scope = previous_scope
101
- end
102
- end
103
-
104
- protected
105
-
106
- # Works like with_scope, but discards any nested properties.
107
- def with_exclusive_scope(method_scoping = {}, &block)
108
- if method_scoping.values.any? { |e| e.is_a?(ActiveRecord::Relation) }
109
- raise ArgumentError, <<-MSG
110
- New finder API can not be used with_exclusive_scope. You can either call unscoped to get an anonymous scope not bound to the default_scope:
111
-
112
- User.unscoped.where(:active => true)
113
-
114
- Or call unscoped with a block:
115
-
116
- User.unscoped do
117
- User.where(:active => true).all
118
- end
119
-
120
- MSG
121
- end
122
- with_scope(method_scoping, :overwrite, &block)
123
- end
124
-
125
11
  def current_scope #:nodoc:
126
12
  Thread.current["#{self}_current_scope"]
127
13
  end
@@ -129,15 +15,6 @@ module ActiveRecord
129
15
  def current_scope=(scope) #:nodoc:
130
16
  Thread.current["#{self}_current_scope"] = scope
131
17
  end
132
-
133
- private
134
-
135
- def construct_finder_arel(options = {}, scope = nil)
136
- relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
137
- relation = scope.merge(relation) if scope
138
- relation
139
- end
140
-
141
18
  end
142
19
 
143
20
  def populate_with_current_scope_attributes
@@ -147,6 +24,5 @@ module ActiveRecord
147
24
  send("#{att}=", value) if respond_to?("#{att}=")
148
25
  end
149
26
  end
150
-
151
27
  end
152
28
  end
@@ -1,22 +1,20 @@
1
- require 'active_support/concern'
2
-
3
1
  module ActiveRecord
4
2
  module Scoping
5
3
  module Default
6
4
  extend ActiveSupport::Concern
7
5
 
8
6
  included do
9
- # Stores the default scope for the class
10
- class_attribute :default_scopes, :instance_writer => false
7
+ # Stores the default scope for the class.
8
+ class_attribute :default_scopes, instance_writer: false
11
9
  self.default_scopes = []
12
10
  end
13
11
 
14
12
  module ClassMethods
15
- # Returns a scope for the model without the default_scope.
13
+ # Returns a scope for the model without the +default_scope+.
16
14
  #
17
15
  # class Post < ActiveRecord::Base
18
16
  # def self.default_scope
19
- # where :published => true
17
+ # where published: true
20
18
  # end
21
19
  # end
22
20
  #
@@ -24,20 +22,20 @@ module ActiveRecord
24
22
  # Post.unscoped.all # Fires "SELECT * FROM posts"
25
23
  #
26
24
  # This method also accepts a block. All queries inside the block will
27
- # not use the default_scope:
25
+ # not use the +default_scope+:
28
26
  #
29
27
  # Post.unscoped {
30
28
  # Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
31
29
  # }
32
30
  #
33
- # It is recommended to use the block form of unscoped because chaining
34
- # unscoped with <tt>scope</tt> does not work. Assuming that
35
- # <tt>published</tt> is a <tt>scope</tt>, the following two statements
36
- # are equal: the default_scope is applied on both.
31
+ # It is recommended that you use the block form of unscoped because
32
+ # chaining unscoped with +scope+ does not work. Assuming that
33
+ # +published+ is a +scope+, the following two statements
34
+ # are equal: the +default_scope+ is applied on both.
37
35
  #
38
36
  # Post.unscoped.published
39
37
  # Post.published
40
- def unscoped #:nodoc:
38
+ def unscoped
41
39
  block_given? ? relation.scoping { yield } : relation
42
40
  end
43
41
 
@@ -51,62 +49,67 @@ module ActiveRecord
51
49
  # the model.
52
50
  #
53
51
  # class Article < ActiveRecord::Base
54
- # default_scope where(:published => true)
52
+ # default_scope { where(published: true) }
55
53
  # end
56
54
  #
57
55
  # Article.all # => SELECT * FROM articles WHERE published = true
58
56
  #
59
- # The <tt>default_scope</tt> is also applied while creating/building a record. It is not
60
- # applied while updating a record.
57
+ # The +default_scope+ is also applied while creating/building a record.
58
+ # It is not applied while updating a record.
61
59
  #
62
60
  # Article.new.published # => true
63
61
  # Article.create.published # => true
64
62
  #
65
- # You can also use <tt>default_scope</tt> with a block, in order to have it lazily evaluated:
66
- #
67
- # class Article < ActiveRecord::Base
68
- # default_scope { where(:published_at => Time.now - 1.week) }
69
- # end
70
- #
71
- # (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
72
- # macro, and it will be called when building the default scope.)
63
+ # (You can also pass any object which responds to +call+ to the
64
+ # +default_scope+ macro, and it will be called when building the
65
+ # default scope.)
73
66
  #
74
- # If you use multiple <tt>default_scope</tt> declarations in your model then they will
75
- # be merged together:
67
+ # If you use multiple +default_scope+ declarations in your model then
68
+ # they will be merged together:
76
69
  #
77
70
  # class Article < ActiveRecord::Base
78
- # default_scope where(:published => true)
79
- # default_scope where(:rating => 'G')
71
+ # default_scope { where(published: true) }
72
+ # default_scope { where(rating: 'G') }
80
73
  # end
81
74
  #
82
75
  # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
83
76
  #
84
- # This is also the case with inheritance and module includes where the parent or module
85
- # defines a <tt>default_scope</tt> and the child or including class defines a second one.
77
+ # This is also the case with inheritance and module includes where the
78
+ # parent or module defines a +default_scope+ and the child or including
79
+ # class defines a second one.
86
80
  #
87
- # If you need to do more complex things with a default scope, you can alternatively
88
- # define it as a class method:
81
+ # If you need to do more complex things with a default scope, you can
82
+ # alternatively define it as a class method:
89
83
  #
90
84
  # class Article < ActiveRecord::Base
91
85
  # def self.default_scope
92
86
  # # Should return a scope, you can call 'super' here etc.
93
87
  # end
94
88
  # end
95
- def default_scope(scope = {})
89
+ def default_scope(scope = nil)
96
90
  scope = Proc.new if block_given?
97
- self.default_scopes = default_scopes + [scope]
91
+
92
+ if scope.is_a?(Relation) || !scope.respond_to?(:call)
93
+ ActiveSupport::Deprecation.warn(
94
+ "Calling #default_scope without a block is deprecated. For example instead " \
95
+ "of `default_scope where(color: 'red')`, please use " \
96
+ "`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
97
+ "self.default_scope.)"
98
+ )
99
+ end
100
+
101
+ self.default_scopes += [scope]
98
102
  end
99
103
 
100
- def build_default_scope #:nodoc:
101
- if method(:default_scope).owner != ActiveRecord::Scoping::Default::ClassMethods
104
+ def build_default_scope # :nodoc:
105
+ if !Base.is_a?(method(:default_scope).owner)
106
+ # The user has defined their own default scope method, so call that
102
107
  evaluate_default_scope { default_scope }
103
108
  elsif default_scopes.any?
104
109
  evaluate_default_scope do
105
110
  default_scopes.inject(relation) do |default_scope, scope|
106
- if scope.is_a?(Hash)
107
- default_scope.apply_finder_options(scope)
108
- elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
109
- default_scope.merge(scope.call)
111
+ if !scope.is_a?(Relation) && scope.respond_to?(:call)
112
+ default_scope.merge(unscoped { scope.call })
110
113
  else
111
114
  default_scope.merge(scope)
112
115
  end
@@ -115,17 +118,18 @@ module ActiveRecord
115
118
  end
116
119
  end
117
120
 
118
- def ignore_default_scope? #:nodoc:
121
+ def ignore_default_scope? # :nodoc:
119
122
  Thread.current["#{self}_ignore_default_scope"]
120
123
  end
121
124
 
122
- def ignore_default_scope=(ignore) #:nodoc:
125
+ def ignore_default_scope=(ignore) # :nodoc:
123
126
  Thread.current["#{self}_ignore_default_scope"] = ignore
124
127
  end
125
128
 
126
- # The ignore_default_scope flag is used to prevent an infinite recursion situation where
127
- # a default scope references a scope which has a default scope which references a scope...
128
- def evaluate_default_scope
129
+ # The ignore_default_scope flag is used to prevent an infinite recursion
130
+ # situation where a default scope references a scope which has a default
131
+ # scope which references a scope...
132
+ def evaluate_default_scope # :nodoc:
129
133
  return if ignore_default_scope?
130
134
 
131
135
  begin
@@ -135,7 +139,6 @@ module ActiveRecord
135
139
  self.ignore_default_scope = false
136
140
  end
137
141
  end
138
-
139
142
  end
140
143
  end
141
144
  end