activerecord 3.0.0.rc → 3.0.0.rc2

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 (55) hide show
  1. data/CHANGELOG +6 -1
  2. data/README.rdoc +9 -9
  3. data/lib/active_record/aggregations.rb +64 -51
  4. data/lib/active_record/association_preload.rb +11 -9
  5. data/lib/active_record/associations.rb +300 -204
  6. data/lib/active_record/associations/association_collection.rb +7 -2
  7. data/lib/active_record/associations/belongs_to_association.rb +9 -5
  8. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +7 -6
  9. data/lib/active_record/associations/has_many_association.rb +6 -6
  10. data/lib/active_record/associations/has_many_through_association.rb +4 -3
  11. data/lib/active_record/associations/has_one_association.rb +7 -7
  12. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -1
  13. data/lib/active_record/attribute_methods/write.rb +2 -2
  14. data/lib/active_record/autosave_association.rb +54 -72
  15. data/lib/active_record/base.rb +167 -108
  16. data/lib/active_record/callbacks.rb +43 -35
  17. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -11
  18. data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
  19. data/lib/active_record/connection_adapters/abstract/query_cache.rb +0 -8
  20. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -6
  22. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -3
  23. data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -1
  24. data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -5
  25. data/lib/active_record/connection_adapters/sqlite_adapter.rb +5 -5
  26. data/lib/active_record/dynamic_finder_match.rb +3 -3
  27. data/lib/active_record/dynamic_scope_match.rb +1 -1
  28. data/lib/active_record/errors.rb +9 -5
  29. data/lib/active_record/fixtures.rb +36 -22
  30. data/lib/active_record/locale/en.yml +2 -2
  31. data/lib/active_record/migration.rb +36 -36
  32. data/lib/active_record/named_scope.rb +23 -11
  33. data/lib/active_record/nested_attributes.rb +3 -3
  34. data/lib/active_record/observer.rb +3 -3
  35. data/lib/active_record/persistence.rb +44 -29
  36. data/lib/active_record/railtie.rb +5 -8
  37. data/lib/active_record/railties/databases.rake +1 -1
  38. data/lib/active_record/reflection.rb +52 -52
  39. data/lib/active_record/relation.rb +26 -19
  40. data/lib/active_record/relation/batches.rb +4 -4
  41. data/lib/active_record/relation/calculations.rb +58 -34
  42. data/lib/active_record/relation/finder_methods.rb +21 -12
  43. data/lib/active_record/relation/query_methods.rb +26 -31
  44. data/lib/active_record/relation/spawn_methods.rb +17 -5
  45. data/lib/active_record/schema.rb +1 -1
  46. data/lib/active_record/schema_dumper.rb +12 -12
  47. data/lib/active_record/serialization.rb +1 -1
  48. data/lib/active_record/serializers/xml_serializer.rb +1 -1
  49. data/lib/active_record/session_store.rb +9 -9
  50. data/lib/active_record/test_case.rb +2 -2
  51. data/lib/active_record/timestamp.rb +31 -32
  52. data/lib/active_record/validations/associated.rb +4 -3
  53. data/lib/active_record/validations/uniqueness.rb +15 -11
  54. data/lib/active_record/version.rb +1 -1
  55. metadata +17 -16
@@ -24,6 +24,8 @@ else
24
24
  end
25
25
  end
26
26
 
27
+ class FixturesFileNotFound < StandardError; end
28
+
27
29
  # Fixtures are a way of organizing data that you want to test against; in short, sample data.
28
30
  #
29
31
  # = Fixture formats
@@ -39,9 +41,10 @@ end
39
41
  # This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
40
42
  # in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
41
43
  #
42
- # Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed
43
- # by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
44
- # put your files in <tt><your-rails-app>/test/fixtures/</tt>). The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
44
+ # Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed
45
+ # in the directory appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is
46
+ # automatically configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
47
+ # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
45
48
  # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a YAML fixture file looks like this:
46
49
  #
47
50
  # rubyonrails:
@@ -58,7 +61,8 @@ end
58
61
  # indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
59
62
  # pleasure.
60
63
  #
61
- # Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type. See http://yaml.org/type/omap.html
64
+ # Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
65
+ # See http://yaml.org/type/omap.html
62
66
  # for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
63
67
  # This is commonly needed for tree structures. Example:
64
68
  #
@@ -79,7 +83,8 @@ end
79
83
  # (Rails example: <tt><your-rails-app>/test/fixtures/web_sites.csv</tt>).
80
84
  #
81
85
  # The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
82
- # humans. The first line of the CSV file is a comma-separated list of field names. The rest of the file is then comprised
86
+ # humans. The first line of the CSV file is a comma-separated list of field names. The rest of the
87
+ # file is then comprised
83
88
  # of the actual data (1 per line). Here's an example:
84
89
  #
85
90
  # id, name, url
@@ -99,15 +104,16 @@ end
99
104
  #
100
105
  # == Single-file fixtures
101
106
  #
102
- # This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
103
- # Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
104
- # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
105
- # put your files in <tt><your-rails-app>/test/fixtures/<your-model-name>/</tt> --
107
+ # This type of fixture was the original format for Active Record that has since been deprecated in
108
+ # favor of the YAML and CSV formats.
109
+ # Fixtures for this format are created by placing text files in a sub-directory (with the name of the model)
110
+ # to the directory appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
111
+ # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/<your-model-name>/</tt> --
106
112
  # like <tt><your-rails-app>/test/fixtures/web_sites/</tt> for the WebSite model).
107
113
  #
108
114
  # Each text file placed in this directory represents a "record". Usually these types of fixtures are named without
109
- # extensions, but if you are on a Windows machine, you might consider adding <tt>.txt</tt> as the extension. Here's what the
110
- # above example might look like:
115
+ # extensions, but if you are on a Windows machine, you might consider adding <tt>.txt</tt> as the extension.
116
+ # Here's what the above example might look like:
111
117
  #
112
118
  # web_sites/google
113
119
  # web_sites/yahoo.txt
@@ -133,7 +139,8 @@ end
133
139
  # end
134
140
  # end
135
141
  #
136
- # By default, the <tt>test_helper module</tt> will load all of your fixtures into your test database, so this test will succeed.
142
+ # By default, the <tt>test_helper module</tt> will load all of your fixtures into your test database,
143
+ # so this test will succeed.
137
144
  # The testing environment will automatically load the all fixtures into the database before each test.
138
145
  # To ensure consistent data, the environment deletes the fixtures before running the load.
139
146
  #
@@ -182,13 +189,15 @@ end
182
189
  # This will create 1000 very simple YAML fixtures.
183
190
  #
184
191
  # Using ERb, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
185
- # This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
186
- # sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application
187
- # is properly testable. Hence, dynamic values in fixtures are to be considered a code smell.
192
+ # This is however a feature to be used with some caution. The point of fixtures are that they're
193
+ # stable units of predictable sample data. If you feel that you need to inject dynamic values, then
194
+ # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
195
+ # in fixtures are to be considered a code smell.
188
196
  #
189
197
  # = Transactional fixtures
190
198
  #
191
- # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
199
+ # TestCases can use begin+rollback to isolate their changes to the database instead of having to
200
+ # delete+insert for every test case.
192
201
  #
193
202
  # class FooTest < ActiveSupport::TestCase
194
203
  # self.use_transactional_fixtures = true
@@ -205,15 +214,18 @@ end
205
214
  # end
206
215
  #
207
216
  # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
208
- # then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes.
217
+ # then you may omit all fixtures declarations in your test cases since all the data's already there
218
+ # and every case rolls back its changes.
209
219
  #
210
220
  # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
211
- # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
221
+ # access to fixture data for every table that has been loaded through fixtures (depending on the
222
+ # value of +use_instantiated_fixtures+)
212
223
  #
213
224
  # When *not* to use transactional fixtures:
214
225
  #
215
- # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
216
- # particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
226
+ # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
227
+ # all parent transactions commit, particularly, the fixtures transaction which is begun in setup
228
+ # and rolled back in teardown. Thus, you won't be able to verify
217
229
  # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
218
230
  # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
219
231
  # Use InnoDB, MaxDB, or NDB instead.
@@ -678,7 +690,7 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
678
690
  end
679
691
 
680
692
  def column_names
681
- @column_names ||= @connection.columns(@table_name).collect(&:name)
693
+ @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
682
694
  end
683
695
 
684
696
  def read_fixture_files
@@ -686,6 +698,8 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
686
698
  read_yaml_fixture_files
687
699
  elsif File.file?(csv_file_path)
688
700
  read_csv_fixture_files
701
+ else
702
+ raise FixturesFileNotFound, "Could not find #{yaml_file_path} or #{csv_file_path}"
689
703
  end
690
704
  end
691
705
 
@@ -894,7 +908,7 @@ module ActiveRecord
894
908
 
895
909
  def uses_transaction(*methods)
896
910
  @uses_transaction = [] unless defined?(@uses_transaction)
897
- @uses_transaction.concat methods.map(&:to_s)
911
+ @uses_transaction.concat methods.map { |m| m.to_s }
898
912
  end
899
913
 
900
914
  def uses_transaction?(method)
@@ -22,7 +22,7 @@ en:
22
22
  # attributes:
23
23
  # login:
24
24
  # blank: "This is a custom blank message for User login"
25
- # Will define custom blank validation message for User model and
25
+ # Will define custom blank validation message for User model and
26
26
  # custom blank validation message for login attribute of User model.
27
27
  #models:
28
28
 
@@ -31,7 +31,7 @@ en:
31
31
  # For example,
32
32
  # user: "Dude"
33
33
  # will translate User model name to "Dude"
34
-
34
+
35
35
  # Translate model attribute names. Used in Model.human_attribute_name(attribute).
36
36
  #attributes:
37
37
  # For example,
@@ -31,13 +31,13 @@ module ActiveRecord
31
31
  end
32
32
 
33
33
  # = Active Record Migrations
34
- #
35
- # Migrations can manage the evolution of a schema used by several physical
34
+ #
35
+ # Migrations can manage the evolution of a schema used by several physical
36
36
  # databases. It's a solution to the common problem of adding a field to make
37
37
  # a new feature work in your local database, but being unsure of how to
38
- # push that change to other developers and to the production server. With
38
+ # push that change to other developers and to the production server. With
39
39
  # migrations, you can describe the transformations in self-contained classes
40
- # that can be checked into version control systems and executed against
40
+ # that can be checked into version control systems and executed against
41
41
  # another database that might be one, two, or five versions behind.
42
42
  #
43
43
  # Example of a simple migration:
@@ -52,12 +52,12 @@ module ActiveRecord
52
52
  # end
53
53
  # end
54
54
  #
55
- # This migration will add a boolean flag to the accounts table and remove it
56
- # if you're backing out of the migration. It shows how all migrations have
57
- # two class methods +up+ and +down+ that describes the transformations
55
+ # This migration will add a boolean flag to the accounts table and remove it
56
+ # if you're backing out of the migration. It shows how all migrations have
57
+ # two class methods +up+ and +down+ that describes the transformations
58
58
  # required to implement or remove the migration. These methods can consist
59
59
  # of both the migration specific methods like add_column and remove_column,
60
- # but may also contain regular Ruby code for generating data needed for the
60
+ # but may also contain regular Ruby code for generating data needed for the
61
61
  # transformations.
62
62
  #
63
63
  # Example of a more complex migration that also needs to initialize data:
@@ -72,8 +72,8 @@ module ActiveRecord
72
72
  # t.integer :position
73
73
  # end
74
74
  #
75
- # SystemSetting.create :name => "notice",
76
- # :label => "Use notice?",
75
+ # SystemSetting.create :name => "notice",
76
+ # :label => "Use notice?",
77
77
  # :value => 1
78
78
  # end
79
79
  #
@@ -82,48 +82,48 @@ module ActiveRecord
82
82
  # end
83
83
  # end
84
84
  #
85
- # This migration first adds the system_settings table, then creates the very
85
+ # This migration first adds the system_settings table, then creates the very
86
86
  # first row in it using the Active Record model that relies on the table. It
87
- # also uses the more advanced create_table syntax where you can specify a
87
+ # also uses the more advanced create_table syntax where you can specify a
88
88
  # complete table schema in one block call.
89
89
  #
90
90
  # == Available transformations
91
91
  #
92
- # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
92
+ # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
93
93
  # makes the table object available to a block that can then add columns to it,
94
- # following the same format as add_column. See example above. The options hash
95
- # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
94
+ # following the same format as add_column. See example above. The options hash
95
+ # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
96
96
  # table definition.
97
97
  # * <tt>drop_table(name)</tt>: Drops the table called +name+.
98
- # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
98
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
99
99
  # to +new_name+.
100
- # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
100
+ # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
101
101
  # to the table called +table_name+
102
102
  # named +column_name+ specified to be one of the following types:
103
- # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
103
+ # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
104
104
  # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
105
105
  # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
106
- # specified by passing an +options+ hash like <tt>{ :default => 11 }</tt>.
107
- # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
108
- # <tt>{ :limit => 50, :null => false }</tt>) -- see
106
+ # specified by passing an +options+ hash like <tt>{ :default => 11 }</tt>.
107
+ # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
108
+ # <tt>{ :limit => 50, :null => false }</tt>) -- see
109
109
  # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
110
110
  # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
111
111
  # a column but keeps the type and content.
112
- # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
112
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
113
113
  # the column to a different type using the same parameters as add_column.
114
- # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named
114
+ # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named
115
115
  # +column_name+ from the table called +table_name+.
116
- # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
116
+ # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
117
117
  # with the name of the column. Other options include
118
- # <tt>:name</tt> and <tt>:unique</tt> (e.g.
118
+ # <tt>:name</tt> and <tt>:unique</tt> (e.g.
119
119
  # <tt>{ :name => "users_name_index", :unique => true }</tt>).
120
- # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified
120
+ # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified
121
121
  # by +index_name+.
122
122
  #
123
123
  # == Irreversible transformations
124
124
  #
125
- # Some transformations are destructive in a manner that cannot be reversed.
126
- # Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
125
+ # Some transformations are destructive in a manner that cannot be reversed.
126
+ # Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
127
127
  # exception in their +down+ method.
128
128
  #
129
129
  # == Running migrations from within Rails
@@ -134,8 +134,8 @@ module ActiveRecord
134
134
  # rails generate migration MyNewMigration
135
135
  #
136
136
  # where MyNewMigration is the name of your migration. The generator will
137
- # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
138
- # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
137
+ # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
138
+ # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
139
139
  # UTC formatted date and time that the migration was generated.
140
140
  #
141
141
  # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
@@ -217,9 +217,9 @@ module ActiveRecord
217
217
  #
218
218
  # == Using a model after changing its table
219
219
  #
220
- # Sometimes you'll want to add a column in a migration and populate it
221
- # immediately after. In that case, you'll need to make a call to
222
- # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
220
+ # Sometimes you'll want to add a column in a migration and populate it
221
+ # immediately after. In that case, you'll need to make a call to
222
+ # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
223
223
  # latest column data from after the new column was added. Example:
224
224
  #
225
225
  # class AddPeopleSalary < ActiveRecord::Migration
@@ -374,7 +374,7 @@ module ActiveRecord
374
374
  end
375
375
 
376
376
  def method_missing(method, *arguments, &block)
377
- arg_list = arguments.map(&:inspect) * ', '
377
+ arg_list = arguments.map{ |a| a.inspect } * ', '
378
378
 
379
379
  say_with_time "#{method}(#{arg_list})" do
380
380
  unless arguments.empty? || method == :execute
@@ -451,7 +451,7 @@ module ActiveRecord
451
451
 
452
452
  def get_all_versions
453
453
  table = Arel::Table.new(schema_migrations_table_name)
454
- Base.connection.select_values(table.project(table['version']).to_sql).map(&:to_i).sort
454
+ Base.connection.select_values(table.project(table['version']).to_sql).map{ |v| v.to_i }.sort
455
455
  end
456
456
 
457
457
  def current_version
@@ -569,7 +569,7 @@ module ActiveRecord
569
569
  klasses << migration
570
570
  end
571
571
 
572
- migrations = migrations.sort_by(&:version)
572
+ migrations = migrations.sort_by { |m| m.version }
573
573
  down? ? migrations.reverse : migrations
574
574
  end
575
575
  end
@@ -20,10 +20,10 @@ module ActiveRecord
20
20
  # fruits = fruits.limit(10) if limited?
21
21
  #
22
22
  # Anonymous \scopes tend to be useful when procedurally generating complex
23
- # queries, where passing intermediate values (\scopes) around as first-class
23
+ # queries, where passing intermediate values (\scopes) around as first-class
24
24
  # objects is convenient.
25
25
  #
26
- # You can define a \scope that applies to all finders using
26
+ # You can define a \scope that applies to all finders using
27
27
  # ActiveRecord::Base.default_scope.
28
28
  def scoped(options = nil)
29
29
  if options
@@ -48,18 +48,21 @@ module ActiveRecord
48
48
  # The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
49
49
  # in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>.
50
50
  #
51
- # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it resembles the association object
52
- # constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
53
- # <tt>Shirt.red.where(:size => 'small')</tt>. Also, just as with the association objects, named \scopes act like an Array,
54
- # implementing Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt>
51
+ # Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it
52
+ # resembles the association object constructed by a <tt>has_many</tt> declaration. For instance,
53
+ # you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>.
54
+ # Also, just as with the association objects, named \scopes act like an Array, implementing Enumerable;
55
+ # <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt>
55
56
  # all behave as if Shirt.red really was an Array.
56
57
  #
57
- # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
58
- # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
59
- # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
58
+ # These named \scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce
59
+ # all shirts that are both red and dry clean only.
60
+ # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
61
+ # returns the number of garments for which these criteria obtain. Similarly with
62
+ # <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
60
63
  #
61
- # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which the \scopes were defined. But they are also available to
62
- # <tt>has_many</tt> associations. If,
64
+ # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which
65
+ # the \scopes were defined. But they are also available to <tt>has_many</tt> associations. If,
63
66
  #
64
67
  # class Person < ActiveRecord::Base
65
68
  # has_many :shirts
@@ -85,6 +88,15 @@ module ActiveRecord
85
88
  # end
86
89
  # end
87
90
  # end
91
+ #
92
+ # Scopes can also be used while creating/building a record.
93
+ #
94
+ # class Article < ActiveRecord::Base
95
+ # scope :published, where(:published => true)
96
+ # end
97
+ #
98
+ # Article.published.new.published # => true
99
+ # Article.published.create.published # => true
88
100
  def scope(name, scope_options = {}, &block)
89
101
  name = name.to_sym
90
102
  valid_scope_name?(name)
@@ -25,7 +25,7 @@ module ActiveRecord
25
25
  #
26
26
  # The attribute writer is named after the association, which means that
27
27
  # in the following example, two new methods are added to your model:
28
- #
28
+ #
29
29
  # <tt>author_attributes=(attributes)</tt> and
30
30
  # <tt>pages_attributes=(attributes)</tt>.
31
31
  #
@@ -78,7 +78,7 @@ module ActiveRecord
78
78
  # member.avatar_attributes = { :id => '2', :_destroy => '1' }
79
79
  # member.avatar.marked_for_destruction? # => true
80
80
  # member.save
81
- # member.reload.avatar #=> nil
81
+ # member.reload.avatar # => nil
82
82
  #
83
83
  # Note that the model will _not_ be destroyed until the parent is saved.
84
84
  #
@@ -180,7 +180,7 @@ module ActiveRecord
180
180
  #
181
181
  # member.attributes = params['member']
182
182
  # member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
183
- # member.posts.length #=> 2
183
+ # member.posts.length # => 2
184
184
  # member.save
185
185
  # member.reload.posts.length # => 1
186
186
  #
@@ -67,8 +67,8 @@ module ActiveRecord
67
67
  #
68
68
  # == Configuration
69
69
  #
70
- # In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration setting in your
71
- # <tt>config/application.rb</tt> file.
70
+ # In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration
71
+ # setting in your <tt>config/application.rb</tt> file.
72
72
  #
73
73
  # config.active_record.observers = :comment_observer, :signup_observer
74
74
  #
@@ -122,7 +122,7 @@ module ActiveRecord
122
122
  end
123
123
 
124
124
  def define_callbacks(klass)
125
- existing_methods = klass.instance_methods.map(&:to_sym)
125
+ existing_methods = klass.instance_methods.map { |m| m.to_sym }
126
126
  observer = self
127
127
  observer_name = observer.class.name.underscore.gsub('/', '__')
128
128
 
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  # = Active Record Persistence
3
3
  module Persistence
4
- # Returns true if this object hasn't been saved yet -- that is, a record
4
+ # Returns true if this object hasn't been saved yet -- that is, a record
5
5
  # for the object doesn't exist in the data store yet; otherwise, returns false.
6
6
  def new_record?
7
7
  @new_record
@@ -60,7 +60,7 @@ module ActiveRecord
60
60
  # reflect that no changes should be made (since they can't be
61
61
  # persisted). Returns the frozen instance.
62
62
  #
63
- # The row is simply removed with a SQL +DELETE+ statement on the
63
+ # The row is simply removed with an SQL +DELETE+ statement on the
64
64
  # record's primary key, and no callbacks are executed.
65
65
  #
66
66
  # To enforce the object's +before_destroy+ and +after_destroy+
@@ -72,7 +72,7 @@ module ActiveRecord
72
72
  freeze
73
73
  end
74
74
 
75
- # Deletes the record in the database and freezes this instance to reflect
75
+ # Deletes the record in the database and freezes this instance to reflect
76
76
  # that no changes should be made (since they can't be persisted).
77
77
  def destroy
78
78
  if persisted?
@@ -83,16 +83,16 @@ module ActiveRecord
83
83
  freeze
84
84
  end
85
85
 
86
- # Returns an instance of the specified +klass+ with the attributes of the
87
- # current record. This is mostly useful in relation to single-table
88
- # inheritance structures where you want a subclass to appear as the
89
- # superclass. This can be used along with record identification in
90
- # Action Pack to allow, say, <tt>Client < Company</tt> to do something
86
+ # Returns an instance of the specified +klass+ with the attributes of the
87
+ # current record. This is mostly useful in relation to single-table
88
+ # inheritance structures where you want a subclass to appear as the
89
+ # superclass. This can be used along with record identification in
90
+ # Action Pack to allow, say, <tt>Client < Company</tt> to do something
91
91
  # like render <tt>:partial => @client.becomes(Company)</tt> to render that
92
92
  # instance using the companies/company partial instead of clients/client.
93
93
  #
94
- # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
95
- # instance will affect the other.
94
+ # Note: The new instance will share a link to the same attributes as the original class.
95
+ # So any change to the attributes in either instance will affect the other.
96
96
  def becomes(klass)
97
97
  became = klass.new
98
98
  became.instance_variable_set("@attributes", @attributes)
@@ -102,29 +102,19 @@ module ActiveRecord
102
102
  became
103
103
  end
104
104
 
105
- # Updates a single attribute and saves the record.
105
+ # Updates a single attribute and saves the record.
106
106
  # This is especially useful for boolean flags on existing records. Also note that
107
107
  #
108
- # * validation is skipped
109
- # * No callbacks are invoked
110
- # * updated_at/updated_on column is updated if that column is available
111
- # * does not work on associations
112
- # * does not work on attr_accessor attributes. The attribute that is being updated must be column name.
108
+ # * Validation is skipped.
109
+ # * Callbacks are invoked.
110
+ # * updated_at/updated_on column is updated if that column is available.
111
+ # * Updates all the attributes that are dirty in this object.
113
112
  #
114
113
  def update_attribute(name, value)
115
- raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s
116
-
117
- changes = record_update_timestamps || {}
118
-
119
- if name
120
- name = name.to_s
121
- send("#{name}=", value)
122
- changes[name] = read_attribute(name)
123
- end
124
-
125
- @changed_attributes.except!(*changes.keys)
126
- primary_key = self.class.primary_key
127
- self.class.update_all(changes, { primary_key => self[primary_key] }) == 1
114
+ name = name.to_s
115
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
116
+ send("#{name}=", value)
117
+ save(:validate => false)
128
118
  end
129
119
 
130
120
  # Updates the attributes of the model from the passed-in hash and saves the
@@ -213,6 +203,31 @@ module ActiveRecord
213
203
  self
214
204
  end
215
205
 
206
+ # Saves the record with the updated_at/on attributes set to the current time.
207
+ # Please note that no validation is performed and no callbacks are executed.
208
+ # If an attribute name is passed, that attribute is updated along with
209
+ # updated_at/on attributes.
210
+ #
211
+ # Examples:
212
+ #
213
+ # product.touch # updates updated_at/on
214
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
215
+ def touch(name = nil)
216
+ attributes = timestamp_attributes_for_update_in_model
217
+ attributes << name if name
218
+
219
+ current_time = current_time_from_proper_timezone
220
+ changes = {}
221
+
222
+ attributes.each do |column|
223
+ changes[column.to_s] = write_attribute(column.to_s, current_time)
224
+ end
225
+
226
+ @changed_attributes.except!(*changes.keys)
227
+ primary_key = self.class.primary_key
228
+ self.class.update_all(changes, { primary_key => self[primary_key] }) == 1
229
+ end
230
+
216
231
  private
217
232
  def create_or_update
218
233
  raise ReadOnlyRecord if readonly?