dm-mongo-adapter 0.2.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +9 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +130 -0
  4. data/Rakefile +36 -0
  5. data/TODO +33 -0
  6. data/VERSION.yml +5 -0
  7. data/bin/console +31 -0
  8. data/dm-mongo-adapter.gemspec +154 -0
  9. data/lib/mongo_adapter.rb +71 -0
  10. data/lib/mongo_adapter/adapter.rb +255 -0
  11. data/lib/mongo_adapter/aggregates.rb +21 -0
  12. data/lib/mongo_adapter/conditions.rb +100 -0
  13. data/lib/mongo_adapter/embedded_model.rb +187 -0
  14. data/lib/mongo_adapter/embedded_resource.rb +134 -0
  15. data/lib/mongo_adapter/embedments/one_to_many.rb +139 -0
  16. data/lib/mongo_adapter/embedments/one_to_one.rb +53 -0
  17. data/lib/mongo_adapter/embedments/relationship.rb +258 -0
  18. data/lib/mongo_adapter/migrations.rb +45 -0
  19. data/lib/mongo_adapter/model.rb +89 -0
  20. data/lib/mongo_adapter/model/embedment.rb +215 -0
  21. data/lib/mongo_adapter/modifier.rb +85 -0
  22. data/lib/mongo_adapter/query.rb +252 -0
  23. data/lib/mongo_adapter/query/java_script.rb +145 -0
  24. data/lib/mongo_adapter/resource.rb +147 -0
  25. data/lib/mongo_adapter/types/date.rb +28 -0
  26. data/lib/mongo_adapter/types/date_time.rb +28 -0
  27. data/lib/mongo_adapter/types/db_ref.rb +65 -0
  28. data/lib/mongo_adapter/types/discriminator.rb +32 -0
  29. data/lib/mongo_adapter/types/object_id.rb +72 -0
  30. data/lib/mongo_adapter/types/objects.rb +31 -0
  31. data/script/performance.rb +260 -0
  32. data/spec/legacy/README +6 -0
  33. data/spec/legacy/adapter_shared_spec.rb +299 -0
  34. data/spec/legacy/adapter_spec.rb +174 -0
  35. data/spec/legacy/associations_spec.rb +140 -0
  36. data/spec/legacy/embedded_resource_spec.rb +157 -0
  37. data/spec/legacy/embedments_spec.rb +177 -0
  38. data/spec/legacy/modifier_spec.rb +81 -0
  39. data/spec/legacy/property_spec.rb +51 -0
  40. data/spec/legacy/spec_helper.rb +3 -0
  41. data/spec/legacy/sti_spec.rb +53 -0
  42. data/spec/lib/cleanup_models.rb +32 -0
  43. data/spec/lib/raw_connections.rb +11 -0
  44. data/spec/public/embedded_collection_spec.rb +61 -0
  45. data/spec/public/embedded_resource_spec.rb +220 -0
  46. data/spec/public/model/embedment_spec.rb +186 -0
  47. data/spec/public/model_spec.rb +37 -0
  48. data/spec/public/resource_spec.rb +564 -0
  49. data/spec/public/shared/model_embedments_spec.rb +338 -0
  50. data/spec/public/shared/object_id_shared_spec.rb +56 -0
  51. data/spec/public/types/df_ref_spec.rb +6 -0
  52. data/spec/public/types/discriminator_spec.rb +118 -0
  53. data/spec/public/types/embedded_array_spec.rb +55 -0
  54. data/spec/public/types/embedded_hash_spec.rb +83 -0
  55. data/spec/public/types/object_id_spec.rb +6 -0
  56. data/spec/rcov.opts +6 -0
  57. data/spec/semipublic/embedded_model_spec.rb +43 -0
  58. data/spec/semipublic/model/embedment_spec.rb +42 -0
  59. data/spec/semipublic/resource_spec.rb +70 -0
  60. data/spec/spec.opts +4 -0
  61. data/spec/spec_helper.rb +45 -0
  62. data/tasks/spec.rake +37 -0
  63. data/tasks/yard.rake +9 -0
  64. data/tasks/yardstick.rake +21 -0
  65. metadata +215 -0
@@ -0,0 +1,72 @@
1
+ module DataMapper
2
+ module Mongo
3
+ module Types
4
+ # Each object (document) stored in Mongo DB has an _id field as its
5
+ # first attribute. This is an object id. It must be unique for each
6
+ # member of a collection (this is enforced if the collection has an _id
7
+ # index, which is the case by default).
8
+ #
9
+ # The database will assign an _id if an object being inserted into a
10
+ # collection does not have one.
11
+ #
12
+ # The _id may be of any type as long as it is a unique value.
13
+ #
14
+ # @see http://www.mongodb.org/display/DOCS/Object+IDs
15
+ #
16
+ # @api public
17
+ class ObjectID < DataMapper::Type
18
+ primitive ::Object
19
+ key true
20
+ field "_id"
21
+ required false
22
+
23
+ # Returns the ObjectID as a string; suitable for use in a Resource
24
+ #
25
+ # @return [String]
26
+ #
27
+ # @api public
28
+ def self.load(value, property)
29
+ typecast(value, property)
30
+ end
31
+
32
+ # Returns the ObjectID as a Mongo::ObjectID; suitable to be passed to
33
+ # the Mongo library
34
+ #
35
+ # @return [Mongo::ObjectID] The dumped ID.
36
+ #
37
+ # @api public
38
+ def self.dump(value, property)
39
+ case value
40
+ when NilClass
41
+ nil
42
+ when String
43
+ ::Mongo::ObjectID.from_string(value)
44
+ when ::Mongo::ObjectID
45
+ value
46
+ else
47
+ raise ArgumentError.new('+value+ must be nil, String or ObjectID')
48
+ end
49
+ end
50
+
51
+ # Returns the ObjectID as a string
52
+ #
53
+ # @return [String]
54
+ #
55
+ # @api semipublic
56
+ def self.typecast(value, property)
57
+ case value
58
+ when NilClass
59
+ nil
60
+ when String
61
+ value
62
+ when ::Mongo::ObjectID
63
+ value.to_s
64
+ else
65
+ raise ArgumentError.new('+value+ must be nil, String or ObjectID')
66
+ end
67
+ end
68
+
69
+ end # ObjectID
70
+ end # Types
71
+ end # Mongo
72
+ end # DataMapper
@@ -0,0 +1,31 @@
1
+ module DataMapper
2
+ module Mongo
3
+ module Types
4
+ class EmbeddedArray < DataMapper::Type
5
+ primitive Object
6
+ end
7
+
8
+ class EmbeddedHash < DataMapper::Type
9
+ primitive Object
10
+
11
+ # @api public
12
+ def self.load(value, property)
13
+ typecast(value, property)
14
+ end
15
+
16
+ # @api semipublic
17
+ def self.typecast(value, property)
18
+ case value
19
+ when NilClass
20
+ nil
21
+ when Hash
22
+ value.to_mash.symbolize_keys
23
+ when Array
24
+ value.empty? ? Mash.new : [value].to_mash.symbolize_keys
25
+ end
26
+ end
27
+ end #EmbeddedHash
28
+
29
+ end # Types
30
+ end # Mongo
31
+ end # DataMapper
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+ require 'rubygems'
5
+
6
+ gem 'addressable', '~>2.0'
7
+ gem 'faker', '~>0.3.1'
8
+ gem 'rbench', '~>0.2.3'
9
+
10
+ require 'addressable/uri'
11
+ require 'faker'
12
+ require 'rbench'
13
+ require 'dm-core'
14
+
15
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'mongo_adapter'))
16
+
17
+ TIMES = ENV.key?('x') ? ENV['x'].to_i : 10_000
18
+
19
+ configuration = { :hostname => 'localhost', :database => 'dm_core_test', :username => 'root', :password => '' }
20
+
21
+ DataMapper.setup(:default, configuration.merge(:adapter => "mongo").except(:username, :password))
22
+ DataMapper.setup(:mysql, configuration.merge(:adapter => "mysql"))
23
+
24
+ class User
25
+ include DataMapper::Resource
26
+
27
+ def self.default_repository_name; :mysql; end
28
+
29
+ property :id, Serial
30
+ property :name, String
31
+ property :email, String
32
+ property :about, Text, :lazy => false
33
+ property :created_on, Time
34
+ end
35
+
36
+ class Exhibit
37
+ include DataMapper::Resource
38
+
39
+ def self.default_repository_name; :mysql; end
40
+
41
+ property :id, Serial
42
+ property :name, String
43
+ property :user_id, Integer
44
+ property :notes, Text, :lazy => false
45
+ property :created_on, Time
46
+ end
47
+
48
+ User.has User.n, :exhibits
49
+ Exhibit.belongs_to :user
50
+
51
+ module Mongo
52
+ class User
53
+ include DataMapper::Mongo::Resource
54
+
55
+ def self.default_storage_name; 'user'; end
56
+
57
+ property :id, DataMapper::Mongo::Types::ObjectID
58
+ property :name, String
59
+ property :email, String
60
+ property :about, Text, :lazy => false
61
+ property :created_on, Time
62
+ end
63
+
64
+ class Exhibit
65
+ include DataMapper::Mongo::Resource
66
+
67
+ def self.default_storage_name; 'exhibit'; end
68
+
69
+ property :id, DataMapper::Mongo::Types::ObjectID
70
+ property :name, String
71
+ property :user_id, Integer
72
+ property :notes, Text, :lazy => false
73
+ property :created_on, Time
74
+
75
+ belongs_to :user
76
+ end
77
+
78
+ User.has User.n, :exhibits
79
+ Exhibit.belongs_to :user
80
+ end
81
+
82
+ def touch_attributes(*exhibits)
83
+ exhibits.flatten.each do |exhibit|
84
+ exhibit.id
85
+ exhibit.name
86
+ exhibit.created_on
87
+ end
88
+ end
89
+
90
+ def touch_relationships(*exhibits)
91
+ exhibits.flatten.each do |exhibit|
92
+ exhibit.id
93
+ exhibit.name
94
+ exhibit.created_on
95
+ exhibit.user
96
+ end
97
+ end
98
+
99
+ root = File.dirname(__FILE__)
100
+
101
+ dump_dir = root / 'dumps'
102
+
103
+ dump_dir_mysql = dump_dir / 'mysql'
104
+ dump_dir_mongo = dump_dir / 'mongo'
105
+
106
+ FileUtils.mkdir_p(dump_dir_mysql) unless File.exist?(dump_dir_mysql)
107
+ FileUtils.mkdir_p(dump_dir_mongo) unless File.exist?(dump_dir_mongo)
108
+
109
+ dump_sql = dump_dir / 'mysql' / 'dump.sql'
110
+ dump_mongo_users = dump_dir_mongo / configuration[:database] / 'users.bson'
111
+ dump_mongo_exhibits = dump_dir_mongo / configuration[:database] / 'exhibits.bson'
112
+
113
+ mongo_user_ids = []
114
+ mongo_exhibit_ids = []
115
+
116
+ mysql_user_ids = []
117
+ mysql_exhibit_ids = []
118
+
119
+ puts 'Clearing databases...'
120
+
121
+ User.auto_migrate!
122
+ Exhibit.auto_migrate!
123
+
124
+ Mongo::User.auto_migrate!
125
+ Mongo::Exhibit.auto_migrate!
126
+
127
+ # pre-compute the insert statements and fake data compilation,
128
+ # so the benchmmysqlks below show the actual runtime for the execute
129
+ # method, minus the setup steps
130
+
131
+ # Using the same paragraphs for all exhibits because it is very slow
132
+ # to generate unique paragraphs for all exhibits.
133
+ notes = Faker::Lorem.paragraphs.join($/)
134
+ today = Time.now
135
+
136
+ puts 'Generating data for benchmarking...'
137
+
138
+ c = configuration
139
+
140
+ if File.exist?(dump_sql) && File.exist?(dump_mongo_users) && File.exist?(dump_mongo_exhibits)
141
+ puts "Found db dumps, importing..."
142
+
143
+ `mysql -u #{c[:username]} #{"-p#{ c[:password]}" unless c[:password].blank?} --database #{c[:database]} < #{dump_sql}`
144
+ `mongorestore -d #{c[:database]} -c users --dir #{dump_mongo_users}`
145
+ `mongorestore -d #{c[:database]} -c exhibits --dir #{dump_mongo_exhibits}`
146
+
147
+ mysql_user_ids = User.all.map(&:id)
148
+ mysql_exhibit_ids = Exhibit.all.map(&:id)
149
+
150
+ mongo_user_ids = Mongo::User.all.map(&:id)
151
+ mongo_exhibit_ids = Mongo::Exhibit.all.map(&:id)
152
+ else
153
+ FileUtils.rm(dump_sql) if File.exist?(dump_sql)
154
+ FileUtils.rm_r(dump_mongo_users) if File.exist?(dump_mongo_users)
155
+ FileUtils.rm_r(dump_mongo_exhibits) if File.exist?(dump_mongo_exhibits)
156
+
157
+ puts 'Inserting 10,000 users and exhibits...'
158
+ 10_000.times do
159
+ user_properties = {
160
+ :created_on => today,
161
+ :name => Faker::Name.name,
162
+ :email => Faker::Internet.email }
163
+
164
+ exhibit_properties = {
165
+ :created_on => today,
166
+ :name => Faker::Company.name,
167
+ :notes => notes
168
+ }
169
+
170
+ mongo_user = Mongo::User.create(user_properties)
171
+ mongo_exhibit = Mongo::Exhibit.create(exhibit_properties.merge(:user_id => mongo_user.id))
172
+
173
+ mongo_user_ids << mongo_user.id.to_s
174
+ mongo_exhibit_ids << mongo_exhibit.id.to_s
175
+
176
+ mysql_user = User.new(user_properties)
177
+ mysql_user.save!
178
+ mysql_exhibit = Exhibit.new(exhibit_properties.merge(:user_id => mysql_user.id))
179
+ mysql_exhibit.save!
180
+
181
+ mysql_user_ids << mysql_user.id
182
+ mysql_exhibit_ids << mysql_exhibit.id
183
+ end
184
+
185
+ # mysql dump
186
+ `mysqldump -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits users > #{dump_sql}`
187
+ # mongo dump
188
+ `mongodump -d #{c[:database]} -c users -o #{dump_dir_mongo}`
189
+ `mongodump -d #{c[:database]} -c exhibits -o #{dump_dir_mongo}`
190
+ end
191
+
192
+ RBench.run(TIMES) do
193
+ column :times
194
+ column :mysql, :title => 'MySQL Adapter'
195
+ column :mongo, :title => "Mongo Adapter"
196
+ column :diff, :compare => [:mysql, :mongo]
197
+
198
+ report 'Model#id', (TIMES * 100).ceil do
199
+ mysql_obj = Exhibit.get(mysql_exhibit_ids.first)
200
+ mongo_obj = Mongo::Exhibit.get(mongo_exhibit_ids.first)
201
+
202
+ mysql { mysql_obj.id }
203
+ mongo { mongo_obj.id }
204
+ end
205
+
206
+ report 'Model.get specific (not cached)' do
207
+ mysql { touch_attributes(Exhibit.get(mysql_exhibit_ids.first)) }
208
+ mongo { touch_attributes(Mongo::Exhibit.get(mongo_exhibit_ids.first)) }
209
+ end
210
+
211
+ report 'Model.get specific (cached)' do
212
+ Exhibit.repository(:mysql) { mysql { touch_attributes(Exhibit.get(mysql_exhibit_ids.first)) } }
213
+ Mongo::Exhibit.repository(:default) { mongo { touch_attributes(Mongo::Exhibit.get(mongo_exhibit_ids.first)) } }
214
+ end
215
+
216
+ report 'Model.first' do
217
+ mysql { touch_attributes(Exhibit.first) }
218
+ mongo { touch_attributes(Mongo::Exhibit.first) }
219
+ end
220
+
221
+ report 'Model.all limit(100)', (TIMES / 100).ceil do
222
+ mysql { touch_attributes(Exhibit.all(:limit => 100)) }
223
+ mongo { touch_attributes(Mongo::Exhibit.all(:limit => 100)) }
224
+ end
225
+
226
+ report 'Model.all limit(10,000)', (TIMES / 1000).ceil do
227
+ mysql { touch_attributes(Exhibit.all(:limit => 10_000)) }
228
+ mongo { touch_attributes(Mongo::Exhibit.all(:limit => 10_000)) }
229
+ end
230
+
231
+ exhibit = {
232
+ :name => Faker::Company.name,
233
+ :notes => Faker::Lorem.paragraphs.join($/),
234
+ :created_on => today
235
+ }
236
+
237
+ report 'Model.create' do
238
+ mysql { Exhibit.create(exhibit) }
239
+ mongo { Mongo::Exhibit.create(exhibit) }
240
+ end
241
+
242
+ report 'Resource#attributes=' do
243
+ attrs_first = { :name => 'sam', :notes => 'foo bar' }
244
+ attrs_second = { :name => 'tom', :notes => 'foo bar' }
245
+ mysql { exhibit = Exhibit.new(attrs_first); exhibit.attributes = attrs_second }
246
+ mongo { exhibit = Mongo::Exhibit.new(attrs_first); exhibit.attributes = attrs_second }
247
+ end
248
+
249
+ report 'Resource#update' do
250
+ mysql { Exhibit.get(mysql_user_ids.first).update(:name => 'bob') }
251
+ mongo { Mongo::Exhibit.get(mongo_exhibit_ids.first).update(:name => 'bob') }
252
+ end
253
+
254
+ report 'Resource#destroy' do
255
+ mysql { Exhibit.first.destroy }
256
+ mongo { Mongo::Exhibit.first.destroy }
257
+ end
258
+
259
+ summary 'Total'
260
+ end
@@ -0,0 +1,6 @@
1
+ This directory contains the old specs, written for 0.1.0. They are retained
2
+ here while the specs are separated into spec/{public,semipublic}.
3
+
4
+ Examples marked as @done indicate those for which there are equivalents in
5
+ spec/{public,semipublic}. Once all specs are moved (or rewritten, as
6
+ appropriate), the legacy specs will be removed.
@@ -0,0 +1,299 @@
1
+ share_examples_for 'An Adapter' do
2
+
3
+ def self.adapter_supports?(*methods)
4
+ methods.all? do |method|
5
+ # TODO: figure out a way to see if the instance method is only inherited
6
+ # from the Abstract Adapter, and not defined in it's class. If that is
7
+ # the case return false
8
+
9
+ # CRUD methods can be inherited from parent class
10
+ described_type.instance_methods.any? { |instance_method| method.to_s == instance_method.to_s }
11
+ end
12
+ end
13
+
14
+ before :all do
15
+ # create all tables and constraints before each spec
16
+ if @repository.respond_to?(:auto_migrate!)
17
+ Heffalump.auto_migrate!
18
+ end
19
+ end
20
+
21
+ if adapter_supports?(:create)
22
+ describe '#create' do
23
+ it 'should not raise any errors' do
24
+ lambda {
25
+ Heffalump.create(:color => 'peach')
26
+ }.should_not raise_error
27
+ end
28
+
29
+ it 'should set the identity field for the resource' do
30
+ heffalump = Heffalump.new(:color => 'peach')
31
+ heffalump.id.should be_nil
32
+ heffalump.save
33
+ heffalump.id.should_not be_nil
34
+ end
35
+ end
36
+ else
37
+ it 'needs to support #create'
38
+ end
39
+
40
+ if adapter_supports?(:read)
41
+ describe '#read' do
42
+ before :all do
43
+ @heffalump = Heffalump.create(:color => 'brownish hue')
44
+ #just going to borrow this, so I can check the return values
45
+ @query = Heffalump.all.query
46
+ end
47
+
48
+ it 'should not raise any errors' do
49
+ lambda {
50
+ Heffalump.all()
51
+ }.should_not raise_error
52
+ end
53
+
54
+ it 'should return stuff' do
55
+ Heffalump.all.should be_include(@heffalump)
56
+ end
57
+ end
58
+ else
59
+ it 'needs to support #read'
60
+ end
61
+
62
+ if adapter_supports?(:update)
63
+ describe '#update' do
64
+ before do
65
+ @heffalump = Heffalump.create(:color => 'indigo')
66
+ end
67
+
68
+ it 'should not raise any errors' do
69
+ lambda {
70
+ @heffalump.color = 'violet'
71
+ @heffalump.save
72
+ }.should_not raise_error
73
+ end
74
+
75
+ it 'should not alter the identity field' do
76
+ id = @heffalump.id
77
+ @heffalump.color = 'violet'
78
+ @heffalump.save
79
+ @heffalump.id.should == id
80
+ end
81
+
82
+ it 'should update altered fields' do
83
+ @heffalump.color = 'violet'
84
+ @heffalump.save
85
+ Heffalump.get(*@heffalump.key).color.should == 'violet'
86
+ end
87
+
88
+ it 'should not alter other fields' do
89
+ color = @heffalump.color
90
+ @heffalump.num_spots = 3
91
+ @heffalump.save
92
+ Heffalump.get(*@heffalump.key).color.should == color
93
+ end
94
+ end
95
+ else
96
+ it 'needs to support #update'
97
+ end
98
+
99
+ if adapter_supports?(:delete)
100
+ describe '#delete' do
101
+ before do
102
+ @heffalump = Heffalump.create(:color => 'forest green')
103
+ end
104
+
105
+ it 'should not raise any errors' do
106
+ lambda {
107
+ @heffalump.destroy
108
+ }.should_not raise_error
109
+ end
110
+
111
+ it 'should delete the requested resource' do
112
+ id = @heffalump.id
113
+ @heffalump.destroy
114
+ Heffalump.get(id).should be_nil
115
+ end
116
+ end
117
+ else
118
+ it 'needs to support #delete'
119
+ end
120
+
121
+ if adapter_supports?(:read, :create)
122
+ describe 'query matching' do
123
+ before :all do
124
+ @red = Heffalump.create(:color => 'red')
125
+ @two = Heffalump.create(:num_spots => 2)
126
+ @five = Heffalump.create(:num_spots => 5)
127
+ end
128
+
129
+ describe 'conditions' do
130
+ describe 'eql' do
131
+ it 'should be able to search for objects included in an inclusive range of values' do
132
+ Heffalump.all(:num_spots => 1..5).should be_include(@five)
133
+ end
134
+
135
+ it 'should be able to search for objects included in an exclusive range of values' do
136
+ Heffalump.all(:num_spots => 1...6).should be_include(@five)
137
+ end
138
+
139
+ it 'should not be able to search for values not included in an inclusive range of values' do
140
+ Heffalump.all(:num_spots => 1..4).should_not be_include(@five)
141
+ end
142
+
143
+ it 'should not be able to search for values not included in an exclusive range of values' do
144
+ Heffalump.all(:num_spots => 1...5).should_not be_include(@five)
145
+ end
146
+ end
147
+
148
+ describe 'not' do
149
+ it 'should be able to search for objects with not equal value' do
150
+ Heffalump.all(:color.not => 'red').should_not be_include(@red)
151
+ end
152
+
153
+ it 'should include objects that are not like the value' do
154
+ Heffalump.all(:color.not => 'black').should be_include(@red)
155
+ end
156
+
157
+ it 'should be able to search for objects with not nil value' do
158
+ Heffalump.all(:color.not => nil).should be_include(@red)
159
+ end
160
+
161
+ it 'should not include objects with a nil value' do
162
+ Heffalump.all(:color.not => nil).should_not be_include(@two)
163
+ end
164
+
165
+ it 'should be able to search for object with a nil value using required properties' do
166
+ Heffalump.all(:id.not => nil).should == [ @red, @two, @five ]
167
+ end
168
+
169
+ it 'should be able to search for objects not in an empty list (match all)' do
170
+ Heffalump.all(:color.not => []).should == [ @red, @two, @five ]
171
+ end
172
+
173
+ it 'should be able to search for objects in an empty list and another OR condition (match none on the empty list)' do
174
+ Heffalump.all(:conditions => DataMapper::Query::Conditions::Operation.new(
175
+ :or,
176
+ DataMapper::Query::Conditions::Comparison.new(:in, Heffalump.properties[:color], []),
177
+ DataMapper::Query::Conditions::Comparison.new(:in, Heffalump.properties[:num_spots], [5]))).should == [ @five ]
178
+ end
179
+
180
+ it 'should be able to search for objects not included in an array of values' do
181
+ Heffalump.all(:num_spots.not => [ 1, 3, 5, 7 ]).should be_include(@two)
182
+ end
183
+
184
+ it 'should be able to search for objects not included in an array of values' do
185
+ Heffalump.all(:num_spots.not => [ 1, 3, 5, 7 ]).should_not be_include(@five)
186
+ end
187
+
188
+ it 'should be able to search for objects not included in an inclusive range of values' do
189
+ Heffalump.all(:num_spots.not => 1..4).should be_include(@five)
190
+ end
191
+
192
+ it 'should be able to search for objects not included in an exclusive range of values' do
193
+ Heffalump.all(:num_spots.not => 1...5).should be_include(@five)
194
+ end
195
+
196
+ it 'should not be able to search for values not included in an inclusive range of values' do
197
+ Heffalump.all(:num_spots.not => 1..5).should_not be_include(@five)
198
+ end
199
+
200
+ it 'should not be able to search for values not included in an exclusive range of values' do
201
+ Heffalump.all(:num_spots.not => 1...6).should_not be_include(@five)
202
+ end
203
+ end
204
+
205
+ describe 'like' do
206
+ it 'should be able to search for objects that match value' do
207
+ Heffalump.all(:color.like => '%ed').should be_include(@red)
208
+ end
209
+
210
+ it 'should not search for objects that do not match the value' do
211
+ Heffalump.all(:color.like => '%blak%').should_not be_include(@red)
212
+ end
213
+ end
214
+
215
+ describe 'regexp' do
216
+ before do
217
+ if (defined?(DataMapper::Adapters::Sqlite3Adapter) && @adapter.kind_of?(DataMapper::Adapters::Sqlite3Adapter) ||
218
+ defined?(DataMapper::Adapters::SqlserverAdapter) && @adapter.kind_of?(DataMapper::Adapters::SqlserverAdapter))
219
+ pending 'delegate regexp matches to same system that the InMemory and YAML adapters use'
220
+ end
221
+ end
222
+
223
+ it 'should be able to search for objects that match value' do
224
+ Heffalump.all(:color => /ed/).should be_include(@red)
225
+ end
226
+
227
+ it 'should not be able to search for objects that do not match the value' do
228
+ Heffalump.all(:color => /blak/).should_not be_include(@red)
229
+ end
230
+
231
+ it 'should be able to do a negated search for objects that match value' do
232
+ Heffalump.all(:color.not => /blak/).should be_include(@red)
233
+ end
234
+
235
+ it 'should not be able to do a negated search for objects that do not match value' do
236
+ Heffalump.all(:color.not => /ed/).should_not be_include(@red)
237
+ end
238
+
239
+ end
240
+
241
+ describe 'gt' do
242
+ it 'should be able to search for objects with value greater than' do
243
+ Heffalump.all(:num_spots.gt => 1).should be_include(@two)
244
+ end
245
+
246
+ it 'should not find objects with a value less than' do
247
+ Heffalump.all(:num_spots.gt => 3).should_not be_include(@two)
248
+ end
249
+ end
250
+
251
+ describe 'gte' do
252
+ it 'should be able to search for objects with value greater than' do
253
+ Heffalump.all(:num_spots.gte => 1).should be_include(@two)
254
+ end
255
+
256
+ it 'should be able to search for objects with values equal to' do
257
+ Heffalump.all(:num_spots.gte => 2).should be_include(@two)
258
+ end
259
+
260
+ it 'should not find objects with a value less than' do
261
+ Heffalump.all(:num_spots.gte => 3).should_not be_include(@two)
262
+ end
263
+ end
264
+
265
+ describe 'lt' do
266
+ it 'should be able to search for objects with value less than' do
267
+ Heffalump.all(:num_spots.lt => 3).should be_include(@two)
268
+ end
269
+
270
+ it 'should not find objects with a value less than' do
271
+ Heffalump.all(:num_spots.gt => 2).should_not be_include(@two)
272
+ end
273
+ end
274
+
275
+ describe 'lte' do
276
+ it 'should be able to search for objects with value less than' do
277
+ Heffalump.all(:num_spots.lte => 3).should be_include(@two)
278
+ end
279
+
280
+ it 'should be able to search for objects with values equal to' do
281
+ Heffalump.all(:num_spots.lte => 2).should be_include(@two)
282
+ end
283
+
284
+ it 'should not find objects with a value less than' do
285
+ Heffalump.all(:num_spots.lte => 1).should_not be_include(@two)
286
+ end
287
+ end
288
+ end
289
+
290
+ describe 'limits' do
291
+ it 'should be able to limit the objects' do
292
+ Heffalump.all(:limit => 2).length.should == 2
293
+ end
294
+ end
295
+ end
296
+ else
297
+ it 'needs to support #read and #create to test query matching'
298
+ end
299
+ end