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.
- data/CHANGELOG +6 -1
- data/README.rdoc +9 -9
- data/lib/active_record/aggregations.rb +64 -51
- data/lib/active_record/association_preload.rb +11 -9
- data/lib/active_record/associations.rb +300 -204
- data/lib/active_record/associations/association_collection.rb +7 -2
- data/lib/active_record/associations/belongs_to_association.rb +9 -5
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +7 -6
- data/lib/active_record/associations/has_many_association.rb +6 -6
- data/lib/active_record/associations/has_many_through_association.rb +4 -3
- data/lib/active_record/associations/has_one_association.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -1
- data/lib/active_record/attribute_methods/write.rb +2 -2
- data/lib/active_record/autosave_association.rb +54 -72
- data/lib/active_record/base.rb +167 -108
- data/lib/active_record/callbacks.rb +43 -35
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +0 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +8 -6
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -3
- data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -5
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +5 -5
- data/lib/active_record/dynamic_finder_match.rb +3 -3
- data/lib/active_record/dynamic_scope_match.rb +1 -1
- data/lib/active_record/errors.rb +9 -5
- data/lib/active_record/fixtures.rb +36 -22
- data/lib/active_record/locale/en.yml +2 -2
- data/lib/active_record/migration.rb +36 -36
- data/lib/active_record/named_scope.rb +23 -11
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/observer.rb +3 -3
- data/lib/active_record/persistence.rb +44 -29
- data/lib/active_record/railtie.rb +5 -8
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/reflection.rb +52 -52
- data/lib/active_record/relation.rb +26 -19
- data/lib/active_record/relation/batches.rb +4 -4
- data/lib/active_record/relation/calculations.rb +58 -34
- data/lib/active_record/relation/finder_methods.rb +21 -12
- data/lib/active_record/relation/query_methods.rb +26 -31
- data/lib/active_record/relation/spawn_methods.rb +17 -5
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +12 -12
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/serializers/xml_serializer.rb +1 -1
- data/lib/active_record/session_store.rb +9 -9
- data/lib/active_record/test_case.rb +2 -2
- data/lib/active_record/timestamp.rb +31 -32
- data/lib/active_record/validations/associated.rb +4 -3
- data/lib/active_record/validations/uniqueness.rb +15 -11
- data/lib/active_record/version.rb +1 -1
- 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
|
43
|
-
# by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is
|
44
|
-
# put your files in <tt><your-rails-app>/test/fixtures/</tt>).
|
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.
|
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
|
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
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
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.
|
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,
|
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
|
186
|
-
# sample data. If you feel that you need to inject dynamic values, then
|
187
|
-
# is properly testable. Hence, dynamic values
|
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
|
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
|
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
|
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
|
216
|
-
# particularly, the fixtures transaction which is begun in setup
|
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
|
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
|
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
|
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
|
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
|
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
|
52
|
-
# constructed by a <tt>has_many</tt> declaration. For instance,
|
53
|
-
# <tt>Shirt.red.where(:size => 'small')</tt>.
|
54
|
-
#
|
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
|
58
|
-
#
|
59
|
-
#
|
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
|
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
|
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
|
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
|
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
|
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
|
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.
|
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
|
-
# *
|
109
|
-
# *
|
110
|
-
# * updated_at/updated_on column is updated if that column is available
|
111
|
-
# *
|
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
|
-
|
116
|
-
|
117
|
-
|
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?
|