activerecord 2.3.5 → 2.3.6

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 (90) hide show
  1. data/CHANGELOG +33 -0
  2. data/Rakefile +1 -1
  3. data/examples/performance.sql +85 -0
  4. data/lib/active_record.rb +1 -2
  5. data/lib/active_record/association_preload.rb +9 -2
  6. data/lib/active_record/associations.rb +48 -38
  7. data/lib/active_record/associations/association_collection.rb +15 -11
  8. data/lib/active_record/associations/association_proxy.rb +16 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +11 -1
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -10
  11. data/lib/active_record/associations/has_many_association.rb +5 -0
  12. data/lib/active_record/associations/has_many_through_association.rb +5 -5
  13. data/lib/active_record/associations/has_one_association.rb +10 -1
  14. data/lib/active_record/attribute_methods.rb +5 -1
  15. data/lib/active_record/autosave_association.rb +66 -35
  16. data/lib/active_record/base.rb +77 -36
  17. data/lib/active_record/batches.rb +13 -9
  18. data/lib/active_record/calculations.rb +6 -3
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -3
  20. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -7
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +64 -10
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -0
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +31 -1
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +31 -66
  28. data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
  29. data/lib/active_record/dirty.rb +2 -2
  30. data/lib/active_record/fixtures.rb +1 -0
  31. data/lib/active_record/locking/optimistic.rb +34 -1
  32. data/lib/active_record/migration.rb +5 -0
  33. data/lib/active_record/nested_attributes.rb +64 -52
  34. data/lib/active_record/reflection.rb +66 -1
  35. data/lib/active_record/schema.rb +5 -1
  36. data/lib/active_record/schema_dumper.rb +3 -0
  37. data/lib/active_record/serializers/json_serializer.rb +1 -1
  38. data/lib/active_record/validations.rb +13 -1
  39. data/lib/active_record/version.rb +1 -1
  40. data/test/cases/active_schema_test_mysql.rb +22 -0
  41. data/test/cases/associations/belongs_to_associations_test.rb +13 -0
  42. data/test/cases/associations/eager_load_nested_include_test.rb +8 -7
  43. data/test/cases/associations/eager_test.rb +7 -1
  44. data/test/cases/associations/has_many_associations_test.rb +26 -0
  45. data/test/cases/associations/inverse_associations_test.rb +566 -0
  46. data/test/cases/associations_test.rb +10 -0
  47. data/test/cases/autosave_association_test.rb +86 -10
  48. data/test/cases/base_test.rb +29 -0
  49. data/test/cases/batches_test.rb +20 -0
  50. data/test/cases/calculations_test.rb +2 -3
  51. data/test/cases/encoding_test.rb +6 -0
  52. data/test/cases/finder_test.rb +19 -3
  53. data/test/cases/fixtures_test.rb +5 -0
  54. data/test/cases/json_serialization_test.rb +14 -0
  55. data/test/cases/locking_test.rb +48 -3
  56. data/test/cases/migration_test.rb +115 -0
  57. data/test/cases/modules_test.rb +28 -0
  58. data/test/cases/named_scope_test.rb +1 -1
  59. data/test/cases/nested_attributes_test.rb +239 -7
  60. data/test/cases/query_cache_test.rb +7 -1
  61. data/test/cases/reflection_test.rb +47 -7
  62. data/test/cases/schema_test_postgresql.rb +2 -2
  63. data/test/cases/validations_i18n_test.rb +6 -36
  64. data/test/cases/validations_test.rb +33 -1
  65. data/test/cases/yaml_serialization_test.rb +11 -0
  66. data/test/fixtures/faces.yml +11 -0
  67. data/test/fixtures/fixture_database.sqlite +0 -0
  68. data/test/fixtures/fixture_database.sqlite3 +0 -0
  69. data/test/fixtures/fixture_database_2.sqlite +0 -0
  70. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  71. data/test/fixtures/interests.yml +33 -0
  72. data/test/fixtures/men.yml +5 -0
  73. data/test/fixtures/zines.yml +5 -0
  74. data/test/models/author.rb +3 -0
  75. data/test/models/bird.rb +6 -0
  76. data/test/models/company_in_module.rb +17 -0
  77. data/test/models/event_author.rb +5 -0
  78. data/test/models/face.rb +7 -0
  79. data/test/models/interest.rb +5 -0
  80. data/test/models/invoice.rb +4 -0
  81. data/test/models/line_item.rb +3 -0
  82. data/test/models/man.rb +9 -0
  83. data/test/models/parrot.rb +6 -0
  84. data/test/models/pirate.rb +10 -0
  85. data/test/models/ship.rb +10 -1
  86. data/test/models/ship_part.rb +3 -1
  87. data/test/models/zine.rb +3 -0
  88. data/test/schema/schema.rb +41 -0
  89. metadata +37 -11
  90. data/lib/active_record/i18n_interpolation_deprecation.rb +0 -26
@@ -244,8 +244,8 @@ module ActiveRecord
244
244
  column ? column['name'] : nil
245
245
  end
246
246
 
247
- def remove_index(table_name, options={}) #:nodoc:
248
- execute "DROP INDEX #{quote_column_name(index_name(table_name, options))}"
247
+ def remove_index!(table_name, index_name) #:nodoc:
248
+ execute "DROP INDEX #{quote_column_name(index_name)}"
249
249
  end
250
250
 
251
251
  def rename_table(name, new_name)
@@ -167,13 +167,13 @@ module ActiveRecord
167
167
 
168
168
  module ClassMethods
169
169
  def self.extended(base)
170
- base.metaclass.alias_method_chain(:alias_attribute, :dirty)
170
+ base.singleton_class.alias_method_chain(:alias_attribute, :dirty)
171
171
  end
172
172
 
173
173
  def alias_attribute_with_dirty(new_name, old_name)
174
174
  alias_attribute_without_dirty(new_name, old_name)
175
175
  DIRTY_SUFFIXES.each do |suffix|
176
- module_eval <<-STR, __FILE__, __LINE__+1
176
+ module_eval <<-STR, __FILE__, __LINE__ + 1
177
177
  def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end # def subject_changed?; self.title_changed?; end
178
178
  STR
179
179
  end
@@ -891,6 +891,7 @@ module ActiveRecord
891
891
 
892
892
  instances.size == 1 ? instances.first : instances
893
893
  end
894
+ private table_name
894
895
  end
895
896
  end
896
897
 
@@ -23,6 +23,16 @@ module ActiveRecord
23
23
  # p2.first_name = "should fail"
24
24
  # p2.save # Raises a ActiveRecord::StaleObjectError
25
25
  #
26
+ # Optimistic locking will also check for stale data when objects are destroyed. Example:
27
+ #
28
+ # p1 = Person.find(1)
29
+ # p2 = Person.find(1)
30
+ #
31
+ # p1.first_name = "Michael"
32
+ # p1.save
33
+ #
34
+ # p2.destroy # Raises a ActiveRecord::StaleObjectError
35
+ #
26
36
  # You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
27
37
  # or otherwise apply the business logic needed to resolve the conflict.
28
38
  #
@@ -39,6 +49,7 @@ module ActiveRecord
39
49
  base.lock_optimistically = true
40
50
 
41
51
  base.alias_method_chain :update, :lock
52
+ base.alias_method_chain :destroy, :lock
42
53
  base.alias_method_chain :attributes_from_column_definition, :lock
43
54
 
44
55
  class << base
@@ -86,7 +97,7 @@ module ActiveRecord
86
97
  end_sql
87
98
 
88
99
  unless affected_rows == 1
89
- raise ActiveRecord::StaleObjectError, "Attempted to update a stale object"
100
+ raise ActiveRecord::StaleObjectError, "Attempted to update a stale object: #{self.class.name}"
90
101
  end
91
102
 
92
103
  affected_rows
@@ -98,6 +109,28 @@ module ActiveRecord
98
109
  end
99
110
  end
100
111
 
112
+ def destroy_with_lock #:nodoc:
113
+ return destroy_without_lock unless locking_enabled?
114
+
115
+ unless new_record?
116
+ lock_col = self.class.locking_column
117
+ previous_value = send(lock_col).to_i
118
+
119
+ affected_rows = connection.delete(
120
+ "DELETE FROM #{self.class.quoted_table_name} " +
121
+ "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id} " +
122
+ "AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}",
123
+ "#{self.class.name} Destroy"
124
+ )
125
+
126
+ unless affected_rows == 1
127
+ raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object: #{self.class.name}"
128
+ end
129
+ end
130
+
131
+ freeze
132
+ end
133
+
101
134
  module ClassMethods
102
135
  DEFAULT_LOCKING_COLUMN = 'lock_version'
103
136
 
@@ -381,6 +381,7 @@ module ActiveRecord
381
381
  def migrate(migrations_path, target_version = nil)
382
382
  case
383
383
  when target_version.nil? then up(migrations_path, target_version)
384
+ when current_version == 0 && target_version == 0 then # noop
384
385
  when current_version > target_version then down(migrations_path, target_version)
385
386
  else up(migrations_path, target_version)
386
387
  end
@@ -408,6 +409,10 @@ module ActiveRecord
408
409
  self.new(direction, migrations_path, target_version).run
409
410
  end
410
411
 
412
+ def migrations_path
413
+ 'db/migrate'
414
+ end
415
+
411
416
  def schema_migrations_table_name
412
417
  Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix
413
418
  end
@@ -184,6 +184,8 @@ module ActiveRecord
184
184
  # the parent model is saved. This happens inside the transaction initiated
185
185
  # by the parents save method. See ActiveRecord::AutosaveAssociation.
186
186
  module ClassMethods
187
+ REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } }
188
+
187
189
  # Defines an attributes writer for the specified association(s). If you
188
190
  # are using <tt>attr_protected</tt> or <tt>attr_accessible</tt>, then you
189
191
  # will need to add the attribute writer to the allowed list.
@@ -208,39 +210,40 @@ module ActiveRecord
208
210
  # nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
209
211
  # exception is raised. If omitted, any number associations can be processed.
210
212
  # Note that the :limit option is only applicable to one-to-many associations.
213
+ # [:update_only]
214
+ # Allows you to specify that an existing record may only be updated.
215
+ # A new record may only be created when there is no existing record.
216
+ # This option only works for one-to-one associations and is ignored for
217
+ # collection associations. This option is off by default.
211
218
  #
212
219
  # Examples:
213
220
  # # creates avatar_attributes=
214
221
  # accepts_nested_attributes_for :avatar, :reject_if => proc { |attributes| attributes['name'].blank? }
222
+ # # creates avatar_attributes=
223
+ # accepts_nested_attributes_for :avatar, :reject_if => :all_blank
215
224
  # # creates avatar_attributes= and posts_attributes=
216
225
  # accepts_nested_attributes_for :avatar, :posts, :allow_destroy => true
217
226
  def accepts_nested_attributes_for(*attr_names)
218
- options = { :allow_destroy => false }
227
+ options = { :allow_destroy => false, :update_only => false }
219
228
  options.update(attr_names.extract_options!)
220
- options.assert_valid_keys(:allow_destroy, :reject_if, :limit)
229
+ options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
230
+ options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
221
231
 
222
232
  attr_names.each do |association_name|
223
233
  if reflection = reflect_on_association(association_name)
224
- type = case reflection.macro
225
- when :has_one, :belongs_to
226
- :one_to_one
227
- when :has_many, :has_and_belongs_to_many
228
- :collection
229
- end
230
-
231
234
  reflection.options[:autosave] = true
232
- self.nested_attributes_options[association_name.to_sym] = options
235
+ add_autosave_association_callbacks(reflection)
236
+ nested_attributes_options[association_name.to_sym] = options
237
+ type = (reflection.collection? ? :collection : :one_to_one)
233
238
 
234
239
  # def pirate_attributes=(attributes)
235
- # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, false)
240
+ # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
236
241
  # end
237
- class_eval %{
242
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
238
243
  def #{association_name}_attributes=(attributes)
239
244
  assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
240
245
  end
241
- }, __FILE__, __LINE__
242
-
243
- add_autosave_association_callbacks(reflection)
246
+ EOS
244
247
  else
245
248
  raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
246
249
  end
@@ -257,46 +260,41 @@ module ActiveRecord
257
260
  marked_for_destruction?
258
261
  end
259
262
 
260
- # Deal with deprecated _delete.
261
- #
262
- def _delete #:nodoc:
263
- ActiveSupport::Deprecation.warn "_delete is deprecated in nested attributes. Use _destroy instead."
264
- _destroy
265
- end
266
-
267
263
  private
268
264
 
269
265
  # Attribute hash keys that should not be assigned as normal attributes.
270
266
  # These hash keys are nested attributes implementation details.
271
- #
272
- # TODO Remove _delete from UNASSIGNABLE_KEYS when deprecation warning are
273
- # removed.
274
- UNASSIGNABLE_KEYS = %w( id _destroy _delete )
267
+ UNASSIGNABLE_KEYS = %w( id _destroy )
275
268
 
276
269
  # Assigns the given attributes to the association.
277
270
  #
278
- # If the given attributes include an <tt>:id</tt> that matches the existing
279
- # record’s id, then the existing record will be modified. Otherwise a new
280
- # record will be built.
271
+ # If update_only is false and the given attributes include an <tt>:id</tt>
272
+ # that matches the existing record’s id, then the existing record will be
273
+ # modified. If update_only is true, a new record is only created when no
274
+ # object exists. Otherwise a new record will be built.
281
275
  #
282
- # If the given attributes include a matching <tt>:id</tt> attribute _and_ a
283
- # <tt>:_destroy</tt> key set to a truthy value, then the existing record
284
- # will be marked for destruction.
276
+ # If the given attributes include a matching <tt>:id</tt> attribute, or
277
+ # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
278
+ # then the existing record will be marked for destruction.
285
279
  def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
286
- options = self.nested_attributes_options[association_name]
280
+ options = nested_attributes_options[association_name]
287
281
  attributes = attributes.with_indifferent_access
282
+ check_existing_record = (options[:update_only] || !attributes['id'].blank?)
288
283
 
289
- if attributes['id'].blank?
290
- unless reject_new_record?(association_name, attributes)
291
- method = "build_#{association_name}"
292
- if respond_to?(method)
293
- send(method, attributes.except(*UNASSIGNABLE_KEYS))
294
- else
295
- raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
296
- end
284
+ if check_existing_record && (record = send(association_name)) &&
285
+ (options[:update_only] || record.id.to_s == attributes['id'].to_s)
286
+ assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy])
287
+
288
+ elsif attributes['id']
289
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
290
+
291
+ elsif !reject_new_record?(association_name, attributes)
292
+ method = "build_#{association_name}"
293
+ if respond_to?(method)
294
+ send(method, attributes.except(*UNASSIGNABLE_KEYS))
295
+ else
296
+ raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
297
297
  end
298
- elsif (existing_record = send(association_name)) && existing_record.id.to_s == attributes['id'].to_s
299
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
300
298
  end
301
299
  end
302
300
 
@@ -328,7 +326,7 @@ module ActiveRecord
328
326
  # { :id => '2', :_destroy => true }
329
327
  # ])
330
328
  def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
331
- options = self.nested_attributes_options[association_name]
329
+ options = nested_attributes_options[association_name]
332
330
 
333
331
  unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
334
332
  raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
@@ -342,15 +340,27 @@ module ActiveRecord
342
340
  attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes }
343
341
  end
344
342
 
343
+ association = send(association_name)
344
+
345
+ existing_records = if association.loaded?
346
+ association.to_a
347
+ else
348
+ attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
349
+ attribute_ids.present? ? association.all(:conditions => {association.primary_key => attribute_ids}) : []
350
+ end
351
+
345
352
  attributes_collection.each do |attributes|
346
353
  attributes = attributes.with_indifferent_access
347
354
 
348
355
  if attributes['id'].blank?
349
356
  unless reject_new_record?(association_name, attributes)
350
- send(association_name).build(attributes.except(*UNASSIGNABLE_KEYS))
357
+ association.build(attributes.except(*UNASSIGNABLE_KEYS))
351
358
  end
352
- elsif existing_record = send(association_name).detect { |record| record.id.to_s == attributes['id'].to_s }
359
+ elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
360
+ association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
353
361
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
362
+ else
363
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
354
364
  end
355
365
  end
356
366
  end
@@ -367,8 +377,7 @@ module ActiveRecord
367
377
 
368
378
  # Determines if a hash contains a truthy _destroy key.
369
379
  def has_destroy_flag?(hash)
370
- ConnectionAdapters::Column.value_to_boolean(hash['_destroy']) ||
371
- ConnectionAdapters::Column.value_to_boolean(hash['_delete']) # TODO Remove after deprecation.
380
+ ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
372
381
  end
373
382
 
374
383
  # Determines if a new record should be build by checking for
@@ -379,14 +388,17 @@ module ActiveRecord
379
388
  end
380
389
 
381
390
  def call_reject_if(association_name, attributes)
382
- callback = self.nested_attributes_options[association_name][:reject_if]
383
-
384
- case callback
391
+ case callback = nested_attributes_options[association_name][:reject_if]
385
392
  when Symbol
386
393
  method(callback).arity == 0 ? send(callback) : send(callback, attributes)
387
394
  when Proc
388
- callback.try(:call, attributes)
395
+ callback.call(attributes)
389
396
  end
390
397
  end
398
+
399
+ def raise_nested_attributes_record_not_found(association_name, record_id)
400
+ reflection = self.class.reflect_on_association(association_name)
401
+ raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
402
+ end
391
403
  end
392
404
  end
@@ -212,6 +212,15 @@ module ActiveRecord
212
212
  end
213
213
 
214
214
  def check_validity!
215
+ check_validity_of_inverse!
216
+ end
217
+
218
+ def check_validity_of_inverse!
219
+ unless options[:polymorphic]
220
+ if has_inverse? && inverse_of.nil?
221
+ raise InverseOfAssociationNotFoundError.new(self)
222
+ end
223
+ end
215
224
  end
216
225
 
217
226
  def through_reflection
@@ -225,10 +234,64 @@ module ActiveRecord
225
234
  nil
226
235
  end
227
236
 
237
+ def has_inverse?
238
+ !@options[:inverse_of].nil?
239
+ end
240
+
241
+ def inverse_of
242
+ if has_inverse?
243
+ @inverse_of ||= klass.reflect_on_association(options[:inverse_of])
244
+ end
245
+ end
246
+
247
+ def polymorphic_inverse_of(associated_class)
248
+ if has_inverse?
249
+ if inverse_relationship = associated_class.reflect_on_association(options[:inverse_of])
250
+ inverse_relationship
251
+ else
252
+ raise InverseOfAssociationNotFoundError.new(self, associated_class)
253
+ end
254
+ end
255
+ end
256
+
257
+ # Returns whether or not this association reflection is for a collection
258
+ # association. Returns +true+ if the +macro+ is one of +has_many+ or
259
+ # +has_and_belongs_to_many+, +false+ otherwise.
260
+ def collection?
261
+ if @collection.nil?
262
+ @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
263
+ end
264
+ @collection
265
+ end
266
+
267
+ # Returns whether or not the association should be validated as part of
268
+ # the parent's validation.
269
+ #
270
+ # Unless you explicitely disable validation with
271
+ # <tt>:validate => false</tt>, it will take place when:
272
+ #
273
+ # * you explicitely enable validation; <tt>:validate => true</tt>
274
+ # * you use autosave; <tt>:autosave => true</tt>
275
+ # * the association is a +has_many+ association
276
+ def validate?
277
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
278
+ end
279
+
280
+ def dependent_conditions(record, base_class, extra_conditions)
281
+ dependent_conditions = []
282
+ dependent_conditions << "#{primary_key_name} = #{record.send(name).send(:owner_quoted_id)}"
283
+ dependent_conditions << "#{options[:as]}_type = '#{base_class.name}'" if options[:as]
284
+ dependent_conditions << klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
285
+ dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ")
286
+ dependent_conditions << extra_conditions if extra_conditions
287
+ dependent_conditions = dependent_conditions.gsub('@', '\@')
288
+ dependent_conditions
289
+ end
290
+
228
291
  private
229
292
  def derive_class_name
230
293
  class_name = name.to_s.camelize
231
- class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
294
+ class_name = class_name.singularize if collection?
232
295
  class_name
233
296
  end
234
297
 
@@ -300,6 +363,8 @@ module ActiveRecord
300
363
  unless [:belongs_to, :has_many, :has_one].include?(source_reflection.macro) && source_reflection.options[:through].nil?
301
364
  raise HasManyThroughSourceAssociationMacroError.new(self)
302
365
  end
366
+
367
+ check_validity_of_inverse!
303
368
  end
304
369
 
305
370
  def through_reflection_primary_key
@@ -28,6 +28,10 @@ module ActiveRecord
28
28
  class Schema < Migration
29
29
  private_class_method :new
30
30
 
31
+ def self.migrations_path
32
+ ActiveRecord::Migrator.migrations_path
33
+ end
34
+
31
35
  # Eval the given block. All methods available to the current connection
32
36
  # adapter are available within the block, so you can easily use the
33
37
  # database definition DSL to build up your schema (+create_table+,
@@ -44,7 +48,7 @@ module ActiveRecord
44
48
 
45
49
  unless info[:version].blank?
46
50
  initialize_schema_migrations_table
47
- assume_migrated_upto_version info[:version]
51
+ assume_migrated_upto_version(info[:version], migrations_path)
48
52
  end
49
53
  end
50
54
  end
@@ -171,6 +171,9 @@ HEADER
171
171
  statment_parts << (':name => ' + index.name.inspect)
172
172
  statment_parts << ':unique => true' if index.unique
173
173
 
174
+ index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
175
+ statment_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present?
176
+
174
177
  ' ' + statment_parts.join(', ')
175
178
  end
176
179
 
@@ -79,7 +79,7 @@ module ActiveRecord #:nodoc:
79
79
 
80
80
  def as_json(options = nil) #:nodoc:
81
81
  hash = Serializer.new(self, options).serializable_record
82
- hash = { self.class.model_name.element => hash } if include_root_in_json
82
+ hash = { options[:root] || self.class.model_name.element => hash } if include_root_in_json
83
83
  hash
84
84
  end
85
85