activerecord 3.2.12 → 3.2.13.rc1

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +309 -2
  3. data/lib/active_record/associations/association.rb +2 -1
  4. data/lib/active_record/associations/has_many_through_association.rb +5 -14
  5. data/lib/active_record/associations/has_one_association.rb +9 -1
  6. data/lib/active_record/associations/preloader/through_association.rb +2 -1
  7. data/lib/active_record/attribute_methods/serialization.rb +18 -0
  8. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -14
  9. data/lib/active_record/attribute_methods/write.rb +5 -4
  10. data/lib/active_record/base.rb +3 -3
  11. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  12. data/lib/active_record/connection_adapters/abstract/quoting.rb +2 -8
  13. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +2 -0
  14. data/lib/active_record/connection_adapters/column.rb +1 -1
  15. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -1
  16. data/lib/active_record/connection_adapters/postgresql_adapter.rb +16 -8
  17. data/lib/active_record/connection_adapters/schema_cache.rb +20 -1
  18. data/lib/active_record/connection_adapters/sqlite_adapter.rb +7 -3
  19. data/lib/active_record/explain.rb +7 -6
  20. data/lib/active_record/integration.rb +12 -1
  21. data/lib/active_record/locking/optimistic.rb +2 -0
  22. data/lib/active_record/log_subscriber.rb +5 -1
  23. data/lib/active_record/migration.rb +2 -2
  24. data/lib/active_record/nested_attributes.rb +4 -3
  25. data/lib/active_record/persistence.rb +1 -1
  26. data/lib/active_record/query_cache.rb +6 -6
  27. data/lib/active_record/railtie.rb +7 -0
  28. data/lib/active_record/railties/databases.rake +22 -8
  29. data/lib/active_record/relation/batches.rb +2 -2
  30. data/lib/active_record/relation/calculations.rb +11 -3
  31. data/lib/active_record/relation/finder_methods.rb +5 -3
  32. data/lib/active_record/relation/predicate_builder.rb +0 -4
  33. data/lib/active_record/scoping/named.rb +1 -3
  34. data/lib/active_record/validations/uniqueness.rb +1 -1
  35. data/lib/active_record/version.rb +2 -2
  36. metadata +15 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 41f4755c9a0e90c223bc3546bba991c54e447232
4
- data.tar.gz: 79dd37a61899bc19b93cd5348b7995c0b08b3a40
3
+ metadata.gz: d152752397963fbb89a3df0673c3fc204a8ad09d
4
+ data.tar.gz: 68758aa69846d6dee43c968e7ec8256db958a44b
5
5
  SHA512:
6
- metadata.gz: 24d5fa3615c010447bbf81eb13f38ef7fa1d90fe7f0aaefdd9a31479a79fbb44bd9945d9ab3300550c034d47b01ded7dcc51f4d5e4a48a8c4adceb2e08cbc62b
7
- data.tar.gz: 340fb9aa98abe8ab95ff6b6103ee8cc2487099f7797a2c905e364ea70de8aefa33af017bf0ef8a34a2c1d8391a7dd2d892e79c108e7a376082bcb7b99e6db51f
6
+ metadata.gz: 9cf4972e5c7a8b5f1880650b0e3f9716c0606773863ca640cea5a71677f1fa6db9095534f25e445644740a98a5ae410a787832c925f19555196cfe7188cab88d
7
+ data.tar.gz: 5f334b07081d7c3bf46b1af59e2123f9e38b2820b89b3b488b549ab98eca8bd656295fdf09f1eb6e01ede7b18c0e8ca67a1beb5b110c57344e2b4bfb161f903d
@@ -1,4 +1,304 @@
1
- ## Rails 3.2.12 (unreleased) ##
1
+ ## unreleased ##
2
+
3
+ * No changes.
4
+
5
+
6
+ ## Rails 3.2.13 (Feb 17, 2013) ##
7
+
8
+ * Reverted 921a296a3390192a71abeec6d9a035cc6d1865c8, 'Quote numeric values
9
+ compared to string columns.' This caused several regressions.
10
+
11
+ *Steve Klabnik*
12
+
13
+ * Fix overriding of attributes by default_scope on `ActiveRecord::Base#dup`.
14
+
15
+ *Hiroshige UMINO*
16
+
17
+ * Fix issue with overriding Active Record reader methods with a composed object
18
+ and using that attribute as the scope of a `uniqueness_of` validation.
19
+ Backport #7072.
20
+
21
+ *Peter Brown*
22
+
23
+ * Sqlite now preserves custom primary keys when copying or altering tables.
24
+ Fixes #9367.
25
+ Backport #2312.
26
+
27
+ *Sean Scally + Yves Senn*
28
+
29
+ * Preloading `has_many :through` associations with conditions won't
30
+ cache the `:through` association. This will prevent invalid
31
+ subsets to be cached.
32
+ Fixes #8423.
33
+ Backport #9252.
34
+
35
+ Example:
36
+
37
+ class User
38
+ has_many :posts
39
+ has_many :recent_comments, -> { where('created_at > ?', 1.week.ago) }, :through => :posts
40
+ end
41
+
42
+ a_user = User.includes(:recent_comments).first
43
+
44
+ # this is preloaded
45
+ a_user.recent_comments
46
+
47
+ # fetching the recent_comments through the posts association won't preload it.
48
+ a_user.posts
49
+
50
+ *Yves Senn*
51
+
52
+ * Fix handling of dirty time zone aware attributes
53
+
54
+ Previously, when `time_zone_aware_attributes` were enabled, after
55
+ changing a datetime or timestamp attribute and then changing it back
56
+ to the original value, `changed_attributes` still tracked the
57
+ attribute as changed. This caused `[attribute]_changed?` and
58
+ `changed?` methods to return true incorrectly.
59
+
60
+ Example:
61
+
62
+ in_time_zone 'Paris' do
63
+ order = Order.new
64
+ original_time = Time.local(2012, 10, 10)
65
+ order.shipped_at = original_time
66
+ order.save
67
+ order.changed? # => false
68
+
69
+ # changing value
70
+ order.shipped_at = Time.local(2013, 1, 1)
71
+ order.changed? # => true
72
+
73
+ # reverting to original value
74
+ order.shipped_at = original_time
75
+ order.changed? # => false, used to return true
76
+ end
77
+
78
+ Backport of #9073
79
+ Fixes #8898
80
+
81
+ *Lilibeth De La Cruz*
82
+
83
+ * Fix counter cache columns not updated when replacing `has_many :through`
84
+ associations.
85
+ Backport #8400.
86
+ Fix #7630.
87
+
88
+ *Matthew Robertson*
89
+
90
+ * Don't update `column_defaults` when calling destructive methods on column with default value.
91
+ Backport c517602.
92
+ Fix #6115.
93
+
94
+ *Piotr Sarnacki + Aleksey Magusev + Alan Daud*
95
+
96
+ * When `#count` is used in conjunction with `#uniq` we perform `count(:distinct => true)`.
97
+ Fix #6865.
98
+
99
+ Example:
100
+
101
+ relation.uniq.count # => SELECT COUNT(DISTINCT *)
102
+
103
+ *Yves Senn + Kaspar Schiess*
104
+
105
+ * Fix `ActiveRecord::Relation#pluck` when columns or tables are reserved words.
106
+ Backport #7536.
107
+ Fix #8968.
108
+
109
+ *Ian Lesperance + Yves Senn + Kaspar Schiess*
110
+
111
+ * Don't run explain on slow queries for database adapters that don't support it.
112
+ Backport #6197.
113
+
114
+ *Blake Smith*
115
+
116
+ * Revert round usec when comparing timestamp attributes in the dirty tracking.
117
+ Fixes #8460.
118
+
119
+ *Andrew White*
120
+
121
+ * Revert creation of through association models when using `collection=[]`
122
+ on a `has_many :through` association from an unsaved model.
123
+ Fix #7661, #8269.
124
+
125
+ *Ernie Miller*
126
+
127
+ * Fix undefined method `to_i` when calling `new` on a scope that uses an
128
+ Array; Fix FloatDomainError when setting integer column to NaN.
129
+ Fixes #8718, #8734, #8757.
130
+
131
+ *Jason Stirk + Tristan Harward*
132
+
133
+ * Serialized attributes can be serialized in integer columns.
134
+ Fix #8575.
135
+
136
+ *Rafael Mendonça França*
137
+
138
+ * Keep index names when using `alter_table` with sqlite3.
139
+ Fix #3489.
140
+ Backport #8522.
141
+
142
+ *Yves Senn*
143
+
144
+ * Recognize migrations placed in directories containing numbers and 'rb'.
145
+ Fix #8492.
146
+ Backport of #8500.
147
+
148
+ *Yves Senn*
149
+
150
+ * Add `ActiveRecord::Base.cache_timestamp_format` class attribute to control
151
+ the format of the timestamp value in the cache key.
152
+ This allows users to improve the precision of the cache key.
153
+ Fixes #8195.
154
+
155
+ *Rafael Mendonça França*
156
+
157
+ * Add `:nsec` date format. This can be used to improve the precision of cache key.
158
+ Please note that this format only works with Ruby 1.9, Ruby 1.8 will ignore it completely.
159
+
160
+ *Jamie Gaskins*
161
+
162
+ * Unscope `update_column(s)` query to ignore default scope.
163
+
164
+ When applying `default_scope` to a class with a where clause, using
165
+ `update_column(s)` could generate a query that would not properly update
166
+ the record due to the where clause from the `default_scope` being applied
167
+ to the update query.
168
+
169
+ class User < ActiveRecord::Base
170
+ default_scope where(active: true)
171
+ end
172
+
173
+ user = User.first
174
+ user.active = false
175
+ user.save!
176
+
177
+ user.update_column(:active, true) # => false
178
+
179
+ In this situation we want to skip the default_scope clause and just
180
+ update the record based on the primary key. With this change:
181
+
182
+ user.update_column(:active, true) # => true
183
+
184
+ Backport of #8436 fix.
185
+
186
+ *Carlos Antonio da Silva*
187
+
188
+ * Fix performance problem with primary_key method in PostgreSQL adapter when having many schemas.
189
+ Uses pg_constraint table instead of pg_depend table which has many records in general.
190
+ Fix #8414
191
+
192
+ *kennyj*
193
+
194
+ * Do not instantiate intermediate Active Record objects when eager loading.
195
+ These records caused `after_find` to run more than expected.
196
+ Fix #3313
197
+ Backport of #8403
198
+
199
+ *Yves Senn*
200
+
201
+ * Fix `pluck` to work with joins. Backport of #4942.
202
+
203
+ *Carlos Antonio da Silva*
204
+
205
+ * Fix a problem with `translate_exception` method in a non English environment.
206
+ Backport of #6397.
207
+
208
+ *kennyj*
209
+
210
+ * Fix dirty attribute checks for TimeZoneConversion with nil and blank
211
+ datetime attributes. Setting a nil datetime to a blank string should not
212
+ result in a change being flagged.
213
+ Fixes #8310.
214
+ Backport of #8311.
215
+
216
+ *Alisdair McDiarmid*
217
+
218
+ * Prevent mass assignment to the type column of polymorphic associations when using `build`.
219
+ Fixes #8265.
220
+ Backport of #8291.
221
+
222
+ *Yves Senn*
223
+
224
+ * When running migrations on Postgresql, the `:limit` option for `binary` and `text` columns is
225
+ silently dropped.
226
+ Previously, these migrations caused sql exceptions, because Postgresql doesn't support limits
227
+ on these types.
228
+
229
+ *Victor Costan*
230
+
231
+ * `#pluck` can be used on a relation with `select` clause.
232
+ Fixes #7551.
233
+ Backport of #8176.
234
+
235
+ Example:
236
+
237
+ Topic.select([:approved, :id]).order(:id).pluck(:id)
238
+
239
+ *Yves Senn*
240
+
241
+ * Use `nil?` instead of `blank?` to check whether dynamic finder with a bang
242
+ should raise RecordNotFound.
243
+ Fixes #7238.
244
+
245
+ *Nikita Afanasenko*
246
+
247
+ * Fix deleting from a HABTM join table upon destroying an object of a model
248
+ with optimistic locking enabled.
249
+ Fixes #5332.
250
+
251
+ *Nick Rogers*
252
+
253
+ * Use query cache/uncache when using ENV["DATABASE_URL"].
254
+ Fixes #6951.
255
+ Backport of #8074.
256
+
257
+ *kennyj*
258
+
259
+ * Do not create useless database transaction when building `has_one` association.
260
+
261
+ Example:
262
+
263
+ User.has_one :profile
264
+ User.new.build_profile
265
+
266
+ Backport of #8154.
267
+
268
+ *Bogdan Gusiev*
269
+
270
+ * `AR::Base#attributes_before_type_cast` now returns unserialized values for serialized attributes.
271
+
272
+ *Nikita Afanasenko*
273
+
274
+ * Fix issue that raises `NameError` when overriding the `accepts_nested_attributes` in child classes.
275
+
276
+ Before:
277
+
278
+ class Shared::Person < ActiveRecord::Base
279
+ has_one :address
280
+
281
+ accepts_nested_attributes :address, :reject_if => :all_blank
282
+ end
283
+
284
+ class Person < Shared::Person
285
+ accepts_nested_attributes :address
286
+ end
287
+
288
+ Person
289
+ #=> NameError: method `address_attributes=' not defined in Person
290
+
291
+ After:
292
+
293
+ Person
294
+ #=> Person(id: integer, ...)
295
+
296
+ Fixes #8131.
297
+
298
+ *Gabriel Sobrinho, Ricardo Henrique*
299
+
300
+
301
+ ## Rails 3.2.12 (Feb 11, 2013) ##
2
302
 
3
303
  * Quote numeric values being compared to non-numeric columns. Otherwise,
4
304
  in some database, the string column values will be coerced to a numeric
@@ -10,17 +310,24 @@
10
310
 
11
311
  *Dylan Smith*
12
312
 
313
+
13
314
  ## Rails 3.2.11 (Jan 8, 2013) ##
14
315
 
15
316
  * Fix querying with an empty hash *Damien Mathieu* [CVE-2013-0155]
16
317
 
17
- ## Rails 3.2.10 ##
318
+
319
+ ## Rails 3.2.10 (Jan 2, 2013) ##
18
320
 
19
321
  * CVE-2012-5664 options hashes should only be extracted if there are extra
20
322
  parameters
21
323
 
324
+
22
325
  ## Rails 3.2.9 (Nov 12, 2012) ##
23
326
 
327
+ * Fix `find_in_batches` crashing when IDs are strings and start option is not specified.
328
+
329
+ *Alexis Bernard*
330
+
24
331
  * Fix issue with collection associations calling first(n)/last(n) and attempting
25
332
  to set the inverse association when `:inverse_of` was used. Fixes #8087.
26
333
 
@@ -231,7 +231,8 @@ module ActiveRecord
231
231
 
232
232
  def build_record(attributes, options)
233
233
  reflection.build_association(attributes, options) do |record|
234
- attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
234
+ skip_assign = [reflection.foreign_key, reflection.type].compact
235
+ attributes = create_scope.except(*(record.changed - skip_assign))
235
236
  record.assign_attributes(attributes, :without_protection => true)
236
237
  end
237
238
  end
@@ -38,20 +38,6 @@ module ActiveRecord
38
38
  super
39
39
  end
40
40
 
41
- def concat_records(records)
42
- ensure_not_nested
43
-
44
- records = super
45
-
46
- if owner.new_record? && records
47
- records.flatten.each do |record|
48
- build_through_record(record)
49
- end
50
- end
51
-
52
- records
53
- end
54
-
55
41
  def insert_record(record, validate = true, raise = false)
56
42
  ensure_not_nested
57
43
 
@@ -153,6 +139,11 @@ module ActiveRecord
153
139
 
154
140
  delete_through_records(records)
155
141
 
142
+ if source_reflection.options[:counter_cache]
143
+ counter = source_reflection.counter_cache_column
144
+ klass.decrement_counter counter, records.map(&:id)
145
+ end
146
+
156
147
  if through_reflection.macro == :has_many && update_through_counter?(method)
157
148
  update_counter(-count, through_reflection)
158
149
  end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  # If target and record are nil, or target is equal to record,
12
12
  # we don't need to have transaction.
13
13
  if (target || record) && target != record
14
- reflection.klass.transaction do
14
+ transaction_if(save) do
15
15
  remove_target!(options[:dependent]) if target && !target.destroyed?
16
16
 
17
17
  if record
@@ -70,6 +70,14 @@ module ActiveRecord
70
70
  def nullify_owner_attributes(record)
71
71
  record[reflection.foreign_key] = nil
72
72
  end
73
+
74
+ def transaction_if(value)
75
+ if value
76
+ reflection.klass.transaction { yield }
77
+ else
78
+ yield
79
+ end
80
+ end
73
81
  end
74
82
  end
75
83
  end
@@ -37,7 +37,8 @@ module ActiveRecord
37
37
  through_records = Array.wrap(owner.send(through_reflection.name))
38
38
 
39
39
  # Dont cache the association - we would only be caching a subset
40
- if reflection.options[:source_type] && through_reflection.collection?
40
+ if (preload_options != through_options) ||
41
+ (reflection.options[:source_type] && through_reflection.collection?)
41
42
  owner.association(through_reflection.name).reset
42
43
  end
43
44
 
@@ -90,6 +90,14 @@ module ActiveRecord
90
90
  end
91
91
  end
92
92
 
93
+ def _field_changed?(attr, old, value)
94
+ if self.class.serialized_attributes.include?(attr)
95
+ old != value
96
+ else
97
+ super
98
+ end
99
+ end
100
+
93
101
  def read_attribute_before_type_cast(attr_name)
94
102
  if serialized_attributes.include?(attr_name)
95
103
  super.unserialized_value
@@ -97,6 +105,16 @@ module ActiveRecord
97
105
  super
98
106
  end
99
107
  end
108
+
109
+ def attributes_before_type_cast
110
+ super.dup.tap do |attributes|
111
+ self.class.serialized_attributes.each_key do |key|
112
+ if attributes.key?(key)
113
+ attributes[key] = attributes[key].unserialized_value
114
+ end
115
+ end
116
+ end
117
+ end
100
118
  end
101
119
  end
102
120
  end
@@ -37,18 +37,16 @@ module ActiveRecord
37
37
  if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
38
38
  method_body, line = <<-EOV, __LINE__ + 1
39
39
  def #{attr_name}=(original_time)
40
+ original_time = nil if original_time.blank?
40
41
  time = original_time
41
42
  unless time.acts_like?(:time)
42
43
  time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
43
44
  end
44
- zoned_time = time && time.in_time_zone rescue nil
45
- rounded_time = round_usec(zoned_time)
46
- rounded_value = round_usec(read_attribute("#{attr_name}"))
47
- if (rounded_value != rounded_time) || (!rounded_value && original_time)
48
- write_attribute("#{attr_name}", original_time)
49
- #{attr_name}_will_change!
50
- @attributes_cache["#{attr_name}"] = zoned_time
51
- end
45
+ time = time.in_time_zone rescue nil if time
46
+ previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
47
+ write_attribute(:#{attr_name}, original_time)
48
+ #{attr_name}_will_change! if previous_time != time
49
+ @attributes_cache["#{attr_name}"] = time
52
50
  end
53
51
  EOV
54
52
  generated_attribute_methods.module_eval(method_body, __FILE__, line)
@@ -62,12 +60,6 @@ module ActiveRecord
62
60
  time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.in?([:datetime, :timestamp])
63
61
  end
64
62
  end
65
-
66
- private
67
- def round_usec(value)
68
- return unless value
69
- value.change(:usec => 0)
70
- end
71
63
  end
72
64
  end
73
65
  end
@@ -54,12 +54,13 @@ module ActiveRecord
54
54
  end
55
55
 
56
56
  def convert_number_column_value(value)
57
- if value == false
57
+ case value
58
+ when FalseClass
58
59
  0
59
- elsif value == true
60
+ when TrueClass
60
61
  1
61
- elsif value.is_a?(String) && value.blank?
62
- nil
62
+ when String
63
+ value.presence
63
64
  else
64
65
  value
65
66
  end
@@ -479,7 +479,8 @@ module ActiveRecord #:nodoc:
479
479
  # # Instantiates a single new object bypassing mass-assignment security
480
480
  # User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
481
481
  def initialize(attributes = nil, options = {})
482
- @attributes = self.class.initialize_attributes(self.class.column_defaults.dup)
482
+ defaults = Hash[self.class.column_defaults.map { |k, v| [k, v.duplicable? ? v.dup : v] }]
483
+ @attributes = self.class.initialize_attributes(defaults)
483
484
  @association_cache = {}
484
485
  @aggregation_cache = {}
485
486
  @attributes_cache = {}
@@ -552,12 +553,11 @@ module ActiveRecord #:nodoc:
552
553
  @new_record = true
553
554
 
554
555
  ensure_proper_type
555
- populate_with_current_scope_attributes
556
556
  super
557
557
  end
558
558
 
559
559
  # Backport dup from 1.9 so that initialize_dup() gets called
560
- unless Object.respond_to?(:initialize_dup)
560
+ unless Object.respond_to?(:initialize_dup, true)
561
561
  def dup # :nodoc:
562
562
  copy = super
563
563
  copy.initialize_dup(self)
@@ -266,7 +266,7 @@ module ActiveRecord
266
266
  # Inserts the given fixture into the table. Overridden in adapters that require
267
267
  # something beyond a simple insert (eg. Oracle).
268
268
  def insert_fixture(fixture, table_name)
269
- columns = Hash[columns(table_name).map { |c| [c.name, c] }]
269
+ columns = schema_cache.columns_hash(table_name)
270
270
 
271
271
  key_list = []
272
272
  value_list = fixture.map do |name, value|
@@ -25,19 +25,13 @@ module ActiveRecord
25
25
  when true, false
26
26
  if column && column.type == :integer
27
27
  value ? '1' : '0'
28
- elsif column && [:text, :string, :binary].include?(column.type)
29
- value ? "'1'" : "'0'"
30
28
  else
31
29
  value ? quoted_true : quoted_false
32
30
  end
33
31
  # BigDecimals need to be put in a non-normalized form and quoted.
34
32
  when nil then "NULL"
35
- when Numeric, ActiveSupport::Duration
36
- value = BigDecimal === value ? value.to_s('F') : value.to_s
37
- if column && ![:integer, :float, :decimal].include?(column.type)
38
- value = "'#{value}'"
39
- end
40
- value
33
+ when BigDecimal then value.to_s('F')
34
+ when Numeric then value.to_s
41
35
  when Date, Time then "'#{quoted_date(value)}'"
42
36
  when Symbol then "'#{quote_string(value.to_s)}'"
43
37
  else
@@ -199,6 +199,8 @@ module ActiveRecord
199
199
  if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
200
200
  s = column.class.string_to_binary(value).unpack("H*")[0]
201
201
  "x'#{s}'"
202
+ elsif value.kind_of?(BigDecimal)
203
+ value.to_s("F")
202
204
  else
203
205
  super
204
206
  end
@@ -175,7 +175,7 @@ module ActiveRecord
175
175
  when TrueClass, FalseClass
176
176
  value ? 1 : 0
177
177
  else
178
- value.to_i
178
+ value.to_i rescue nil
179
179
  end
180
180
  end
181
181
 
@@ -2,7 +2,7 @@ require 'active_record/connection_adapters/abstract_mysql_adapter'
2
2
  require 'active_record/connection_adapters/statement_pool'
3
3
  require 'active_support/core_ext/hash/keys'
4
4
 
5
- gem 'mysql', '~> 2.8.1'
5
+ gem 'mysql', '~> 2.8'
6
6
  require 'mysql'
7
7
 
8
8
  class Mysql
@@ -315,8 +315,6 @@ module ActiveRecord
315
315
  @visitor = BindSubstitution.new self
316
316
  end
317
317
 
318
- connection_parameters.delete :prepared_statements
319
-
320
318
  @connection_parameters, @config = connection_parameters, config
321
319
 
322
320
  # @local_tz is initialized as nil to avoid warnings when connect tries to use it
@@ -988,12 +986,11 @@ module ActiveRecord
988
986
  # Returns just a table's primary key
989
987
  def primary_key(table)
990
988
  row = exec_query(<<-end_sql, 'SCHEMA').rows.first
991
- SELECT DISTINCT(attr.attname)
989
+ SELECT attr.attname
992
990
  FROM pg_attribute attr
993
- INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
994
991
  INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
995
992
  WHERE cons.contype = 'p'
996
- AND dep.refobjid = '#{quote_table_name(table)}'::regclass
993
+ AND cons.conrelid = '#{quote_table_name(table)}'::regclass
997
994
  end_sql
998
995
 
999
996
  row && row.first
@@ -1078,6 +1075,13 @@ module ActiveRecord
1078
1075
  when nil, 0..0x3fffffff; super(type)
1079
1076
  else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
1080
1077
  end
1078
+ when 'text'
1079
+ # PostgreSQL doesn't support limits on text columns.
1080
+ # The hard limit is 1Gb, according to section 8.3 in the manual.
1081
+ case limit
1082
+ when nil, 0..0x3fffffff; super(type)
1083
+ else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
1084
+ end
1081
1085
  when 'integer'
1082
1086
  return 'integer' unless limit
1083
1087
 
@@ -1135,11 +1139,15 @@ module ActiveRecord
1135
1139
  @connection.server_version
1136
1140
  end
1137
1141
 
1142
+ # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
1143
+ FOREIGN_KEY_VIOLATION = "23503"
1144
+ UNIQUE_VIOLATION = "23505"
1145
+
1138
1146
  def translate_exception(exception, message)
1139
- case exception.message
1140
- when /duplicate key value violates unique constraint/
1147
+ case exception.result.error_field(PGresult::PG_DIAG_SQLSTATE)
1148
+ when UNIQUE_VIOLATION
1141
1149
  RecordNotUnique.new(message, exception)
1142
- when /violates foreign key constraint/
1150
+ when FOREIGN_KEY_VIOLATION
1143
1151
  InvalidForeignKey.new(message, exception)
1144
1152
  else
1145
1153
  super
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  class SchemaCache
4
- attr_reader :columns, :columns_hash, :primary_keys, :tables
4
+ attr_reader :primary_keys, :tables
5
5
  attr_reader :connection
6
6
 
7
7
  def initialize(conn)
@@ -30,6 +30,25 @@ module ActiveRecord
30
30
  @tables[name] = connection.table_exists?(name)
31
31
  end
32
32
 
33
+ # Get the columns for a table
34
+ def columns(table = nil)
35
+ if table
36
+ @columns[table]
37
+ else
38
+ @columns
39
+ end
40
+ end
41
+
42
+ # Get the columns for a table as a hash, key is the column name
43
+ # value is the column object.
44
+ def columns_hash(table = nil)
45
+ if table
46
+ @columns_hash[table]
47
+ else
48
+ @columns_hash
49
+ end
50
+ end
51
+
33
52
  # Clears out internal caches
34
53
  def clear!
35
54
  @columns.clear
@@ -490,7 +490,11 @@ module ActiveRecord
490
490
  end
491
491
 
492
492
  def copy_table(from, to, options = {}) #:nodoc:
493
- options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s))
493
+ from_primary_key = primary_key(from)
494
+ options[:primary_key] = from_primary_key if from_primary_key != 'id'
495
+ unless options[:primary_key]
496
+ options[:id] = !columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == from_primary_key
497
+ end
494
498
  create_table(to, options) do |definition|
495
499
  @definition = definition
496
500
  columns(from).each do |column|
@@ -504,7 +508,7 @@ module ActiveRecord
504
508
  :precision => column.precision, :scale => column.scale,
505
509
  :null => column.null)
506
510
  end
507
- @definition.primary_key(primary_key(from)) if primary_key(from)
511
+ @definition.primary_key(from_primary_key) if from_primary_key
508
512
  yield @definition if block_given?
509
513
  end
510
514
 
@@ -530,7 +534,7 @@ module ActiveRecord
530
534
 
531
535
  unless columns.empty?
532
536
  # index name can't be the same
533
- opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
537
+ opts = { :name => name.gsub(/(^|_)(#{from})_/, "\\1#{to}_") }
534
538
  opts[:unique] = true if index.unique
535
539
  add_index(to, columns, opts)
536
540
  end
@@ -11,11 +11,12 @@ module ActiveRecord
11
11
  end
12
12
  end
13
13
 
14
- # If auto explain is enabled, this method triggers EXPLAIN logging for the
15
- # queries triggered by the block if it takes more than the threshold as a
16
- # whole. That is, the threshold is not checked against each individual
17
- # query, but against the duration of the entire block. This approach is
18
- # convenient for relations.
14
+ # If the database adapter supports explain and auto explain is enabled,
15
+ # this method triggers EXPLAIN logging for the queries triggered by the
16
+ # block if it takes more than the threshold as a whole. That is, the
17
+ # threshold is not checked against each individual query, but against the
18
+ # duration of the entire block. This approach is convenient for relations.
19
+
19
20
  #
20
21
  # The available_queries_for_explain thread variable collects the queries
21
22
  # to be explained. If the value is nil, it means queries are not being
@@ -26,7 +27,7 @@ module ActiveRecord
26
27
 
27
28
  threshold = auto_explain_threshold_in_seconds
28
29
  current = Thread.current
29
- if threshold && current[:available_queries_for_explain].nil?
30
+ if connection.supports_explain? && threshold && current[:available_queries_for_explain].nil?
30
31
  begin
31
32
  queries = current[:available_queries_for_explain] = []
32
33
  start = Time.now
@@ -1,5 +1,16 @@
1
1
  module ActiveRecord
2
2
  module Integration
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ ##
7
+ # :singleton-method:
8
+ # Indicates the format used to generate the timestamp format in the cache key.
9
+ # This is +:number+, by default.
10
+ class_attribute :cache_timestamp_format, :instance_writer => false
11
+ self.cache_timestamp_format = :number
12
+ end
13
+
3
14
  # Returns a String, which Action Pack uses for constructing an URL to this
4
15
  # object. The default implementation returns this record's id as a String,
5
16
  # or nil if this record's unsaved.
@@ -39,7 +50,7 @@ module ActiveRecord
39
50
  when new_record?
40
51
  "#{self.class.model_name.cache_key}/new"
41
52
  when timestamp = self[:updated_at]
42
- timestamp = timestamp.utc.to_s(:number)
53
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
43
54
  "#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
44
55
  else
45
56
  "#{self.class.model_name.cache_key}/#{id}"
@@ -102,6 +102,8 @@ module ActiveRecord
102
102
  def destroy #:nodoc:
103
103
  return super unless locking_enabled?
104
104
 
105
+ destroy_associations
106
+
105
107
  if persisted?
106
108
  table = self.class.arel_table
107
109
  lock_col = self.class.locking_column
@@ -32,7 +32,11 @@ module ActiveRecord
32
32
 
33
33
  unless (payload[:binds] || []).empty?
34
34
  binds = " " + payload[:binds].map { |col,v|
35
- [col.name, v]
35
+ if col
36
+ [col.name, v]
37
+ else
38
+ [nil, v]
39
+ end
36
40
  }.inspect
37
41
  end
38
42
 
@@ -458,7 +458,7 @@ module ActiveRecord
458
458
  say_with_time "#{method}(#{arg_list})" do
459
459
  unless reverting?
460
460
  unless arguments.empty? || method == :execute
461
- arguments[0] = Migrator.proper_table_name(arguments.first)
461
+ arguments[0] = Migrator.proper_table_name(arguments.first) unless method == :assume_migrated_upto_version
462
462
  arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
463
463
  end
464
464
  end
@@ -627,7 +627,7 @@ module ActiveRecord
627
627
  seen = Hash.new false
628
628
 
629
629
  migrations = files.map do |file|
630
- version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
630
+ version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
631
631
 
632
632
  raise IllegalMigrationNameError.new(file) unless version
633
633
  version = version.to_i
@@ -277,13 +277,14 @@ module ActiveRecord
277
277
 
278
278
  type = (reflection.collection? ? :collection : :one_to_one)
279
279
 
280
+ # remove_possible_method :pirate_attributes=
281
+ #
280
282
  # def pirate_attributes=(attributes)
281
283
  # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
282
284
  # end
283
285
  class_eval <<-eoruby, __FILE__, __LINE__ + 1
284
- if method_defined?(:#{association_name}_attributes=)
285
- remove_method(:#{association_name}_attributes=)
286
- end
286
+ remove_possible_method(:#{association_name}_attributes=)
287
+
287
288
  def #{association_name}_attributes=(attributes)
288
289
  assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
289
290
  end
@@ -194,7 +194,7 @@ module ActiveRecord
194
194
  raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
195
195
  raise ActiveRecordError, "can not update on a new record object" unless persisted?
196
196
 
197
- updated_count = self.class.update_all({ name => value }, self.class.primary_key => id)
197
+ updated_count = self.class.unscoped.update_all({ name => value }, self.class.primary_key => id)
198
198
 
199
199
  raw_write_attribute(name, value)
200
200
 
@@ -6,19 +6,19 @@ module ActiveRecord
6
6
  module ClassMethods
7
7
  # Enable the query cache within the block if Active Record is configured.
8
8
  def cache(&block)
9
- if ActiveRecord::Base.configurations.blank?
10
- yield
11
- else
9
+ if ActiveRecord::Base.connected?
12
10
  connection.cache(&block)
11
+ else
12
+ yield
13
13
  end
14
14
  end
15
15
 
16
16
  # Disable the query cache within the block if Active Record is configured.
17
17
  def uncached(&block)
18
- if ActiveRecord::Base.configurations.blank?
19
- yield
20
- else
18
+ if ActiveRecord::Base.connected?
21
19
  connection.uncached(&block)
20
+ else
21
+ yield
22
22
  end
23
23
  end
24
24
  end
@@ -83,6 +83,13 @@ module ActiveRecord
83
83
  end
84
84
  end
85
85
 
86
+ initializer "active_record.validate_explain_support" do |app|
87
+ if app.config.active_record[:auto_explain_threshold_in_seconds] &&
88
+ !ActiveRecord::Base.connection.supports_explain?
89
+ warn "auto_explain_threshold_in_seconds is set but will be ignored because your adapter does not support this feature. Please unset the configuration to avoid this warning."
90
+ end
91
+ end
92
+
86
93
  # Expose database runtime to controller for logging.
87
94
  initializer "active_record.log_runtime" do |app|
88
95
  require "active_record/railties/controller_runtime"
@@ -64,10 +64,21 @@ db_namespace = namespace :db do
64
64
  end
65
65
  end
66
66
 
67
+ # If neither encoding nor collation is specified, use the utf-8 defaults.
67
68
  def mysql_creation_options(config)
68
- @charset = ENV['CHARSET'] || 'utf8'
69
- @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
70
- {:charset => (config['encoding'] || @charset), :collation => (config['collation'] || @collation)}
69
+ default_charset = ENV['CHARSET'] || 'utf8'
70
+ default_collation = ENV['COLLATION'] || 'utf8_unicode_ci'
71
+
72
+ Hash.new.tap do |options|
73
+ options[:charset] = config['encoding'] if config.include? 'encoding'
74
+ options[:collation] = config['collation'] if config.include? 'collation'
75
+
76
+ # Set default charset only when collation isn't set.
77
+ options[:charset] ||= default_charset unless options[:collation]
78
+
79
+ # Set default collation only when charset is also default.
80
+ options[:collation] ||= default_collation if options[:charset] == default_charset
81
+ end
71
82
  end
72
83
 
73
84
  def create_database(config)
@@ -101,9 +112,12 @@ db_namespace = namespace :db do
101
112
  error_class = config['adapter'] =~ /mysql2/ ? Mysql2::Error : Mysql::Error
102
113
  end
103
114
  access_denied_error = 1045
115
+
116
+ create_options = mysql_creation_options(config)
117
+
104
118
  begin
105
119
  ActiveRecord::Base.establish_connection(config.merge('database' => nil))
106
- ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
120
+ ActiveRecord::Base.connection.create_database(config['database'], create_options)
107
121
  ActiveRecord::Base.establish_connection(config)
108
122
  rescue error_class => sqlerr
109
123
  if sqlerr.errno == access_denied_error
@@ -119,7 +133,7 @@ db_namespace = namespace :db do
119
133
  ActiveRecord::Base.establish_connection(config)
120
134
  else
121
135
  $stderr.puts sqlerr.error
122
- $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['encoding'] || @charset}, collation: #{config['collation'] || @collation}"
136
+ $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{create_options[:charset]}, collation: #{create_options[:collation]}"
123
137
  $stderr.puts "(if you set the charset manually, make sure you have a matching collation)" if config['encoding']
124
138
  end
125
139
  end
@@ -419,11 +433,11 @@ db_namespace = namespace :db do
419
433
  when 'sqlserver'
420
434
  `smoscript -s #{config['host']} -d #{config['database']} -u #{config['username']} -p #{config['password']} -f #{filename} -A -U`
421
435
  when "firebird"
422
- set_firebird_env(abcs[Rails.env])
423
- db_string = firebird_db_string(abcs[Rails.env])
436
+ set_firebird_env(config)
437
+ db_string = firebird_db_string(config)
424
438
  sh "isql -a #{db_string} > #{filename}"
425
439
  else
426
- raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
440
+ raise "Task not supported by '#{config['adapter']}'"
427
441
  end
428
442
 
429
443
  if ActiveRecord::Base.connection.supports_migrations?
@@ -59,11 +59,11 @@ module ActiveRecord
59
59
  relation = apply_finder_options(finder_options)
60
60
  end
61
61
 
62
- start = options.delete(:start).to_i
62
+ start = options.delete(:start)
63
63
  batch_size = options.delete(:batch_size) || 1000
64
64
 
65
65
  relation = relation.reorder(batch_order).limit(batch_size)
66
- records = relation.where(table[primary_key].gteq(start)).all
66
+ records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
67
67
 
68
68
  while records.any?
69
69
  records_size = records.size
@@ -177,8 +177,15 @@ module ActiveRecord
177
177
  # Person.where(:confirmed => true).limit(5).pluck(:id)
178
178
  #
179
179
  def pluck(column_name)
180
- column_name = column_name.to_s
181
- klass.connection.select_all(select(column_name).arel).map! do |attributes|
180
+ if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
181
+ column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
182
+ else
183
+ column_name = column_name.to_s
184
+ end
185
+
186
+ relation = clone
187
+ relation.select_values = [column_name]
188
+ klass.connection.select_all(relation.arel).map! do |attributes|
182
189
  klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
183
190
  end
184
191
  end
@@ -188,7 +195,8 @@ module ActiveRecord
188
195
  def perform_calculation(operation, column_name, options = {})
189
196
  operation = operation.to_s.downcase
190
197
 
191
- distinct = options[:distinct]
198
+ # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
199
+ distinct = options[:distinct] || self.uniq_value
192
200
 
193
201
  if operation == "count"
194
202
  column_name ||= (select_for_count || :all)
@@ -253,9 +253,11 @@ module ActiveRecord
253
253
  orders = relation.order_values.map { |val| val.presence }.compact
254
254
  values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
255
255
 
256
- relation = relation.dup
256
+ relation = relation.dup.select(values)
257
+
258
+ id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
259
+ ids_array = id_rows.map {|row| row[primary_key]}
257
260
 
258
- ids_array = relation.select(values).collect {|row| row[primary_key]}
259
261
  ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
260
262
  end
261
263
 
@@ -263,7 +265,7 @@ module ActiveRecord
263
265
  conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
264
266
  result = where(conditions).send(match.finder)
265
267
 
266
- if match.bang? && result.blank?
268
+ if match.bang? && result.nil?
267
269
  raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
268
270
  else
269
271
  yield(result) if block_given?
@@ -51,10 +51,6 @@ module ActiveRecord
51
51
  when Class
52
52
  # FIXME: I think we need to deprecate this behavior
53
53
  attribute.eq(value.name)
54
- when Integer, ActiveSupport::Duration
55
- # Arel treats integers as literals, but they should be quoted when compared with strings
56
- column = engine.connection.schema_cache.columns_hash[table.name][attribute.name.to_s]
57
- attribute.eq(Arel::Nodes::SqlLiteral.new(engine.connection.quote(value, column)))
58
54
  else
59
55
  attribute.eq(value)
60
56
  end
@@ -161,16 +161,14 @@ module ActiveRecord
161
161
  # end
162
162
  #
163
163
  # def self.titles
164
- # map(&:title)
164
+ # pluck(:title)
165
165
  # end
166
- #
167
166
  # end
168
167
  #
169
168
  # We are able to call the methods like this:
170
169
  #
171
170
  # Article.published.featured.latest_article
172
171
  # Article.featured.titles
173
-
174
172
  def scope(name, scope_options = {})
175
173
  name = name.to_sym
176
174
  valid_scope_name?(name)
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
27
27
 
28
28
  Array.wrap(options[:scope]).each do |scope_item|
29
- scope_value = record.send(scope_item)
29
+ scope_value = record.read_attribute(scope_item)
30
30
  relation = relation.and(table[scope_item].eq(scope_value))
31
31
  end
32
32
 
@@ -2,8 +2,8 @@ module ActiveRecord
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
4
  MINOR = 2
5
- TINY = 12
6
- PRE = nil
5
+ TINY = 13
6
+ PRE = "rc1"
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.12
4
+ version: 3.2.13.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-02-11 00:00:00.000000000 Z
11
+ date: 2013-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,54 +16,54 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 3.2.12
19
+ version: 3.2.13.rc1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 3.2.12
26
+ version: 3.2.13.rc1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 3.2.12
33
+ version: 3.2.13.rc1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 3.2.12
40
+ version: 3.2.13.rc1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: arel
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
47
  version: 3.0.2
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: 3.0.2
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: tzinfo
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ~>
60
60
  - !ruby/object:Gem::Version
61
61
  version: 0.3.29
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ~>
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.3.29
69
69
  description: Databases on Rails. Build a persistent domain model by mapping database
@@ -229,23 +229,23 @@ licenses: []
229
229
  metadata: {}
230
230
  post_install_message:
231
231
  rdoc_options:
232
- - "--main"
232
+ - --main
233
233
  - README.rdoc
234
234
  require_paths:
235
235
  - lib
236
236
  required_ruby_version: !ruby/object:Gem::Requirement
237
237
  requirements:
238
- - - ">="
238
+ - - '>='
239
239
  - !ruby/object:Gem::Version
240
240
  version: 1.8.7
241
241
  required_rubygems_version: !ruby/object:Gem::Requirement
242
242
  requirements:
243
- - - ">="
243
+ - - '>'
244
244
  - !ruby/object:Gem::Version
245
- version: '0'
245
+ version: 1.3.1
246
246
  requirements: []
247
247
  rubyforge_project:
248
- rubygems_version: 2.0.0.rc.2
248
+ rubygems_version: 2.0.0
249
249
  signing_key:
250
250
  specification_version: 4
251
251
  summary: Object-relational mapper framework (part of Rails).