mongo_record 0.4.3 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +10 -0
- data/lib/mongo_record/base.rb +97 -18
- data/mongo-activerecord-ruby.gemspec +1 -1
- data/tests/test_mongo.rb +147 -10
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -83,3 +83,13 @@ Jim Mulholland, jim at squeejee dot com
|
|
83
83
|
|
84
84
|
Clinton R. Nixon, crnixon at gmail dot com
|
85
85
|
* Ability to define and query indexes from models
|
86
|
+
|
87
|
+
Nate Wiger, http://github.com/nateware
|
88
|
+
* Optimization to first and last to close cursor and avoid expensive to_a
|
89
|
+
* Implemented Model.update_all leveraging Mongo collection.update
|
90
|
+
* Scoped dynamic finders to each instance, so rows with varying attributes work
|
91
|
+
* Added row.attributes helper to enable use of ActiveRecord::Callbacks if desired
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
data/lib/mongo_record/base.rb
CHANGED
@@ -18,6 +18,7 @@ require 'mongo/types/code'
|
|
18
18
|
require 'mongo/cursor'
|
19
19
|
require 'mongo_record/convert'
|
20
20
|
require 'mongo_record/sql'
|
21
|
+
#require 'active_support/core_ext' # symbolize_keys!
|
21
22
|
|
22
23
|
class String
|
23
24
|
# Convert this String to an ObjectID.
|
@@ -110,6 +111,7 @@ module MongoRecord
|
|
110
111
|
subclass.instance_variable_set("@field_names", []) # array of scalars names (symbols)
|
111
112
|
subclass.instance_variable_set("@subobjects", {}) # key = name (symbol), value = class
|
112
113
|
subclass.instance_variable_set("@arrays", {}) # key = name (symbol), value = class
|
114
|
+
subclass.field(:_id, :_ns)
|
113
115
|
end
|
114
116
|
|
115
117
|
# Call this method to set the Mongo collection name for this class.
|
@@ -117,7 +119,6 @@ module MongoRecord
|
|
117
119
|
# lower_case_with_underscores.
|
118
120
|
def collection_name(coll_name)
|
119
121
|
@coll_name = coll_name
|
120
|
-
field(:_id, :_ns)
|
121
122
|
end
|
122
123
|
|
123
124
|
# Creates one or more collection fields. Each field will be saved to
|
@@ -130,6 +131,7 @@ module MongoRecord
|
|
130
131
|
field = field.to_sym
|
131
132
|
unless @field_names.include?(field)
|
132
133
|
ivar_name = "@" + field.to_s
|
134
|
+
# this is better than lambda because it's only eval'ed once
|
133
135
|
define_method(field, lambda { instance_variable_get(ivar_name) })
|
134
136
|
define_method("#{field}=".to_sym, lambda { |val| instance_variable_set(ivar_name, val) })
|
135
137
|
define_method("#{field}?".to_sym, lambda {
|
@@ -162,7 +164,7 @@ module MongoRecord
|
|
162
164
|
end
|
163
165
|
|
164
166
|
fields = fields.map do |field|
|
165
|
-
field = field.
|
167
|
+
field = field.is_a?(Array) ? field : [field, :asc]
|
166
168
|
field[1] = (field[1] == :desc) ? Mongo::DESCENDING : Mongo::ASCENDING
|
167
169
|
field
|
168
170
|
end
|
@@ -395,7 +397,7 @@ module MongoRecord
|
|
395
397
|
end
|
396
398
|
|
397
399
|
def sum(column)
|
398
|
-
x =
|
400
|
+
x = all(:select => column)
|
399
401
|
x.map {|p1| p1[column.to_sym]}.compact.inject(0) { |s,v| s += v }
|
400
402
|
end
|
401
403
|
|
@@ -410,17 +412,24 @@ module MongoRecord
|
|
410
412
|
id.is_a?(Array) ? id.each { |oid| destroy(oid) } : find(id).destroy
|
411
413
|
end
|
412
414
|
|
413
|
-
#
|
414
|
-
|
415
|
-
|
416
|
-
|
415
|
+
# This updates all records matching the specified criteria. It leverages the
|
416
|
+
# db.update call from the Mongo core API to guarantee atomicity. You can
|
417
|
+
# specify either a hash for simplicity, or full Mongo API operators to the
|
418
|
+
# update part of the method call:
|
419
|
+
#
|
420
|
+
# Person.update_all({:name => 'Bob'}, {:name => 'Fred'})
|
421
|
+
# Person.update_all({'$set' => {:name => 'Bob'}, '$inc' => {:age => 1}}, {:name => 'Fred'})
|
422
|
+
def update_all(updates, conditions = nil, options = {})
|
423
|
+
all(:conditions => conditions).each do |row|
|
424
|
+
collection.update(criteria_from(conditions).merge(:_id => row.id.to_oid), update_fields_from(updates), options)
|
425
|
+
end
|
417
426
|
end
|
418
427
|
|
419
428
|
# Destroy all objects that match +conditions+. Warning: if
|
420
429
|
# +conditions+ is +nil+, all records in the collection will be
|
421
430
|
# destroyed.
|
422
431
|
def destroy_all(conditions = nil)
|
423
|
-
|
432
|
+
all(:conditions => conditions).each { |object| object.destroy }
|
424
433
|
end
|
425
434
|
|
426
435
|
# Deletes all records that match +condition+, which can be a
|
@@ -451,13 +460,13 @@ module MongoRecord
|
|
451
460
|
# Example of updating multiple records:
|
452
461
|
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
|
453
462
|
# Person.update(people.keys, people.values)
|
454
|
-
def update(id,
|
463
|
+
def update(id, attrib)
|
455
464
|
if id.is_a?(Array)
|
456
465
|
i = -1
|
457
|
-
id.collect { |id| i += 1; update(id,
|
466
|
+
id.collect { |id| i += 1; update(id, attrib[i]) }
|
458
467
|
else
|
459
468
|
object = find(id)
|
460
|
-
object.update_attributes(
|
469
|
+
object.update_attributes(attrib)
|
461
470
|
object
|
462
471
|
end
|
463
472
|
end
|
@@ -502,15 +511,21 @@ module MongoRecord
|
|
502
511
|
def find_initial(options)
|
503
512
|
options[:limit] = 1
|
504
513
|
options[:order] = 'created_at asc'
|
505
|
-
|
506
|
-
row.to_a[0]
|
514
|
+
find_one(options)
|
507
515
|
end
|
508
516
|
|
509
517
|
def find_last(options)
|
510
518
|
options[:limit] = 1
|
511
519
|
options[:order] = 'created_at desc'
|
512
|
-
|
513
|
-
|
520
|
+
find_one(options)
|
521
|
+
end
|
522
|
+
|
523
|
+
def find_one(options)
|
524
|
+
one = nil
|
525
|
+
cursor = find_every(options)
|
526
|
+
one = cursor.detect {|c| c}
|
527
|
+
cursor.close
|
528
|
+
one
|
514
529
|
end
|
515
530
|
|
516
531
|
def find_every(options)
|
@@ -523,7 +538,6 @@ module MongoRecord
|
|
523
538
|
find_options[:offset] = options[:offset].to_i if options[:offset]
|
524
539
|
find_options[:sort] = sort_by_from(options[:order]) if options[:order]
|
525
540
|
|
526
|
-
|
527
541
|
cursor = collection.find(criteria, find_options)
|
528
542
|
|
529
543
|
# Override cursor.next_object so it returns a new instance of this class
|
@@ -735,6 +749,26 @@ module MongoRecord
|
|
735
749
|
end
|
736
750
|
end
|
737
751
|
|
752
|
+
# Turns {:key => 'Value'} in update_all into the appropriate '$set' operator
|
753
|
+
def update_fields_from(arg)
|
754
|
+
raise "Update spec for #{self.name}.update_all must be a hash" unless arg.is_a?(Hash)
|
755
|
+
updates = {}
|
756
|
+
arg.each do |key,val|
|
757
|
+
case val
|
758
|
+
when Hash
|
759
|
+
# Assume something like $inc => {:num => 1}
|
760
|
+
updates[key] = val
|
761
|
+
when Array, Range
|
762
|
+
raise "Array/range not supported in value of update spec"
|
763
|
+
else
|
764
|
+
# Assume a simple value, so change to $set
|
765
|
+
updates['$set'] ||= {}
|
766
|
+
updates['$set'][key] = val
|
767
|
+
end
|
768
|
+
end
|
769
|
+
updates
|
770
|
+
end
|
771
|
+
|
738
772
|
# Overwrite the default class equality method to provide support for association proxies.
|
739
773
|
def ===(object)
|
740
774
|
object.is_a?(self)
|
@@ -767,9 +801,20 @@ module MongoRecord
|
|
767
801
|
iv = "@#{iv}"
|
768
802
|
instance_variable_set(iv, []) unless instance_variable_defined?(iv)
|
769
803
|
}
|
804
|
+
|
805
|
+
# Create accessors for any per-row dynamic fields we got from our schemaless store
|
806
|
+
self.instance_values.keys.each do |key|
|
807
|
+
next if respond_to?(key.to_sym) # exists
|
808
|
+
define_instance_accessors(key)
|
809
|
+
end
|
810
|
+
|
770
811
|
yield self if block_given?
|
771
812
|
end
|
772
813
|
|
814
|
+
def attributes
|
815
|
+
self.instance_values.inject({}){|h,iv| h[iv.first] = iv.last; h}
|
816
|
+
end
|
817
|
+
|
773
818
|
# Set the id of this object. Normally not called by user code.
|
774
819
|
def id=(val); @_id = (val == '' ? nil : val); end
|
775
820
|
|
@@ -882,13 +927,13 @@ module MongoRecord
|
|
882
927
|
end
|
883
928
|
|
884
929
|
def []=(attr_name, value)
|
885
|
-
|
930
|
+
define_instance_accessors(attr_name)
|
886
931
|
self.send(attr_name.to_s + '=', value)
|
887
932
|
end
|
888
933
|
|
889
934
|
def method_missing(sym, *args)
|
890
935
|
if self.instance_variables.include?("@#{sym}")
|
891
|
-
|
936
|
+
define_instance_accessors(sym)
|
892
937
|
return self.send(sym)
|
893
938
|
else
|
894
939
|
super
|
@@ -927,6 +972,9 @@ module MongoRecord
|
|
927
972
|
save!
|
928
973
|
end
|
929
974
|
|
975
|
+
def valid?; true; end
|
976
|
+
alias_method :respond_to_without_attributes?, :respond_to?
|
977
|
+
|
930
978
|
# Does nothing.
|
931
979
|
def attributes_from_column_definition; end
|
932
980
|
|
@@ -943,6 +991,15 @@ module MongoRecord
|
|
943
991
|
}
|
944
992
|
end
|
945
993
|
|
994
|
+
#--
|
995
|
+
# ================================================================
|
996
|
+
# "Dirty" attribute tracking, adapted from ActiveRecord. This is
|
997
|
+
# a big performance boost, plus it avoids issues if two people
|
998
|
+
# are updating a record concurrently.
|
999
|
+
# ================================================================
|
1000
|
+
#++
|
1001
|
+
|
1002
|
+
|
946
1003
|
private
|
947
1004
|
|
948
1005
|
def create_or_update
|
@@ -982,6 +1039,28 @@ module MongoRecord
|
|
982
1039
|
}
|
983
1040
|
end
|
984
1041
|
|
1042
|
+
# Per-object accessors, since row-to-row attributes can change
|
1043
|
+
# Use instance_eval so that they don't bleed over to other objects that lack the fields
|
1044
|
+
def define_instance_accessors(*fields)
|
1045
|
+
fields = Array(fields)
|
1046
|
+
fields.each do |field|
|
1047
|
+
ivar_name = "@" + field.to_s
|
1048
|
+
instance_eval <<-EndAccessors
|
1049
|
+
def #{field}
|
1050
|
+
instance_variable_get('#{ivar_name}')
|
1051
|
+
end
|
1052
|
+
def #{field}=(val)
|
1053
|
+
old = instance_variable_get('#{ivar_name}')
|
1054
|
+
instance_variable_set('#{ivar_name}', val)
|
1055
|
+
instance_variable_set('#{ivar_name}', val)
|
1056
|
+
end
|
1057
|
+
def #{field}?
|
1058
|
+
val = instance_variable_get('#{ivar_name}')
|
1059
|
+
val != nil && (!val.kind_of?(String) || val != '')
|
1060
|
+
end
|
1061
|
+
EndAccessors
|
1062
|
+
end
|
1063
|
+
end
|
985
1064
|
end
|
986
1065
|
|
987
1066
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'mongo_record'
|
3
|
-
s.version = '0.4.
|
3
|
+
s.version = '0.4.4'
|
4
4
|
s.platform = Gem::Platform::RUBY
|
5
5
|
s.summary = 'ActiveRecord-like models for the MongoDB'
|
6
6
|
s.description = 'MongoRecord is an ActiveRecord-like framework for MongoDB. For more information about Mongo, see http://www.mongodb.org.'
|
data/tests/test_mongo.rb
CHANGED
@@ -59,10 +59,10 @@ class MongoTest < Test::Unit::TestCase
|
|
59
59
|
super
|
60
60
|
MongoRecord::Base.connection = @@db
|
61
61
|
|
62
|
-
@@students.
|
63
|
-
@@courses.
|
64
|
-
@@tracks.
|
65
|
-
@@playlists.
|
62
|
+
@@students.remove
|
63
|
+
@@courses.remove
|
64
|
+
@@tracks.remove
|
65
|
+
@@playlists.remove
|
66
66
|
|
67
67
|
# Manually insert data without using MongoRecord::Base
|
68
68
|
@@tracks.insert({:_id => Mongo::ObjectID.new, :artist => 'Thomas Dolby', :album => 'Aliens Ate My Buick', :song => 'The Ability to Swing'})
|
@@ -85,10 +85,10 @@ class MongoTest < Test::Unit::TestCase
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def teardown
|
88
|
-
@@students.
|
89
|
-
@@courses.
|
90
|
-
@@tracks.
|
91
|
-
@@playlists.
|
88
|
+
@@students.remove
|
89
|
+
@@courses.remove
|
90
|
+
@@tracks.remove
|
91
|
+
@@playlists.remove
|
92
92
|
super
|
93
93
|
end
|
94
94
|
|
@@ -98,6 +98,11 @@ class MongoTest < Test::Unit::TestCase
|
|
98
98
|
assert t.instance_variable_defined?("@#{iv}")
|
99
99
|
}
|
100
100
|
end
|
101
|
+
|
102
|
+
def test_new_record_set_correctly
|
103
|
+
t = Track.new(:_id => 12345, :artist => 'Alice In Chains')
|
104
|
+
assert_equal true, t.new_record?
|
105
|
+
end
|
101
106
|
|
102
107
|
def test_method_generation
|
103
108
|
x = Track.new({:artist => 1, :album => 2})
|
@@ -124,6 +129,94 @@ class MongoTest < Test::Unit::TestCase
|
|
124
129
|
assert_nil(x.track)
|
125
130
|
end
|
126
131
|
|
132
|
+
def test_dynamic_methods_in_new
|
133
|
+
x = Track.new({:foo => 1, :bar => 2})
|
134
|
+
y = Track.new({:artist => 3, :song => 4})
|
135
|
+
|
136
|
+
assert x.respond_to?(:_id)
|
137
|
+
assert x.respond_to?(:artist)
|
138
|
+
assert x.respond_to?(:album)
|
139
|
+
assert x.respond_to?(:song)
|
140
|
+
assert x.respond_to?(:track)
|
141
|
+
assert x.respond_to?(:_id=)
|
142
|
+
assert x.respond_to?(:artist=)
|
143
|
+
assert x.respond_to?(:album=)
|
144
|
+
assert x.respond_to?(:song=)
|
145
|
+
assert x.respond_to?(:track=)
|
146
|
+
assert x.respond_to?(:_id?)
|
147
|
+
assert x.respond_to?(:artist?)
|
148
|
+
assert x.respond_to?(:album?)
|
149
|
+
assert x.respond_to?(:song?)
|
150
|
+
assert x.respond_to?(:track?)
|
151
|
+
|
152
|
+
# dynamic fields
|
153
|
+
assert x.respond_to?(:foo)
|
154
|
+
assert x.respond_to?(:bar)
|
155
|
+
assert x.respond_to?(:foo=)
|
156
|
+
assert x.respond_to?(:bar=)
|
157
|
+
assert x.respond_to?(:foo?)
|
158
|
+
assert x.respond_to?(:bar?)
|
159
|
+
|
160
|
+
# make sure accessors only per-object
|
161
|
+
assert !y.respond_to?(:foo)
|
162
|
+
assert !y.respond_to?(:bar)
|
163
|
+
assert !y.respond_to?(:foo=)
|
164
|
+
assert !y.respond_to?(:bar=)
|
165
|
+
assert !y.respond_to?(:foo?)
|
166
|
+
assert !y.respond_to?(:bar?)
|
167
|
+
|
168
|
+
assert_equal(1, x.foo)
|
169
|
+
assert_equal(2, x.bar)
|
170
|
+
assert_nil(x.song)
|
171
|
+
assert_nil(x.track)
|
172
|
+
assert_equal(3, y.artist)
|
173
|
+
assert_equal(4, y.song)
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_dynamic_methods_in_find
|
177
|
+
@@tracks.insert({:_id => 909, :artist => 'Faith No More', :album => 'Album Of The Year', :song => 'Stripsearch', :track => 2,
|
178
|
+
:vocals => 'Mike Patton', :drums => 'Mike Bordin', :producers => ['Roli Mosimann', 'Billy Gould']})
|
179
|
+
x = Track.find_by_id(909)
|
180
|
+
|
181
|
+
# defined
|
182
|
+
assert x.respond_to?(:_id)
|
183
|
+
assert x.respond_to?(:artist)
|
184
|
+
assert x.respond_to?(:album)
|
185
|
+
assert x.respond_to?(:song)
|
186
|
+
assert x.respond_to?(:track)
|
187
|
+
assert x.respond_to?(:_id=)
|
188
|
+
assert x.respond_to?(:artist=)
|
189
|
+
assert x.respond_to?(:album=)
|
190
|
+
assert x.respond_to?(:song=)
|
191
|
+
assert x.respond_to?(:track=)
|
192
|
+
assert x.respond_to?(:_id?)
|
193
|
+
assert x.respond_to?(:artist?)
|
194
|
+
assert x.respond_to?(:album?)
|
195
|
+
assert x.respond_to?(:song?)
|
196
|
+
assert x.respond_to?(:track?)
|
197
|
+
|
198
|
+
# dynamic fields
|
199
|
+
assert x.respond_to?(:vocals)
|
200
|
+
assert x.respond_to?(:drums)
|
201
|
+
assert x.respond_to?(:producers)
|
202
|
+
assert x.respond_to?(:vocals=)
|
203
|
+
assert x.respond_to?(:drums=)
|
204
|
+
assert x.respond_to?(:producers=)
|
205
|
+
assert x.respond_to?(:vocals?)
|
206
|
+
assert x.respond_to?(:drums?)
|
207
|
+
assert x.respond_to?(:producers?)
|
208
|
+
|
209
|
+
assert_equal 'Faith No More', x.artist
|
210
|
+
assert_equal 'Album Of The Year', x.album
|
211
|
+
assert_equal 'Stripsearch', x.song
|
212
|
+
assert_equal 2, x.track
|
213
|
+
assert_equal 'Mike Patton', x.vocals
|
214
|
+
assert_equal 'Mike Bordin', x.drums
|
215
|
+
assert_equal ['Roli Mosimann', 'Billy Gould'], x.producers
|
216
|
+
|
217
|
+
x.destroy
|
218
|
+
end
|
219
|
+
|
127
220
|
def test_initialize_block
|
128
221
|
track = Track.new { |t|
|
129
222
|
t.artist = "Me'Shell Ndegeocello"
|
@@ -293,6 +386,27 @@ class MongoTest < Test::Unit::TestCase
|
|
293
386
|
assert_no_match(/song: The Mayor Of Simpleton/, Track.find(:all).inject('') { |str, t| str + t.to_s })
|
294
387
|
end
|
295
388
|
|
389
|
+
def test_update_all
|
390
|
+
Track.update_all({:track => 919}, {:artist => 'XTC'})
|
391
|
+
Track.all.each{|r| assert_equal(919, r.track) if r.artist == 'XTC' }
|
392
|
+
|
393
|
+
# Should fail (can't $inc/$set) - remove this test once Mongo 1.2 is out
|
394
|
+
error = nil
|
395
|
+
begin
|
396
|
+
Track.update_all({:song => 'Just Drums'}, {}, :safe => true)
|
397
|
+
rescue Mongo::OperationFailure => error
|
398
|
+
end
|
399
|
+
assert_instance_of Mongo::OperationFailure, error
|
400
|
+
|
401
|
+
@@tracks.drop_index 'song_-1' # otherwise update_all $set fails
|
402
|
+
Track.update_all({:song => 'Just Drums'}, {}, :safe => true)
|
403
|
+
|
404
|
+
assert_no_match(/song: Budapest by Blimp/, Track.all.inject('') { |str, t| str + t.to_s })
|
405
|
+
|
406
|
+
assert_equal 6, Track.count
|
407
|
+
Track.index [:song, :desc], true # reindex
|
408
|
+
end
|
409
|
+
|
296
410
|
def test_delete_all
|
297
411
|
Track.delete_all({:artist => 'XTC'})
|
298
412
|
assert_no_match(/artist: XTC/, Track.find(:all).inject('') { |str, t| str + t.to_s })
|
@@ -635,7 +749,7 @@ class MongoTest < Test::Unit::TestCase
|
|
635
749
|
# Make sure collection exists
|
636
750
|
coll = alt_db.collection('students')
|
637
751
|
coll.insert('name' => 'foo')
|
638
|
-
coll.
|
752
|
+
coll.remove
|
639
753
|
|
640
754
|
assert_equal 0, coll.count()
|
641
755
|
s = Student.new(:name => 'Spongebob Squarepants', :address => @spongebob_addr)
|
@@ -730,7 +844,30 @@ class MongoTest < Test::Unit::TestCase
|
|
730
844
|
opts = {:artist => 'The Outfield', :album => 'Play Deep', :song => 'Your Love', :year => 1986}
|
731
845
|
playlist = Playlist.new
|
732
846
|
playlist.update_attributes(opts)
|
733
|
-
|
847
|
+
|
848
|
+
# We *want* the following to fail, because otherwise MongoRecord is buggy in the following
|
849
|
+
# situation:
|
850
|
+
#
|
851
|
+
# Rails/Sinatra/etc server instance #1 does: playlist.custom_field = 'foo'
|
852
|
+
# Rails/Sinatra/etc instance #2 attempts to do: Playlist.find_by_custom_field
|
853
|
+
#
|
854
|
+
# This will fail because, in previous versions of MongoRecord, the instance would callback
|
855
|
+
# into class.field(), the changing the class definition, then use this modified definition to
|
856
|
+
# determine whether dynamic finders work.
|
857
|
+
#
|
858
|
+
# The biggest issue is you'll never catch this in dev, since everything is a single instance
|
859
|
+
# in memory. It will manifest as mysterious "undefined method" production bugs. As such, we *must*
|
860
|
+
# restrict dynamic accessors to only modifying the instance for each row, or else they corrupt
|
861
|
+
# the class. This means find_by_whatever only works for fields defined via fields()
|
862
|
+
error = nil
|
863
|
+
begin
|
864
|
+
p = Playlist.find_by_artist("The Outfield")
|
865
|
+
rescue NoMethodError => error
|
866
|
+
end
|
867
|
+
assert_instance_of NoMethodError, error
|
868
|
+
|
869
|
+
# This should work though
|
870
|
+
p = Playlist.first(:conditions => {:artist => 'The Outfield'})
|
734
871
|
assert_equal(p.year, 1986)
|
735
872
|
end
|
736
873
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Menard
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-
|
13
|
+
date: 2009-12-29 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|