dm-mongo-adapter 0.2.0.pre3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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