dm-mongo-adapter 0.2.0.pre3 → 0.6.0

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.
Files changed (64) hide show
  1. data/Gemfile +27 -0
  2. data/README.rdoc +16 -35
  3. data/Rakefile +6 -16
  4. data/VERSION.yml +2 -2
  5. data/dm-mongo-adapter.gemspec +100 -121
  6. data/lib/mongo_adapter.rb +19 -62
  7. data/lib/mongo_adapter/adapter.rb +49 -36
  8. data/lib/mongo_adapter/conditions.rb +0 -5
  9. data/lib/mongo_adapter/migrations.rb +17 -23
  10. data/lib/mongo_adapter/model.rb +3 -74
  11. data/lib/mongo_adapter/modifier.rb +1 -1
  12. data/lib/mongo_adapter/property/array.rb +10 -0
  13. data/lib/mongo_adapter/property/db_ref.rb +17 -0
  14. data/lib/mongo_adapter/property/hash.rb +27 -0
  15. data/lib/mongo_adapter/property/object_id.rb +47 -0
  16. data/lib/mongo_adapter/query.rb +42 -45
  17. data/lib/mongo_adapter/rails/storage.rb +15 -0
  18. data/lib/mongo_adapter/resource.rb +0 -130
  19. data/lib/mongo_adapter/support/class.rb +11 -0
  20. data/lib/mongo_adapter/support/date.rb +11 -0
  21. data/lib/mongo_adapter/support/date_time.rb +12 -0
  22. data/lib/mongo_adapter/support/object.rb +9 -0
  23. data/spec/legacy/adapter_spec.rb +9 -6
  24. data/spec/legacy/associations_spec.rb +37 -29
  25. data/spec/legacy/property_spec.rb +4 -3
  26. data/spec/legacy/sti_spec.rb +1 -2
  27. data/spec/lib/cleanup_models.rb +0 -1
  28. data/spec/public/aggregates_spec.rb +171 -0
  29. data/spec/public/model_spec.rb +8 -22
  30. data/spec/public/modifier_spec.rb +109 -0
  31. data/spec/public/properties/db_ref_spec.rb +17 -0
  32. data/spec/public/properties/object_id_spec.rb +15 -0
  33. data/spec/public/resource_spec.rb +2 -383
  34. data/spec/public/shared/object_id_shared_spec.rb +16 -39
  35. data/spec/spec_helper.rb +0 -4
  36. metadata +87 -117
  37. data/.gitignore +0 -9
  38. data/lib/mongo_adapter/embedded_model.rb +0 -187
  39. data/lib/mongo_adapter/embedded_resource.rb +0 -134
  40. data/lib/mongo_adapter/embedments/one_to_many.rb +0 -144
  41. data/lib/mongo_adapter/embedments/one_to_one.rb +0 -57
  42. data/lib/mongo_adapter/embedments/relationship.rb +0 -258
  43. data/lib/mongo_adapter/model/embedment.rb +0 -215
  44. data/lib/mongo_adapter/types/date.rb +0 -24
  45. data/lib/mongo_adapter/types/date_time.rb +0 -28
  46. data/lib/mongo_adapter/types/db_ref.rb +0 -65
  47. data/lib/mongo_adapter/types/discriminator.rb +0 -32
  48. data/lib/mongo_adapter/types/object_id.rb +0 -72
  49. data/lib/mongo_adapter/types/objects.rb +0 -31
  50. data/spec/legacy/embedded_resource_spec.rb +0 -157
  51. data/spec/legacy/embedments_spec.rb +0 -177
  52. data/spec/legacy/modifier_spec.rb +0 -81
  53. data/spec/public/embedded_collection_spec.rb +0 -61
  54. data/spec/public/embedded_resource_spec.rb +0 -220
  55. data/spec/public/model/embedment_spec.rb +0 -186
  56. data/spec/public/shared/model_embedments_spec.rb +0 -338
  57. data/spec/public/types/df_ref_spec.rb +0 -6
  58. data/spec/public/types/discriminator_spec.rb +0 -118
  59. data/spec/public/types/embedded_array_spec.rb +0 -55
  60. data/spec/public/types/embedded_hash_spec.rb +0 -83
  61. data/spec/public/types/object_id_spec.rb +0 -6
  62. data/spec/semipublic/embedded_model_spec.rb +0 -43
  63. data/spec/semipublic/model/embedment_spec.rb +0 -42
  64. data/spec/semipublic/resource_spec.rb +0 -70
@@ -0,0 +1,15 @@
1
+ module Rails
2
+ module DataMapper
3
+ class Storage
4
+ class Mongo < Storage
5
+ def _create
6
+ # noop
7
+ end
8
+
9
+ def _drop
10
+ # noop
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,141 +5,11 @@ module DataMapper
5
5
  module Resource
6
6
  def self.included(model)
7
7
  model.send(:include, DataMapper::Resource)
8
- model.send(:include, ResourceMethods)
9
8
  model.send(:include, Modifier)
10
9
 
11
10
  # Needs to be after the inclusion of DM::Resource so as to overwrite
12
11
  # methods added by DM::Model.
13
12
  model.extend(Model)
14
- end
15
-
16
- module ResourceMethods
17
- # monkey patching based on this: http://github.com/datamapper/dm-core/commit/3332db6c25ab9cea9ba58ce62a9ad3038303baa1
18
- # TODO: remove once dm-core 0.10.3 is released
19
- def eager_load(properties)
20
- unless properties.empty? || key.nil? || collection.nil?
21
- collection.reload(:fields => properties)
22
- end
23
-
24
- self
25
- end
26
-
27
- # Assign values to multiple attributes in one call (mass assignment)
28
- #
29
- # Overrides attributes= in dm-core so as to permit assignments to
30
- # embedments.
31
- #
32
- # @param [Hash] attributes
33
- # names and values of attributes to assign
34
- #
35
- # @return [Hash]
36
- # names and values of attributes assigned
37
- #
38
- # @api public
39
- def attributes=(attributes)
40
- attributes.each do |name, value|
41
- name.set(self, value) if name.kind_of?(Embedments::Relationship)
42
- end
43
-
44
- super(attributes)
45
- end
46
-
47
- # Checks if the resource, or embedded documents, have unsaved changes
48
- #
49
- # @return [Boolean]
50
- # True if resource may be persisted
51
- #
52
- # @overrides DataMapper::Resource#dirty?
53
- #
54
- # @api public
55
- def dirty?
56
- super || run_once(true) { dirty_embedments? }
57
- end
58
-
59
- # Checks if any embedded documents have unsaved changes
60
- #
61
- # @return [Boolean]
62
- # True if any embedded documents can be persisted
63
- #
64
- # @api private
65
- def dirty_embedments?
66
- embedments.values.any? do |embedment|
67
- embedment.loaded?(self) && case embedment
68
- when Embedments::OneToOne::Relationship then embedment.get!(self).dirty?
69
- when Embedments::OneToMany::Relationship then embedment.get!(self).any? { |r| r.dirty? }
70
- else false
71
- end
72
- end
73
- end
74
-
75
- # Hash of attributes that have unsaved changes
76
- #
77
- # @return [Hash]
78
- # attributes that have unsaved changes
79
- #
80
- # @overrides DataMapper::Resource#dirty_attributes
81
- #
82
- # @api semipublic
83
- def dirty_attributes
84
- embedded_attributes = {}
85
-
86
- each_embedment do |name, target|
87
- case (embedment = embedments[name])
88
- when Embedments::OneToMany::Relationship
89
- target.each do |resource|
90
- if resource.dirty?
91
- embedded_attributes[embedment] ||= []
92
- embedded_attributes[embedment] << resource.dirty_attributes
93
- end
94
- end
95
- when Embedments::OneToOne::Relationship
96
- # Relationship target is a single resource.
97
- if target.dirty?
98
- embedded_attributes[embedment] = target.dirty_attributes
99
- end
100
- end
101
- end
102
-
103
- super.merge(embedded_attributes)
104
- end
105
-
106
- # Saves the resource and it's embedments
107
- #
108
- # @return [Boolean]
109
- # True if the resource was successfully saved
110
- #
111
- # @overrides DataMapper::Resource#save_self
112
- #
113
- # @api semipublic
114
- def save_self(safe = true)
115
- super && embedments.values.each do |e|
116
- e.loaded?(self) && Array(e.get!(self)).each { |r| r.original_attributes.clear }
117
- end
118
- end
119
-
120
- private
121
-
122
- # The embedments (relationships to embedded objects) on this model
123
- #
124
- # @return [Hash<Symbol,Embedments::Relationship>]
125
- #
126
- # @api private
127
- def embedments
128
- model.embedments
129
- end
130
-
131
- # Iterates through each loaded embedment, yielding the name and value
132
- #
133
- # @yieldparam [Symbol]
134
- # The name of the embedment
135
- # @yieldparam [Mongo::Collection]
136
- # The embedded resource, or collection of embedded resources
137
- #
138
- # @api private
139
- def each_embedment
140
- embedments.each { |name, embedment|
141
- embedment.loaded?(self) && yield(name, embedment.get!(self)) }
142
- end
143
13
  end # ResourceMethods
144
14
 
145
15
  end # Resource
@@ -0,0 +1,11 @@
1
+ class Class
2
+ # @api public
3
+ def self.to_mongo(value)
4
+ value.name
5
+ end
6
+
7
+ # @api public
8
+ def self.from_mongo(value)
9
+ DataMapper::Inflector.classify(value)
10
+ end
11
+ end # Class
@@ -0,0 +1,11 @@
1
+ class Date
2
+ # @api public
3
+ def self.to_mongo(value)
4
+ Time.utc(value.year, value.month, value.day)
5
+ end
6
+
7
+ # @api public
8
+ def self.from_mongo(value)
9
+ ::Date.new(value.year, value.month, value.day)
10
+ end
11
+ end # Date
@@ -0,0 +1,12 @@
1
+ class DateTime
2
+ # @api public
3
+ def self.to_mongo(value)
4
+ utc = value.new_offset(0)
5
+ ::Time.utc(utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec)
6
+ end
7
+
8
+ # @api public
9
+ def self.from_mongo(value)
10
+ value.to_datetime
11
+ end
12
+ end # DateTime
@@ -0,0 +1,9 @@
1
+ class Object
2
+ def self.to_mongo(value)
3
+ value
4
+ end
5
+
6
+ def self.from_mongo(value)
7
+ value
8
+ end
9
+ end
@@ -9,7 +9,7 @@ describe DataMapper::Adapters::MongoAdapter do
9
9
  class ::Heffalump
10
10
  include DataMapper::Mongo::Resource
11
11
 
12
- property :id, ObjectID
12
+ property :id, ObjectId
13
13
  property :color, String
14
14
  property :num_spots, Integer
15
15
  property :striped, Boolean
@@ -113,9 +113,9 @@ describe DataMapper::Adapters::MongoAdapter do
113
113
  class ::Zoo
114
114
  include DataMapper::Mongo::Resource
115
115
 
116
- property :id, ObjectID
117
- property :animals, EmbeddedArray
118
- property :address, EmbeddedHash
116
+ property :id, ObjectId
117
+ property :animals, Array
118
+ property :address, Hash
119
119
  end
120
120
  end
121
121
 
@@ -134,8 +134,11 @@ describe DataMapper::Adapters::MongoAdapter do
134
134
  Zoo.create(:animals => penguins)
135
135
 
136
136
  zoo = Zoo.first(:animals => penguins)
137
- zoo.should_not be_nil
138
- zoo.animals.should eql(penguins)
137
+
138
+ pending "this is currently not supported by dm-core" do
139
+ zoo.should_not be_nil
140
+ zoo.animals.should eql(penguins)
141
+ end
139
142
  end
140
143
  end
141
144
 
@@ -7,7 +7,7 @@ describe "associations" do
7
7
  class ::User
8
8
  include DataMapper::Mongo::Resource
9
9
 
10
- property :id, ObjectID
10
+ property :id, ObjectId
11
11
  property :group_id, DBRef
12
12
  property :name, String
13
13
  property :age, Integer
@@ -16,14 +16,14 @@ describe "associations" do
16
16
  class ::Group
17
17
  include DataMapper::Mongo::Resource
18
18
 
19
- property :id, ObjectID
19
+ property :id, ObjectId
20
20
  property :name, String
21
21
  end
22
22
 
23
23
  class ::Friend
24
24
  include DataMapper::Mongo::Resource
25
25
 
26
- property :id, ObjectID
26
+ property :id, ObjectId
27
27
  property :name, String
28
28
  end
29
29
 
@@ -72,38 +72,46 @@ describe "associations" do
72
72
  end
73
73
 
74
74
  describe "has many" do
75
- before :each do
76
- @john = User.create(:name => 'john', :age => 101)
77
- @jane = User.create(:name => 'jane', :age => 102)
78
-
79
- @group = Group.create(:name => 'dm hackers')
80
-
81
- [@john, @jane].each { |user| user.update(:group_id => @group.id) }
82
- end
75
+ # before :each do
76
+ # @john = User.create(:name => 'john', :age => 101)
77
+ # @jane = User.create(:name => 'jane', :age => 102)
78
+ #
79
+ # @group = Group.create(:name => 'dm hackers')
80
+ #
81
+ # [@john, @jane].each { |user| user.update(:group_id => @group.id) }
82
+ # end
83
83
 
84
84
  # @done
85
85
  it "should get children" do
86
- @group.users.size.should eql(2)
86
+ pending "bug in edge dm-core causes an infinite loop here" do
87
+ @group.users.size.should eql(2)
88
+ end
87
89
  end
88
90
 
89
91
  it "should add new children with <<" do
90
- user = User.new(:name => 'kyle')
91
- @group.users << user
92
- user.group_id.should eql(@group.id)
93
- @group.users.size.should eql(3)
92
+ pending "bug in edge dm-core causes an infinite loop here" do
93
+ user = User.new(:name => 'kyle')
94
+ @group.users << user
95
+ user.group_id.should eql(@group.id)
96
+ @group.users.size.should eql(3)
97
+ end
94
98
  end
95
99
 
96
100
  # @done
97
101
  it "should replace children" do
98
- user = User.create(:name => 'stan')
99
- @group.users = [user]
100
- @group.users.size.should eql(1)
101
- @group.users.first.should eql(user)
102
+ pending "bug in edge dm-core causes an infinite loop here" do
103
+ user = User.create(:name => 'stan')
104
+ @group.users = [user]
105
+ @group.users.size.should eql(1)
106
+ @group.users.first.should eql(user)
107
+ end
102
108
  end
103
109
 
104
110
  it "should fetch children matching conditions" do
105
- users = @group.users.all(:name => 'john')
106
- users.size.should eql(1)
111
+ pending "bug in edge dm-core causes an infinite loop here" do
112
+ users = @group.users.all(:name => 'john')
113
+ users.size.should eql(1)
114
+ end
107
115
  end
108
116
  end
109
117
 
@@ -114,13 +122,13 @@ describe "associations" do
114
122
  @user1 = User.new
115
123
  @user2 = User.new
116
124
  @group = Group.new(:users =>
117
- [
118
- {:friends =>
119
- [{:name => "blah"}, {:name => "blah2"}]
120
- },
121
- {:friends =>
122
- [{:name => "blah3"},{:name => "blah4"}]
123
- }])
125
+ [
126
+ {:friends =>
127
+ [{:name => "blah"}, {:name => "blah2"}]
128
+ },
129
+ {:friends =>
130
+ [{:name => "blah3"},{:name => "blah4"}]
131
+ }])
124
132
  end
125
133
 
126
134
  # @done
@@ -7,7 +7,7 @@ describe "Property" do
7
7
  class ::User
8
8
  include DataMapper::Mongo::Resource
9
9
 
10
- property :id, ObjectID
10
+ property :id, ObjectId
11
11
  property :date_time_field, DateTime
12
12
  property :date_field, Date
13
13
  property :type, Discriminator
@@ -32,7 +32,8 @@ describe "Property" do
32
32
  user = User.get(_id)
33
33
 
34
34
  user.date_time_field.class.should be(DateTime)
35
- user.date_time_field.to_time.to_i.should == dt_now.to_time.to_i
35
+
36
+ Time.parse(user.date_time_field.to_s).to_i.should == Time.parse(dt_now.to_s).to_i
36
37
  end
37
38
  end
38
39
 
@@ -43,7 +44,7 @@ describe "Property" do
43
44
  _id = $db.collection('users').insert(:type => 'User', :date_field => Time.parse(today.to_s))
44
45
 
45
46
  user = User.get(_id)
46
-
47
+
47
48
  user.date_field.class.should be(Date)
48
49
  Time.parse(user.date_field.to_s).should == Time.parse(today.to_s)
49
50
  end
@@ -6,7 +6,7 @@ describe "Single Table Inheritance" do
6
6
  class ::Person
7
7
  include DataMapper::Mongo::Resource
8
8
 
9
- property :id, ObjectID
9
+ property :id, ObjectId
10
10
  property :name, String
11
11
  property :job, String
12
12
  property :type, Discriminator
@@ -24,7 +24,6 @@ describe "Single Table Inheritance" do
24
24
  it "should have a type property that reflects the class" do
25
25
  [Person, Male, Father, Son].each_with_index do |model, i|
26
26
  object = model.create!(:name => "#{model} #{i}")
27
- object.reload
28
27
  object.type.should == model
29
28
  end
30
29
  end
@@ -19,7 +19,6 @@ module DataMapper::Mongo::Spec
19
19
  end
20
20
 
21
21
  DataMapper::Model.descendants.delete(model)
22
- DataMapper::Mongo::EmbeddedModel.descendants.delete(model)
23
22
 
24
23
  Object.send(:remove_const, sym)
25
24
  end
@@ -0,0 +1,171 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe DataMapper::Mongo::Model do
4
+ before(:all) do
5
+ class ::Student
6
+ include DataMapper::Mongo::Resource
7
+
8
+ property :id, ObjectId
9
+ property :name, String
10
+ property :school, String
11
+ property :score, Float
12
+ end
13
+
14
+ Student.all.destroy!
15
+
16
+ @student_one = Student.create(:school => 'School 1', :name => 'One', :score => 3.0)
17
+ @student_two = Student.create(:school => 'School 2', :name => 'Two', :score => 3.5)
18
+ @student_three = Student.create(:school => 'School 2', :name => 'Three', :score => 4.5)
19
+ end
20
+
21
+ describe "#count" do
22
+ describe 'with no query' do
23
+ it 'should return number of all resources' do
24
+ Student.count.should == 3
25
+ end
26
+ end
27
+
28
+ describe 'with a query' do
29
+ it 'should return number of resources matching conditions' do
30
+ Student.count(:name => /one|two/i).should == 2
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "#aggregate" do
36
+ describe "without operators" do
37
+ describe "without conditions" do
38
+ it "should return array of hashes based on all records" do
39
+ result = Student.aggregate(:school, :score).to_a
40
+
41
+ result.should == [
42
+ { :school => "School 1", :score => 3.0 },
43
+ { :school => "School 2", :score => 3.5 },
44
+ { :school => "School 2", :score => 4.5 }]
45
+ end
46
+ end
47
+
48
+ describe "with conditions" do
49
+ it "should return array of hashes based on records that match conditions" do
50
+ result = Student.aggregate(:school, :score, :score.gt => 3.0)
51
+
52
+ result.should == [
53
+ { :school => "School 2", :score => 3.5 },
54
+ { :school => "School 2", :score => 4.5 }]
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "count operator" do
60
+ describe "without conditions" do
61
+ it "should get correct results based on all records" do
62
+ result = Student.aggregate(:school, :score.count)
63
+
64
+ result.size.should == 2
65
+
66
+ school_1, school_2 = result
67
+
68
+ school_1[:school].should == 'School 1'
69
+ school_2[:school].should == 'School 2'
70
+
71
+ school_1[:score].should == 1
72
+ school_2[:score].should == 2
73
+ end
74
+ end
75
+
76
+ describe "with conditions" do
77
+ it "should get correct results based on records that match conditions" do
78
+ result = Student.aggregate(:school, :score.count, :name => /two|three/i)
79
+
80
+ result.size.should == 1
81
+ result.first[:score].should == 2
82
+ result.first[:school].should == 'School 2'
83
+ end
84
+ end
85
+ end
86
+
87
+ #
88
+ # avg
89
+ #
90
+ # TODO: add spec for #avg with conditions
91
+
92
+ describe "avg operator" do
93
+ describe 'without conditions' do
94
+ it 'should return an avarage value of the given field' do
95
+ result = Student.aggregate(:school, :score.avg)
96
+
97
+ school_1, school_2 = result
98
+
99
+ school_1[:school].should == 'School 1'
100
+ school_2[:school].should == 'School 2'
101
+
102
+ school_1[:score].should == 3.0
103
+ school_2[:score].should == 4.0
104
+ end
105
+ end
106
+ end
107
+
108
+ #
109
+ # min
110
+ #
111
+ # TODO: add spec for #min with conditions
112
+
113
+ describe "min operator" do
114
+ describe 'without conditions' do
115
+ it 'should return the minimum value of the given field' do
116
+ result = Student.aggregate(:school, :score.min)
117
+
118
+ school_1, school_2 = result
119
+
120
+ school_1[:school].should == 'School 1'
121
+ school_2[:school].should == 'School 2'
122
+
123
+ school_1[:score].should == 3.0
124
+ school_2[:score].should == 3.5
125
+ end
126
+ end
127
+ end
128
+
129
+ #
130
+ # max
131
+ #
132
+ # TODO: add spec for #max with conditions
133
+
134
+ describe "max operator" do
135
+ describe 'without conditions' do
136
+ it 'should return the maximum value of the given field' do
137
+ result = Student.aggregate(:school, :score.max)
138
+
139
+ school_1, school_2 = result
140
+
141
+ school_1[:school].should == 'School 1'
142
+ school_2[:school].should == 'School 2'
143
+
144
+ school_1[:score].should == 3.0
145
+ school_2[:score].should == 4.5
146
+ end
147
+ end
148
+ end
149
+
150
+ #
151
+ # max
152
+ #
153
+ # TODO: add spec for #sum with conditions
154
+
155
+ describe "sum operator" do
156
+ describe 'without conditions' do
157
+ it 'should return the maximum value of the given field' do
158
+ result = Student.aggregate(:school, :score.sum)
159
+
160
+ school_1, school_2 = result
161
+
162
+ school_1[:school].should == 'School 1'
163
+ school_2[:school].should == 'School 2'
164
+
165
+ school_1[:score].should == 3.0
166
+ school_2[:score].should == 8.0
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end