activerecord 2.3.9 → 2.3.10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +4 -0
- data/Rakefile +1 -1
- data/examples/performance.rb +37 -3
- data/lib/active_record/association_preload.rb +1 -1
- data/lib/active_record/associations/association_collection.rb +15 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +2 -1
- data/lib/active_record/named_scope.rb +1 -1
- data/lib/active_record/nested_attributes.rb +8 -11
- data/lib/active_record/version.rb +1 -1
- data/test/cases/associations/eager_test.rb +6 -1
- data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +5 -0
- data/test/cases/associations/has_many_associations_test.rb +6 -1
- data/test/cases/associations/has_many_through_associations_test.rb +14 -0
- data/test/cases/migration_test.rb +7 -0
- data/test/cases/named_scope_test.rb +6 -0
- data/test/cases/nested_attributes_test.rb +12 -14
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/fixtures/fixture_database.sqlite3 +0 -0
- data/test/fixtures/fixture_database_2.sqlite +0 -0
- data/test/fixtures/fixture_database_2.sqlite3 +0 -0
- metadata +15 -6
data/CHANGELOG
CHANGED
data/Rakefile
CHANGED
@@ -192,7 +192,7 @@ spec = Gem::Specification.new do |s|
|
|
192
192
|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
193
193
|
end
|
194
194
|
|
195
|
-
s.add_dependency('activesupport', '= 2.3.
|
195
|
+
s.add_dependency('activesupport', '= 2.3.10' + PKG_BUILD)
|
196
196
|
|
197
197
|
s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite"
|
198
198
|
s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite"
|
data/examples/performance.rb
CHANGED
@@ -27,7 +27,7 @@ conn[:socket] = Pathname.glob(%w[
|
|
27
27
|
/tmp/mysql.sock
|
28
28
|
/var/mysql/mysql.sock
|
29
29
|
/var/run/mysqld/mysqld.sock
|
30
|
-
]).find { |path| path.socket? }
|
30
|
+
]).find { |path| path.socket? }.to_s
|
31
31
|
|
32
32
|
ActiveRecord::Base.establish_connection(conn)
|
33
33
|
|
@@ -60,7 +60,7 @@ end
|
|
60
60
|
sqlfile = "#{__DIR__}/performance.sql"
|
61
61
|
|
62
62
|
if File.exists?(sqlfile)
|
63
|
-
mysql_bin = %w[mysql mysql5].
|
63
|
+
mysql_bin = %w[mysql mysql5].detect { |bin| `which #{bin}`.length > 0 }
|
64
64
|
`#{mysql_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} < #{sqlfile}`
|
65
65
|
else
|
66
66
|
puts 'Generating data...'
|
@@ -90,7 +90,7 @@ else
|
|
90
90
|
)
|
91
91
|
end
|
92
92
|
|
93
|
-
mysqldump_bin = %w[mysqldump mysqldump5].
|
93
|
+
mysqldump_bin = %w[mysqldump mysqldump5].detect { |bin| `which #{bin}`.length > 0 }
|
94
94
|
`#{mysqldump_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} exhibits users > #{sqlfile}`
|
95
95
|
end
|
96
96
|
|
@@ -157,6 +157,40 @@ RBench.run(TIMES) do
|
|
157
157
|
ar { Exhibit.transaction { Exhibit.new } }
|
158
158
|
end
|
159
159
|
|
160
|
+
report 'Model.find(id)' do
|
161
|
+
id = Exhibit.first.id
|
162
|
+
ar { Exhibit.find(id) }
|
163
|
+
end
|
164
|
+
|
165
|
+
report 'Model.find_by_sql' do
|
166
|
+
ar { Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first }
|
167
|
+
end
|
168
|
+
|
169
|
+
report 'Model.log', (TIMES * 10) do
|
170
|
+
ar { Exhibit.connection.send(:log, "hello", "world") {} }
|
171
|
+
end
|
172
|
+
|
173
|
+
report 'AR.execute(query)', (TIMES / 2) do
|
174
|
+
ar { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
|
175
|
+
end
|
176
|
+
|
177
|
+
report 'Model.find(id)' do
|
178
|
+
id = Exhibit.first.id
|
179
|
+
ar { Exhibit.find(id) }
|
180
|
+
end
|
181
|
+
|
182
|
+
report 'Model.find_by_sql' do
|
183
|
+
ar { Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first }
|
184
|
+
end
|
185
|
+
|
186
|
+
report 'Model.log', (TIMES * 10) do
|
187
|
+
ar { Exhibit.connection.send(:log, "hello", "world") {} }
|
188
|
+
end
|
189
|
+
|
190
|
+
report 'AR.execute(query)', (TIMES / 2) do
|
191
|
+
ar { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
|
192
|
+
end
|
193
|
+
|
160
194
|
summary 'Total'
|
161
195
|
end
|
162
196
|
|
@@ -283,7 +283,7 @@ module ActiveRecord
|
|
283
283
|
through_records.flatten!
|
284
284
|
else
|
285
285
|
options = {}
|
286
|
-
options[:include] = reflection.options[:include] || reflection.options[:source] if reflection.options[:conditions]
|
286
|
+
options[:include] = reflection.options[:include] || reflection.options[:source] if reflection.options[:conditions] || reflection.options[:order]
|
287
287
|
options[:order] = reflection.options[:order]
|
288
288
|
options[:conditions] = reflection.options[:conditions]
|
289
289
|
records.first.class.preload_associations(records, through_association, options)
|
@@ -332,6 +332,7 @@ module ActiveRecord
|
|
332
332
|
|
333
333
|
def include?(record)
|
334
334
|
return false unless record.is_a?(@reflection.klass)
|
335
|
+
return include_in_memory?(record) if record.new_record?
|
335
336
|
load_target if @reflection.options[:finder_sql] && !loaded?
|
336
337
|
return @target.include?(record) if loaded?
|
337
338
|
exists?(record)
|
@@ -491,8 +492,8 @@ module ActiveRecord
|
|
491
492
|
def callbacks_for(callback_name)
|
492
493
|
full_callback_name = "#{callback_name}_for_#{@reflection.name}"
|
493
494
|
@owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
|
494
|
-
end
|
495
|
-
|
495
|
+
end
|
496
|
+
|
496
497
|
def ensure_owner_is_not_new
|
497
498
|
if @owner.new_record?
|
498
499
|
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
|
@@ -503,6 +504,18 @@ module ActiveRecord
|
|
503
504
|
args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
|
504
505
|
@target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
|
505
506
|
end
|
507
|
+
|
508
|
+
def include_in_memory?(record)
|
509
|
+
if @reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
510
|
+
@owner.send(proxy_reflection.through_reflection.name.to_sym).each do |source|
|
511
|
+
source_reflection_target = source.send(proxy_reflection.source_reflection.name)
|
512
|
+
return true if source_reflection_target.respond_to?(:include?) ? source_reflection_target.include?(record) : source_reflection_target == record
|
513
|
+
end
|
514
|
+
false
|
515
|
+
else
|
516
|
+
@target.include?(record)
|
517
|
+
end
|
518
|
+
end
|
506
519
|
end
|
507
520
|
end
|
508
521
|
end
|
@@ -274,7 +274,7 @@ module ActiveRecord
|
|
274
274
|
|
275
275
|
if Hash === options # legacy support, since this param was a string
|
276
276
|
index_type = options[:unique] ? "UNIQUE" : ""
|
277
|
-
index_name = options[:name]
|
277
|
+
index_name = options[:name].to_s if options[:name]
|
278
278
|
else
|
279
279
|
index_type = options
|
280
280
|
end
|
@@ -347,6 +347,7 @@ module ActiveRecord
|
|
347
347
|
# as there's no way to determine the correct answer in that case.
|
348
348
|
def index_exists?(table_name, index_name, default)
|
349
349
|
return default unless respond_to?(:indexes)
|
350
|
+
index_name = index_name.to_s
|
350
351
|
indexes(table_name).detect { |i| i.name == index_name }
|
351
352
|
end
|
352
353
|
|
@@ -120,7 +120,7 @@ module ActiveRecord
|
|
120
120
|
options ||= {}
|
121
121
|
[options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
|
122
122
|
extend Module.new(&block) if block_given?
|
123
|
-
unless Scope === proxy_scope
|
123
|
+
unless (Scope === proxy_scope || ActiveRecord::Associations::AssociationCollection === proxy_scope)
|
124
124
|
@current_scoped_methods_when_defined = proxy_scope.send(:current_scoped_methods)
|
125
125
|
end
|
126
126
|
@proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
|
@@ -286,9 +286,7 @@ module ActiveRecord
|
|
286
286
|
assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy])
|
287
287
|
|
288
288
|
elsif attributes['id']
|
289
|
-
|
290
|
-
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
291
|
-
self.send(association_name.to_s+'=', existing_record)
|
289
|
+
raise_nested_attributes_record_not_found(association_name, attributes['id'])
|
292
290
|
|
293
291
|
elsif !reject_new_record?(association_name, attributes)
|
294
292
|
method = "build_#{association_name}"
|
@@ -358,16 +356,11 @@ module ActiveRecord
|
|
358
356
|
unless reject_new_record?(association_name, attributes)
|
359
357
|
association.build(attributes.except(*UNASSIGNABLE_KEYS))
|
360
358
|
end
|
361
|
-
|
362
|
-
elsif existing_records.size == 0 # Existing record but not yet associated
|
363
|
-
existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
|
364
|
-
association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
|
365
|
-
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
366
|
-
|
367
359
|
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
|
368
360
|
association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
|
369
361
|
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
|
370
|
-
|
362
|
+
else
|
363
|
+
raise_nested_attributes_record_not_found(association_name, attributes['id'])
|
371
364
|
end
|
372
365
|
end
|
373
366
|
end
|
@@ -387,7 +380,7 @@ module ActiveRecord
|
|
387
380
|
ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
|
388
381
|
end
|
389
382
|
|
390
|
-
# Determines if a new record should be
|
383
|
+
# Determines if a new record should be build by checking for
|
391
384
|
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
|
392
385
|
# association and evaluates to +true+.
|
393
386
|
def reject_new_record?(association_name, attributes)
|
@@ -403,5 +396,9 @@ module ActiveRecord
|
|
403
396
|
end
|
404
397
|
end
|
405
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
|
406
403
|
end
|
407
404
|
end
|
@@ -363,7 +363,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
|
|
363
363
|
assert_equal post_tags, eager_post_tags
|
364
364
|
end
|
365
365
|
|
366
|
-
|
366
|
+
def test_eager_with_has_many_through_association_with_order
|
367
|
+
author_comments = Author.find(authors(:david).id).comments_desc
|
368
|
+
eager_author_comments = Author.find(authors(:david).id, :include => :comments_desc).comments_desc
|
369
|
+
assert_equal eager_author_comments, author_comments
|
370
|
+
end
|
371
|
+
|
367
372
|
def test_eager_with_has_many_through_join_model_with_include
|
368
373
|
author_comments = Author.find(authors(:david).id, :include => :comments_with_include).comments_with_include.to_a
|
369
374
|
assert_no_queries do
|
@@ -819,4 +819,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
|
819
819
|
assert_queries(0) { david.projects.columns; david.projects.columns }
|
820
820
|
end
|
821
821
|
|
822
|
+
def test_include_method_in_has_and_belongs_to_many_association_should_return_true_for_instance_added_with_build
|
823
|
+
project = Project.new
|
824
|
+
developer = project.developers.build
|
825
|
+
assert project.developers.include?(developer)
|
826
|
+
end
|
822
827
|
end
|
@@ -1221,5 +1221,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
|
|
1221
1221
|
end
|
1222
1222
|
EOF
|
1223
1223
|
end
|
1224
|
-
end
|
1225
1224
|
|
1225
|
+
def test_include_method_in_has_many_association_should_return_true_for_instance_added_with_build
|
1226
|
+
post = Post.new
|
1227
|
+
comment = post.comments.build
|
1228
|
+
assert post.comments.include?(comment)
|
1229
|
+
end
|
1230
|
+
end
|
@@ -343,4 +343,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
|
343
343
|
lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
|
344
344
|
].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
|
345
345
|
end
|
346
|
+
|
347
|
+
def test_include_method_in_association_through_should_return_true_for_instance_added_with_build
|
348
|
+
person = Person.new
|
349
|
+
reference = person.references.build
|
350
|
+
job = reference.build_job
|
351
|
+
assert person.jobs.include?(job)
|
352
|
+
end
|
353
|
+
|
354
|
+
def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
|
355
|
+
author = Author.new
|
356
|
+
post = author.posts.build
|
357
|
+
comment = post.comments.build
|
358
|
+
assert author.comments.include?(comment)
|
359
|
+
end
|
346
360
|
end
|
@@ -119,6 +119,13 @@ if ActiveRecord::Base.connection.supports_migrations?
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
def test_index_symbol_names
|
123
|
+
assert_nothing_raised { Person.connection.add_index :people, :primary_contact_id, :name => :symbol_index_name }
|
124
|
+
assert Person.connection.index_exists?(:people, :symbol_index_name, true)
|
125
|
+
assert_nothing_raised { Person.connection.remove_index :people, :name => :symbol_index_name }
|
126
|
+
assert_equal true, !Person.connection.index_exists?(:people, :symbol_index_name, false)
|
127
|
+
end
|
128
|
+
|
122
129
|
def test_add_index_length_limit
|
123
130
|
good_index_name = 'x' * Person.connection.index_name_length
|
124
131
|
too_long_index_name = good_index_name + 'x'
|
@@ -146,6 +146,12 @@ class NamedScopeTest < ActiveRecord::TestCase
|
|
146
146
|
assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
|
147
147
|
end
|
148
148
|
|
149
|
+
def test_nested_named_scopes_doesnt_duplicate_conditions_on_child_scopes
|
150
|
+
comments_scope = posts(:welcome).comments.send(:construct_sql)
|
151
|
+
named_scope_sql_conditions = posts(:welcome).comments.containing_the_letter_e.send(:current_scoped_methods)[:find][:conditions]
|
152
|
+
assert_no_match /#{comments_scope}.*#{comments_scope}/i, named_scope_sql_conditions
|
153
|
+
end
|
154
|
+
|
149
155
|
def test_has_many_through_associations_have_access_to_named_scopes
|
150
156
|
assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
|
151
157
|
assert !Comment.containing_the_letter_e.empty?
|
@@ -175,6 +175,12 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
|
|
175
175
|
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
|
176
176
|
end
|
177
177
|
|
178
|
+
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
|
179
|
+
assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Ship with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
|
180
|
+
@pirate.ship_attributes = { :id => 1234567890 }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
178
184
|
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
|
179
185
|
@pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
|
180
186
|
|
@@ -324,13 +330,10 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
|
|
324
330
|
assert_equal 'Arr', @ship.pirate.catchphrase
|
325
331
|
end
|
326
332
|
|
327
|
-
def
|
328
|
-
@ship.
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
assert_equal @ship.name, 'Nights Dirty Lightning'
|
333
|
-
assert_equal @pirate, @ship.pirate
|
333
|
+
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
|
334
|
+
assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find Pirate with ID=1234567890 for Ship with ID=#{@ship.id}" do
|
335
|
+
@ship.pirate_attributes = { :id => 1234567890 }
|
336
|
+
end
|
334
337
|
end
|
335
338
|
|
336
339
|
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
|
@@ -434,11 +437,6 @@ module NestedAttributesOnACollectionAssociationTests
|
|
434
437
|
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
|
435
438
|
end
|
436
439
|
|
437
|
-
def test_should_assign_existing_children_if_parent_is_new
|
438
|
-
@pirate = Pirate.new({:catchphrase => "Don' botharr talkin' like one, savvy?"}.merge(@alternate_params))
|
439
|
-
assert_equal ['Grace OMalley', 'Privateers Greed'], [@pirate.send(@association_name)[0].name, @pirate.send(@association_name)[1].name]
|
440
|
-
end
|
441
|
-
|
442
440
|
def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
|
443
441
|
@pirate.send(association_setter, @alternate_params[association_getter].values)
|
444
442
|
@pirate.save
|
@@ -508,8 +506,8 @@ module NestedAttributesOnACollectionAssociationTests
|
|
508
506
|
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
|
509
507
|
end
|
510
508
|
|
511
|
-
def
|
512
|
-
|
509
|
+
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
|
510
|
+
assert_raise_with_message ActiveRecord::RecordNotFound, "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}" do
|
513
511
|
@pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
|
514
512
|
end
|
515
513
|
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 2
|
7
8
|
- 3
|
8
|
-
-
|
9
|
-
version: 2.3.
|
9
|
+
- 10
|
10
|
+
version: 2.3.10
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- David Heinemeier Hansson
|
@@ -14,21 +15,23 @@ autorequire: active_record
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-10-15 00:00:00 +13:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: activesupport
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - "="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
27
30
|
segments:
|
28
31
|
- 2
|
29
32
|
- 3
|
30
|
-
-
|
31
|
-
version: 2.3.
|
33
|
+
- 10
|
34
|
+
version: 2.3.10
|
32
35
|
type: :runtime
|
33
36
|
version_requirements: *id001
|
34
37
|
description: Implements the ActiveRecord pattern (Fowler, PoEAA) for ORM. It ties database tables and classes together for business objects, like Customer or Subscription, that can find, save, and destroy themselves without resorting to manual SQL.
|
@@ -236,7 +239,9 @@ files:
|
|
236
239
|
- test/fixtures/edges.yml
|
237
240
|
- test/fixtures/entrants.yml
|
238
241
|
- test/fixtures/faces.yml
|
242
|
+
- test/fixtures/fixture_database.sqlite
|
239
243
|
- test/fixtures/fixture_database.sqlite3
|
244
|
+
- test/fixtures/fixture_database_2.sqlite
|
240
245
|
- test/fixtures/fixture_database_2.sqlite3
|
241
246
|
- test/fixtures/fk_test_has_fk.yml
|
242
247
|
- test/fixtures/fk_test_has_pk.yml
|
@@ -407,23 +412,27 @@ rdoc_options:
|
|
407
412
|
require_paths:
|
408
413
|
- lib
|
409
414
|
required_ruby_version: !ruby/object:Gem::Requirement
|
415
|
+
none: false
|
410
416
|
requirements:
|
411
417
|
- - ">="
|
412
418
|
- !ruby/object:Gem::Version
|
419
|
+
hash: 3
|
413
420
|
segments:
|
414
421
|
- 0
|
415
422
|
version: "0"
|
416
423
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
424
|
+
none: false
|
417
425
|
requirements:
|
418
426
|
- - ">="
|
419
427
|
- !ruby/object:Gem::Version
|
428
|
+
hash: 3
|
420
429
|
segments:
|
421
430
|
- 0
|
422
431
|
version: "0"
|
423
432
|
requirements: []
|
424
433
|
|
425
434
|
rubyforge_project: activerecord
|
426
|
-
rubygems_version: 1.3.
|
435
|
+
rubygems_version: 1.3.7
|
427
436
|
signing_key:
|
428
437
|
specification_version: 3
|
429
438
|
summary: Implements the ActiveRecord pattern for ORM.
|