activerecord 3.0.20 → 3.1.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 (122) hide show
  1. data/CHANGELOG +220 -91
  2. data/README.rdoc +3 -3
  3. data/examples/performance.rb +88 -109
  4. data/lib/active_record.rb +6 -2
  5. data/lib/active_record/aggregations.rb +22 -45
  6. data/lib/active_record/associations.rb +264 -991
  7. data/lib/active_record/associations/alias_tracker.rb +85 -0
  8. data/lib/active_record/associations/association.rb +231 -0
  9. data/lib/active_record/associations/association_scope.rb +120 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +40 -60
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +15 -63
  12. data/lib/active_record/associations/builder/association.rb +53 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +85 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +63 -0
  16. data/lib/active_record/associations/builder/has_many.rb +65 -0
  17. data/lib/active_record/associations/builder/has_one.rb +63 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +524 -0
  20. data/lib/active_record/associations/collection_proxy.rb +125 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +27 -118
  22. data/lib/active_record/associations/has_many_association.rb +50 -79
  23. data/lib/active_record/associations/has_many_through_association.rb +98 -67
  24. data/lib/active_record/associations/has_one_association.rb +45 -115
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency.rb +215 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +150 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_helper.rb +56 -0
  31. data/lib/active_record/associations/preloader.rb +177 -0
  32. data/lib/active_record/associations/preloader/association.rb +126 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  42. data/lib/active_record/associations/singular_association.rb +55 -0
  43. data/lib/active_record/associations/through_association.rb +80 -0
  44. data/lib/active_record/attribute_methods.rb +19 -5
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +9 -8
  46. data/lib/active_record/attribute_methods/dirty.rb +8 -2
  47. data/lib/active_record/attribute_methods/primary_key.rb +33 -13
  48. data/lib/active_record/attribute_methods/read.rb +17 -17
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -4
  50. data/lib/active_record/attribute_methods/write.rb +2 -1
  51. data/lib/active_record/autosave_association.rb +66 -45
  52. data/lib/active_record/base.rb +445 -273
  53. data/lib/active_record/callbacks.rb +24 -33
  54. data/lib/active_record/coders/yaml_column.rb +41 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +106 -13
  56. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +16 -2
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -12
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +16 -16
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +61 -22
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -273
  62. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -42
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +44 -25
  64. data/lib/active_record/connection_adapters/column.rb +268 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +686 -0
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +331 -88
  67. data/lib/active_record/connection_adapters/postgresql_adapter.rb +295 -267
  68. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +3 -7
  69. data/lib/active_record/connection_adapters/sqlite_adapter.rb +108 -26
  70. data/lib/active_record/counter_cache.rb +7 -4
  71. data/lib/active_record/fixtures.rb +174 -192
  72. data/lib/active_record/identity_map.rb +131 -0
  73. data/lib/active_record/locking/optimistic.rb +20 -14
  74. data/lib/active_record/locking/pessimistic.rb +4 -4
  75. data/lib/active_record/log_subscriber.rb +24 -4
  76. data/lib/active_record/migration.rb +265 -144
  77. data/lib/active_record/migration/command_recorder.rb +103 -0
  78. data/lib/active_record/named_scope.rb +68 -25
  79. data/lib/active_record/nested_attributes.rb +58 -15
  80. data/lib/active_record/observer.rb +3 -7
  81. data/lib/active_record/persistence.rb +58 -38
  82. data/lib/active_record/query_cache.rb +25 -3
  83. data/lib/active_record/railtie.rb +21 -12
  84. data/lib/active_record/railties/console_sandbox.rb +6 -0
  85. data/lib/active_record/railties/databases.rake +147 -116
  86. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  87. data/lib/active_record/reflection.rb +176 -44
  88. data/lib/active_record/relation.rb +125 -49
  89. data/lib/active_record/relation/batches.rb +7 -5
  90. data/lib/active_record/relation/calculations.rb +50 -18
  91. data/lib/active_record/relation/finder_methods.rb +47 -26
  92. data/lib/active_record/relation/predicate_builder.rb +24 -21
  93. data/lib/active_record/relation/query_methods.rb +117 -101
  94. data/lib/active_record/relation/spawn_methods.rb +27 -20
  95. data/lib/active_record/result.rb +34 -0
  96. data/lib/active_record/schema.rb +5 -6
  97. data/lib/active_record/schema_dumper.rb +11 -13
  98. data/lib/active_record/serialization.rb +2 -2
  99. data/lib/active_record/serializers/xml_serializer.rb +10 -10
  100. data/lib/active_record/session_store.rb +8 -2
  101. data/lib/active_record/test_case.rb +9 -20
  102. data/lib/active_record/timestamp.rb +21 -9
  103. data/lib/active_record/transactions.rb +16 -15
  104. data/lib/active_record/validations.rb +21 -22
  105. data/lib/active_record/validations/associated.rb +3 -1
  106. data/lib/active_record/validations/uniqueness.rb +48 -58
  107. data/lib/active_record/version.rb +3 -3
  108. data/lib/rails/generators/active_record.rb +6 -0
  109. data/lib/rails/generators/active_record/migration/templates/migration.rb +10 -2
  110. data/lib/rails/generators/active_record/model/model_generator.rb +2 -1
  111. data/lib/rails/generators/active_record/model/templates/migration.rb +6 -5
  112. data/lib/rails/generators/active_record/model/templates/model.rb +2 -0
  113. data/lib/rails/generators/active_record/model/templates/module.rb +2 -0
  114. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  115. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +2 -1
  116. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +2 -2
  117. metadata +106 -77
  118. checksums.yaml +0 -7
  119. data/lib/active_record/association_preload.rb +0 -431
  120. data/lib/active_record/associations/association_collection.rb +0 -572
  121. data/lib/active_record/associations/association_proxy.rb +0 -304
  122. data/lib/active_record/associations/through_association_scope.rb +0 -160
data/CHANGELOG CHANGED
@@ -1,139 +1,177 @@
1
- ## Rails 3.0.20 (unreleased)
1
+ *Rails 3.1.0 (unreleased)*
2
2
 
3
- ## Rails 3.0.19 (Jan 8, 2013)
3
+ * default_scope can take a block, lambda, or any other object which responds to `call` for lazy
4
+ evaluation:
4
5
 
5
- * Fix querying with an empty hash *Damien Mathieu* [CVE-2013-0155]
6
+ default_scope { ... }
7
+ default_scope lambda { ... }
8
+ default_scope method(:foo)
6
9
 
7
- ## Rails 3.0.18 (Jan 2, 2013)
10
+ This feature was originally implemented by Tim Morgan, but was then removed in favour of
11
+ defining a 'default_scope' class method, but has now been added back in by Jon Leighton.
12
+ The relevant lighthouse ticket is #1812.
8
13
 
9
- * CVE-2012-5664 ensure that options are never taken from the first parameter
14
+ * Default scopes are now evaluated at the latest possible moment, to avoid problems where
15
+ scopes would be created which would implicitly contain the default scope, which would then
16
+ be impossible to get rid of via Model.unscoped.
10
17
 
11
- ## Rails 3.0.17 (Aug 9, 2012)
18
+ Note that this means that if you are inspecting the internal structure of an
19
+ ActiveRecord::Relation, it will *not* contain the default scope, though the resulting
20
+ query will do. You can get a relation containing the default scope by calling
21
+ ActiveRecord#with_default_scope, though this is not part of the public API.
12
22
 
13
- * Fix type_to_sql with text and limit on mysql/mysql2 (GH #7252)
14
-
15
- ## Rails 3.0.16 (Jul 26, 2012)
16
-
17
- * No changes.
18
-
19
- ## Rails 3.0.14 (Jun 12, 2012)
20
-
21
- * protect against the nesting of hashes changing the
22
- table context in the next call to build_from_hash. This fix
23
- covers this case as well.
24
-
25
- CVE-2012-2695
26
-
27
- * Rails 3.0.13 (May 31, 2012)
28
-
29
- * Bugfix circular reference while saving has_one relationship
30
-
31
- * Test for circular reference while saving has_one relationship
32
-
33
- * Fixed typo in composed_of example with Money#<=>
23
+ [Jon Leighton]
34
24
 
35
- * predicate builder should not recurse for determining where columns.
36
- Thanks to Ben Murphy for reporting this! CVE-2012-2661
25
+ * Calling 'default_scope' multiple times in a class (including when a superclass calls
26
+ 'default_scope') is deprecated. The current behavior is that this will merge the default
27
+ scopes together:
37
28
 
38
- * Rails 3.0.11 (unreleased)
29
+ class Post < ActiveRecord::Base # Rails 3.1
30
+ default_scope where(:published => true)
31
+ default_scope where(:hidden => false)
32
+ # The default scope is now: where(:published => true, :hidden => false)
33
+ end
39
34
 
40
- * Exceptions from database adapters should not lose their backtrace.
35
+ In Rails 3.2, the behavior will be changed to overwrite previous scopes:
41
36
 
42
- * Backport "ActiveRecord::Persistence#touch should not use default_scope" (GH #1519)
37
+ class Post < ActiveRecord::Base # Rails 3.2
38
+ default_scope where(:published => true)
39
+ default_scope where(:hidden => false)
40
+ # The default scope is now: where(:hidden => false)
41
+ end
43
42
 
44
- * Psych errors with poor yaml formatting are proxied. Fixes GH #2645 and
45
- GH #2731
43
+ If you wish to merge default scopes in special ways, it is recommended to define your default
44
+ scope as a class method and use the standard techniques for sharing code (inheritance, mixins,
45
+ etc.):
46
46
 
47
- * Fix ActiveRecord#exists? when passsed a nil value
47
+ class Post < ActiveRecord::Base
48
+ def self.default_scope
49
+ where(:published => true).where(:hidden => false)
50
+ end
51
+ end
48
52
 
53
+ [Jon Leighton]
49
54
 
50
- * Rails 3.0.10 (August 16, 2011)
55
+ * PostgreSQL adapter only supports PostgreSQL version 8.2 and higher.
51
56
 
52
- * Magic encoding comment added to schema.rb files
57
+ * ConnectionManagement middleware is changed to clean up the connection pool
58
+ after the rack body has been flushed.
53
59
 
54
- * schema.rb is written as UTF-8 by default.
60
+ * Added an update_column method on ActiveRecord. This new method updates a given attribute on an object, skipping validations and callbacks.
61
+ It is recommended to use #update_attribute unless you are sure you do not want to execute any callback, including the modification of
62
+ the updated_at column. It should not be called on new records.
63
+ Example:
55
64
 
56
- * Ensuring an established connection when running `rake db:schema:dump`
65
+ User.first.update_column(:name, "sebastian") # => true
57
66
 
58
- * Association conditions will not clobber join conditions.
67
+ [Sebastian Martinez]
59
68
 
60
- * Destroying a record will destroy the HABTM record before destroying itself.
61
- GH #402.
69
+ * Associations with a :through option can now use *any* association as the
70
+ through or source association, including other associations which have a
71
+ :through option and has_and_belongs_to_many associations
62
72
 
63
- * Make `ActiveRecord::Batches#find_each` to not return `self`.
73
+ [Jon Leighton]
64
74
 
65
- * Update `table_exists?` in PG to to always use current search_path or schema
66
- if explictly set.
75
+ * The configuration for the current database connection is now accessible via
76
+ ActiveRecord::Base.connection_config. [fxn]
67
77
 
78
+ * limits and offsets are removed from COUNT queries unless both are supplied.
79
+ For example:
68
80
 
69
- *Rails 3.0.9 (June 16, 2011)*
81
+ People.limit(1).count # => 'SELECT COUNT(*) FROM people'
82
+ People.offset(1).count # => 'SELECT COUNT(*) FROM people'
83
+ People.limit(1).offset(1).count # => 'SELECT COUNT(*) FROM people LIMIT 1 OFFSET 1'
70
84
 
71
- *No changes.
85
+ [lighthouse #6262]
72
86
 
87
+ * ActiveRecord::Associations::AssociationProxy has been split. There is now an Association class
88
+ (and subclasses) which are responsible for operating on associations, and then a separate,
89
+ thin wrapper called CollectionProxy, which proxies collection associations.
73
90
 
74
- *Rails 3.0.8 (June 7, 2011)*
91
+ This prevents namespace pollution, separates concerns, and will allow further refactorings.
75
92
 
76
- * Fix various problems with using :primary_key and :foreign_key options in conjunction with
77
- :through associations. [Jon Leighton]
93
+ Singular associations (has_one, belongs_to) no longer have a proxy at all. They simply return
94
+ the associated record or nil. This means that you should not use undocumented methods such
95
+ as bob.mother.create - use bob.create_mother instead.
78
96
 
79
- * Correctly handle inner joins on polymorphic relationships.
97
+ [Jon Leighton]
80
98
 
81
- * Fixed infinity and negative infinity cases in PG date columns.
99
+ * Make has_many :through associations work correctly when you build a record and then save it. This
100
+ requires you to set the :inverse_of option on the source reflection on the join model, like so:
82
101
 
83
- * Creating records with invalid associations via `create` or `save` will no
84
- longer raise exceptions.
102
+ class Post < ActiveRecord::Base
103
+ has_many :taggings
104
+ has_many :tags, :through => :taggings
105
+ end
85
106
 
86
- *Rails 3.0.7 (April 18, 2011)*
107
+ class Tagging < ActiveRecord::Base
108
+ belongs_to :post
109
+ belongs_to :tag, :inverse_of => :tagging # :inverse_of must be set!
110
+ end
87
111
 
88
- * Destroying records via nested attributes works independent of reject_if LH #6006 [Durran Jordan]
112
+ class Tag < ActiveRecord::Base
113
+ has_many :taggings
114
+ has_many :posts, :through => :taggings
115
+ end
89
116
 
90
- * Delegate any? and many? to Model.scoped for consistency [Andrew White]
117
+ post = Post.first
118
+ tag = post.tags.build :name => "ruby"
119
+ tag.save # will save a Taggable linking to the post
91
120
 
92
- * Quote the ORDER BY clause in batched finds - fixes #6620 [Andrew White]
121
+ [Jon Leighton]
93
122
 
94
- * Change exists? so records are not instantiated - fixes #6127. This prevents after_find
95
- and after_initialize callbacks being triggered when checking for record existence.
96
- [Andrew White]
123
+ * Support the :dependent option on has_many :through associations. For historical and practical
124
+ reasons, :delete_all is the default deletion strategy employed by association.delete(*records),
125
+ despite the fact that the default strategy is :nullify for regular has_many. Also, this only
126
+ works at all if the source reflection is a belongs_to. For other situations, you should directly
127
+ modify the through association.
97
128
 
98
- * Fix performance bug with attribute accessors which only occurred on Ruby 1.8.7, and ensure we
99
- cache type-casted values when the column returned from the db contains non-standard chars.
100
129
  [Jon Leighton]
101
130
 
102
- * Fix performance bug with attribute accessors which only occurred on Ruby 1.8.7, and ensure we
103
- cache type-casted values when the column returned from the db contains non-standard chars.
104
- [Jon Leighton]
131
+ * Changed the behaviour of association.destroy for has_and_belongs_to_many and has_many :through.
132
+ From now on, 'destroy' or 'delete' on an association will be taken to mean 'get rid of the link',
133
+ not (necessarily) 'get rid of the associated records'.
105
134
 
106
- * Fix a performance regression introduced here 86acbf1cc050c8fa8c74a10c735e467fb6fd7df8
107
- related to read_attribute method [Stian Grytøyr]
135
+ Previously, has_and_belongs_to_many.destroy(*records) would destroy the records themselves. It
136
+ would not delete any records in the join table. Now, it deletes the records in the join table.
108
137
 
138
+ Previously, has_many_through.destroy(*records) would destroy the records themselves, and the
139
+ records in the join table. [Note: This has not always been the case; previous version of Rails
140
+ only deleted the records themselves.] Now, it destroys only the records in the join table.
109
141
 
110
- *Rails 3.0.6 (April 5, 2011)*
142
+ Note that this change is backwards-incompatible to an extent, but there is unfortunately no
143
+ way to 'deprecate' it before changing it. The change is being made in order to have
144
+ consistency as to the meaning of 'destroy' or 'delete' across the different types of associations.
111
145
 
112
- * Un-deprecate reorder method [Sebastian Martinez]
146
+ If you wish to destroy the records themselves, you can do records.association.each(&:destroy)
113
147
 
114
- * Extensions are applied when calling +except+ or +only+ on relations.
115
- Thanks to Iain Hecker.
148
+ [Jon Leighton]
116
149
 
117
- * Schemas set in set_table_name are respected by the mysql adapter. LH #5322
150
+ * Add :bulk => true option to change_table to make all the schema changes defined in change_table block using a single ALTER statement. [Pratik Naik]
118
151
 
119
- * Fixed a bug when empty? was called on a grouped Relation that wasn't loaded.
120
- LH #5829
152
+ Example:
121
153
 
122
- * Reapply extensions when using except and only. Thanks Iain Hecker.
154
+ change_table(:users, :bulk => true) do |t|
155
+ t.string :company_name
156
+ t.change :birthdate, :datetime
157
+ end
123
158
 
124
- * Binary data is escaped when being inserted to SQLite3 Databases. Thanks
125
- Naruse!
159
+ This will now result in:
126
160
 
127
- *Rails 3.0.5 (February 26, 2011)*
161
+ ALTER TABLE `users` ADD COLUMN `company_name` varchar(255), CHANGE `updated_at` `updated_at` datetime DEFAULT NULL
128
162
 
129
- * Model.where(:column => 1).where(:column => 2) will always produce an AND
130
- query.
163
+ * Removed support for accessing attributes on a has_and_belongs_to_many join table. This has been
164
+ documented as deprecated behaviour since April 2006. Please use has_many :through instead.
165
+ [Jon Leighton]
131
166
 
132
- [Aaron Patterson]
167
+ * Added a create_association! method for has_one and belongs_to associations. [Jon Leighton]
133
168
 
134
- * Deprecated support for interpolated association conditions in the form of :conditions => 'foo = #{bar}'.
169
+ * Migration files generated from model and constructive migration generators
170
+ (for example, add_name_to_users) use the reversible migration's `change`
171
+ method instead of the ordinary `up` and `down` methods. [Prem Sichanugrist]
135
172
 
136
- Instead, you should use a proc, like so:
173
+ * Removed support for interpolating string SQL conditions on associations. Instead, you should
174
+ use a proc, like so:
137
175
 
138
176
  Before:
139
177
 
@@ -154,13 +192,106 @@ query.
154
192
  'record' to get the record being inserted or deleted. This is now passed as an argument to
155
193
  the proc.
156
194
 
157
- [Jon Leighton]
195
+ * Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with BCrypt encryption and salting [DHH]. Example:
196
+
197
+ # Schema: User(name:string, password_digest:string, password_salt:string)
198
+ class User < ActiveRecord::Base
199
+ has_secure_password
200
+ end
201
+
202
+ user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch")
203
+ user.save # => false, password required
204
+ user.password = "mUc3m00RsqyRe"
205
+ user.save # => false, confirmation doesn't match
206
+ user.password_confirmation = "mUc3m00RsqyRe"
207
+ user.save # => true
208
+ user.authenticate("notright") # => false
209
+ user.authenticate("mUc3m00RsqyRe") # => user
210
+ User.find_by_name("david").try(:authenticate, "notright") # => nil
211
+ User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
212
+
213
+
214
+ * When a model is generated add_index is added by default for belongs_to or references columns
215
+
216
+ rails g model post user:belongs_to will generate the following:
217
+
218
+ class CreatePosts < ActiveRecord::Migration
219
+ def up
220
+ create_table :posts do |t|
221
+ t.belongs_to :user
222
+
223
+ t.timestamps
224
+ end
158
225
 
159
- *Rails 3.0.4 (February 8, 2011)*
226
+ add_index :posts, :user_id
227
+ end
228
+
229
+ def down
230
+ drop_table :posts
231
+ end
232
+ end
233
+
234
+ [Santiago Pastorino]
235
+
236
+ * Setting the id of a belongs_to object will update the reference to the
237
+ object. [#2989 state:resolved]
238
+
239
+ * ActiveRecord::Base#dup and ActiveRecord::Base#clone semantics have changed
240
+ to closer match normal Ruby dup and clone semantics.
241
+
242
+ * Calling ActiveRecord::Base#clone will result in a shallow copy of the record,
243
+ including copying the frozen state. No callbacks will be called.
244
+
245
+ * Calling ActiveRecord::Base#dup will duplicate the record, including calling
246
+ after initialize hooks. Frozen state will not be copied, and all associations
247
+ will be cleared. A duped record will return true for new_record?, have a nil
248
+ id field, and is saveable.
249
+
250
+ * Migrations can be defined as reversible, meaning that the migration system
251
+ will figure out how to reverse your migration. To use reversible migrations,
252
+ just define the "change" method. For example:
253
+
254
+ class MyMigration < ActiveRecord::Migration
255
+ def change
256
+ create_table(:horses) do
257
+ t.column :content, :text
258
+ t.column :remind_at, :datetime
259
+ end
260
+ end
261
+ end
262
+
263
+ Some things cannot be automatically reversed for you. If you know how to
264
+ reverse those things, you should define 'up' and 'down' in your migration. If
265
+ you define something in `change` that cannot be reversed, an
266
+ IrreversibleMigration exception will be raised when going down.
267
+
268
+ * Migrations should use instance methods rather than class methods:
269
+ class FooMigration < ActiveRecord::Migration
270
+ def up
271
+ ...
272
+ end
273
+ end
274
+
275
+ [Aaron Patterson]
276
+
277
+ * has_one maintains the association with separate after_create/after_update instead
278
+ of a single after_save. [fxn]
279
+
280
+ * The following code:
281
+
282
+ Model.limit(10).scoping { Model.count }
283
+
284
+ now generates the following SQL:
285
+
286
+ SELECT COUNT(*) FROM models LIMIT 10
287
+
288
+ This may not return what you want. Instead, you may with to do something
289
+ like this:
290
+
291
+ Model.limit(10).scoping { Model.all.size }
292
+
293
+ [Aaron Patterson]
160
294
 
161
- * Added deprecation warning for has_and_belongs_to_many associations where the join table has
162
- additional attributes other than the keys. Access to these attributes is removed in 3.1.
163
- Please use has_many :through instead. [Jon Leighton]
164
295
 
165
296
  *Rails 3.0.3 (November 16, 2010)*
166
297
 
@@ -198,12 +329,10 @@ query.
198
329
 
199
330
  [Aaron Patterson]
200
331
 
201
-
202
332
  *Rails 3.0.1 (October 15, 2010)*
203
333
 
204
334
  * Introduce a fix for CVE-2010-3993
205
335
 
206
-
207
336
  *Rails 3.0.0 (August 29, 2010)*
208
337
 
209
338
  * Changed update_attribute to not run callbacks and update the record directly in the database [Neeraj Singh]
@@ -84,7 +84,7 @@ A short rundown of some of the major features:
84
84
 
85
85
  class CommentObserver < ActiveRecord::Observer
86
86
  def after_create(comment) # is called just after Comment#save
87
- Notifications.deliver_new_comment("david@loudthinking.com", comment)
87
+ CommentMailer.new_comment_email("david@loudthinking.com", comment)
88
88
  end
89
89
  end
90
90
 
@@ -128,7 +128,7 @@ A short rundown of some of the major features:
128
128
 
129
129
  # connect to MySQL with authentication
130
130
  ActiveRecord::Base.establish_connection(
131
- :adapter => "mysql",
131
+ :adapter => "mysql2",
132
132
  :host => "localhost",
133
133
  :username => "me",
134
134
  :password => "secret",
@@ -203,7 +203,7 @@ The latest version of Active Record can be installed with Rubygems:
203
203
 
204
204
  Source code can be downloaded as part of the Rails project on GitHub
205
205
 
206
- * http://github.com/rails/rails/tree/master/activerecord/
206
+ * https://github.com/rails/rails/tree/master/activerecord/
207
207
 
208
208
 
209
209
  == License
@@ -1,31 +1,9 @@
1
- #!/usr/bin/env ruby -KU
2
-
3
1
  TIMES = (ENV['N'] || 10000).to_i
4
- require 'rubygems'
5
-
6
- gem 'addressable', '~>2.0'
7
- gem 'faker', '~>0.3.1'
8
- gem 'rbench', '~>0.2.3'
9
2
 
10
- require 'addressable/uri'
11
- require 'faker'
12
- require 'rbench'
13
-
14
- require File.expand_path("../../../load_paths", __FILE__)
3
+ require 'rubygems'
15
4
  require "active_record"
16
5
 
17
- conn = { :adapter => 'mysql',
18
- :database => 'activerecord_unittest',
19
- :username => 'rails', :password => '',
20
- :encoding => 'utf8' }
21
-
22
- conn[:socket] = Pathname.glob(%w[
23
- /opt/local/var/run/mysql5/mysqld.sock
24
- /tmp/mysqld.sock
25
- /tmp/mysql.sock
26
- /var/mysql/mysql.sock
27
- /var/run/mysqld/mysqld.sock
28
- ]).find { |path| path.socket? }.to_s
6
+ conn = { :adapter => 'sqlite3', :database => ':memory:' }
29
7
 
30
8
  ActiveRecord::Base.establish_connection(conn)
31
9
 
@@ -55,125 +33,126 @@ class Exhibit < ActiveRecord::Base
55
33
  def self.feel(exhibits) exhibits.each { |e| e.feel } end
56
34
  end
57
35
 
58
- sqlfile = File.expand_path("../performance.sql", __FILE__)
59
-
60
- if File.exists?(sqlfile)
61
- mysql_bin = %w[mysql mysql5].detect { |bin| `which #{bin}`.length > 0 }
62
- `#{mysql_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} < #{sqlfile}`
63
- else
64
- puts 'Generating data...'
65
-
66
- # pre-compute the insert statements and fake data compilation,
67
- # so the benchmarks below show the actual runtime for the execute
68
- # method, minus the setup steps
69
-
70
- # Using the same paragraph for all exhibits because it is very slow
71
- # to generate unique paragraphs for all exhibits.
72
- notes = Faker::Lorem.paragraphs.join($/)
73
- today = Date.today
74
-
75
- puts 'Inserting 10,000 users and exhibits...'
76
- 10_000.times do
77
- user = User.create(
78
- :created_at => today,
79
- :name => Faker::Name.name,
80
- :email => Faker::Internet.email
81
- )
82
-
83
- Exhibit.create(
84
- :created_at => today,
85
- :name => Faker::Company.name,
86
- :user => user,
87
- :notes => notes
88
- )
89
- end
90
-
91
- mysqldump_bin = %w[mysqldump mysqldump5].select { |bin| `which #{bin}`.length > 0 }
92
- `#{mysqldump_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} exhibits users > #{sqlfile}`
36
+ puts 'Generating data...'
37
+
38
+ module ActiveRecord
39
+ class Faker
40
+ LOREM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit. Integer consequat tincidunt felis. Etiam non erat dolor. Vivamus imperdiet nibh sit amet diam eleifend id posuere diam malesuada. Mauris at accumsan sem. Donec id lorem neque. Fusce erat lorem, ornare eu congue vitae, malesuada quis neque. Maecenas vel urna a velit pretium fermentum. Donec tortor enim, tempor venenatis egestas a, tempor sed ipsum. Ut arcu justo, faucibus non imperdiet ac, interdum at diam. Pellentesque ipsum enim, venenatis ut iaculis vitae, varius vitae sem. Sed rutrum quam ac elit euismod bibendum. Donec ultricies ultricies magna, at lacinia libero mollis aliquam. Sed ac arcu in tortor elementum tincidunt vel interdum sem. Curabitur eget erat arcu. Praesent eget eros leo. Nam magna enim, sollicitudin vehicula scelerisque in, vulputate ut libero. Praesent varius tincidunt commodo".split
41
+ def self.name
42
+ LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join ' '
43
+ end
44
+
45
+ def self.email
46
+ LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join('@') + ".com"
47
+ end
48
+ end
49
+ end
50
+
51
+ # pre-compute the insert statements and fake data compilation,
52
+ # so the benchmarks below show the actual runtime for the execute
53
+ # method, minus the setup steps
54
+
55
+ # Using the same paragraph for all exhibits because it is very slow
56
+ # to generate unique paragraphs for all exhibits.
57
+ notes = ActiveRecord::Faker::LOREM.join ' '
58
+ today = Date.today
59
+
60
+ puts 'Inserting 10,000 users and exhibits...'
61
+ 10_000.times do
62
+ user = User.create(
63
+ :created_at => today,
64
+ :name => ActiveRecord::Faker.name,
65
+ :email => ActiveRecord::Faker.email
66
+ )
67
+
68
+ Exhibit.create(
69
+ :created_at => today,
70
+ :name => ActiveRecord::Faker.name,
71
+ :user => user,
72
+ :notes => notes
73
+ )
93
74
  end
94
75
 
95
- RBench.run(TIMES) do
96
- column :times
97
- column :ar
76
+ require 'benchmark'
98
77
 
99
- report 'Model#id', (TIMES * 100).ceil do
100
- ar_obj = Exhibit.find(1)
78
+ Benchmark.bm(46) do |x|
79
+ ar_obj = Exhibit.find(1)
80
+ attrs = { :name => 'sam' }
81
+ attrs_first = { :name => 'sam' }
82
+ attrs_second = { :name => 'tom' }
83
+ exhibit = {
84
+ :name => ActiveRecord::Faker.name,
85
+ :notes => notes,
86
+ :created_at => Date.today
87
+ }
101
88
 
102
- ar { ar_obj.id }
89
+ x.report("Model#id (x#{(TIMES * 100).ceil})") do
90
+ (TIMES * 100).ceil.times { ar_obj.id }
103
91
  end
104
92
 
105
- report 'Model.new (instantiation)' do
106
- ar { Exhibit.new }
93
+ x.report 'Model.new (instantiation)' do
94
+ TIMES.times { Exhibit.new }
107
95
  end
108
96
 
109
- report 'Model.new (setting attributes)' do
110
- attrs = { :name => 'sam' }
111
- ar { Exhibit.new(attrs) }
97
+ x.report 'Model.new (setting attributes)' do
98
+ TIMES.times { Exhibit.new(attrs) }
112
99
  end
113
100
 
114
- report 'Model.first' do
115
- ar { Exhibit.first.look }
101
+ x.report 'Model.first' do
102
+ TIMES.times { Exhibit.first.look }
116
103
  end
117
104
 
118
- report 'Model.all limit(100)', (TIMES / 10).ceil do
119
- ar { Exhibit.look Exhibit.limit(100) }
105
+ x.report("Model.all limit(100) (x#{(TIMES / 10).ceil})") do
106
+ (TIMES / 10).ceil.times { Exhibit.look Exhibit.limit(100) }
120
107
  end
121
108
 
122
- report 'Model.all limit(100) with relationship', (TIMES / 10).ceil do
123
- ar { Exhibit.feel Exhibit.limit(100).includes(:user) }
109
+ x.report "Model.all limit(100) with relationship (x#{(TIMES / 10).ceil})" do
110
+ (TIMES / 10).ceil.times { Exhibit.feel Exhibit.limit(100).includes(:user) }
124
111
  end
125
112
 
126
- report 'Model.all limit(10,000)', (TIMES / 1000).ceil do
127
- ar { Exhibit.look Exhibit.limit(10000) }
113
+ x.report "Model.all limit(10,000) x(#{(TIMES / 1000).ceil})" do
114
+ (TIMES / 1000).ceil.times { Exhibit.look Exhibit.limit(10000) }
128
115
  end
129
116
 
130
- exhibit = {
131
- :name => Faker::Company.name,
132
- :notes => Faker::Lorem.paragraphs.join($/),
133
- :created_at => Date.today
134
- }
135
-
136
- report 'Model.create' do
137
- ar { Exhibit.create(exhibit) }
117
+ x.report 'Model.create' do
118
+ TIMES.times { Exhibit.create(exhibit) }
138
119
  end
139
120
 
140
- report 'Resource#attributes=' do
141
- attrs_first = { :name => 'sam' }
142
- attrs_second = { :name => 'tom' }
143
- ar { exhibit = Exhibit.new(attrs_first); exhibit.attributes = attrs_second }
121
+ x.report 'Resource#attributes=' do
122
+ TIMES.times {
123
+ exhibit = Exhibit.new(attrs_first)
124
+ exhibit.attributes = attrs_second
125
+ }
144
126
  end
145
127
 
146
- report 'Resource#update' do
147
- ar { Exhibit.first.update_attributes(:name => 'bob') }
128
+ x.report 'Resource#update' do
129
+ TIMES.times { Exhibit.first.update_attributes(:name => 'bob') }
148
130
  end
149
131
 
150
- report 'Resource#destroy' do
151
- ar { Exhibit.first.destroy }
132
+ x.report 'Resource#destroy' do
133
+ TIMES.times { Exhibit.first.destroy }
152
134
  end
153
135
 
154
- report 'Model.transaction' do
155
- ar { Exhibit.transaction { Exhibit.new } }
136
+ x.report 'Model.transaction' do
137
+ TIMES.times { Exhibit.transaction { Exhibit.new } }
156
138
  end
157
139
 
158
- report 'Model.find(id)' do
140
+ x.report 'Model.find(id)' do
159
141
  id = Exhibit.first.id
160
- ar { Exhibit.find(id) }
142
+ TIMES.times { Exhibit.find(id) }
161
143
  end
162
144
 
163
- report 'Model.find_by_sql' do
164
- ar { Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first }
145
+ x.report 'Model.find_by_sql' do
146
+ TIMES.times {
147
+ Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first
148
+ }
165
149
  end
166
150
 
167
- report 'Model.log', (TIMES * 10) do
168
- ar { Exhibit.connection.send(:log, "hello", "world") {} }
151
+ x.report "Model.log x(#{TIMES * 10})" do
152
+ (TIMES * 10).times { Exhibit.connection.send(:log, "hello", "world") {} }
169
153
  end
170
154
 
171
- report 'AR.execute(query)', (TIMES / 2) do
172
- ar { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
155
+ x.report "AR.execute(query) (#{TIMES / 2})" do
156
+ (TIMES / 2).times { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
173
157
  end
174
-
175
- summary 'Total'
176
158
  end
177
-
178
- ActiveRecord::Migration.drop_table "exhibits"
179
- ActiveRecord::Migration.drop_table "users"