mongomatic 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -98,7 +98,52 @@ You can add validations to your model by creating a validate method. If your val
98
98
  p["address"] = { "zip" => 94107 }
99
99
  p.valid?
100
100
  => true
101
-
101
+
102
+ === The Validation Helper (Expectations)
103
+
104
+ To make writing your validate method a little simpler you can use Mongomatic::Expectations. You must include Mongomatic::Expectations::Helper on any class you wish to use them. You can use expectations like this:
105
+
106
+ class Person < Mongomatic::Base
107
+ include Mongomatic::Expectations::Helper
108
+
109
+ def validate
110
+ expectations do
111
+ be_present self['name'], "Name cannot be blank"
112
+ not_be_match self['nickname'], "Nickname cannot contain an uppercase letter", :with => /[A-Z]/
113
+ end
114
+ end
115
+ end
116
+
117
+ p = Person.new
118
+ p.valid?
119
+ => false
120
+ p.errors.full_messages
121
+ => ["Name cannot be blank"]
122
+ p['name'] = 'Jordan'
123
+ p.valid?
124
+ => true
125
+ p['nickname'] = 'Jordan'
126
+ p.valid?
127
+ p.errors.full_messages
128
+ => ["Name cannot be blank", "Nickname cannot contain an uppercase letter"]=> false
129
+
130
+ You can use any of these expectations inside of the expectations block:
131
+
132
+ * be_expected value, error_message - validates that value is true
133
+ * not_be_expected value, error_message - validates that value is false
134
+ * [not]_be_present value, error_message - validates presence of value
135
+ * [not]_be_a_number value, error_message, options - validates that value is/is not a number
136
+ Can be a string containing only a number).
137
+ Only takes :allow_nil option
138
+
139
+ * [not]_be_match value, error_message, options - validates that value matches/does not match
140
+ regular expression specified by option :with.
141
+ Also, takes option :allow_nil
142
+ * be_of_length value, error_message, options - validates that value is any of: less than the number
143
+ specified in the :minimum option, greater than the number
144
+ specified in option :maximum, or in range specified by
145
+ option :range
146
+
102
147
  == Relationships
103
148
 
104
149
  Mongomatic doesn't have any kind of special support for relationship management but it's easy to add this to your models where you need it. Here's an example that models Twitter-like followers on a User model:
data/lib/mongomatic.rb CHANGED
@@ -15,10 +15,13 @@ end
15
15
 
16
16
  module Mongomatic
17
17
  class << self
18
+ # Returns an instance of Mongo::DB
18
19
  def db
19
20
  @db
20
21
  end
21
22
 
23
+ # Set to an instance of Mongo::DB to be used for all models:
24
+ # Mongomatic.db = Mongo::Connection.new().db('mydb')
22
25
  def db=(obj)
23
26
  unless obj.is_a?(Mongo::DB)
24
27
  raise(ArgumentError, "Must supply a Mongo::DB object")
@@ -30,4 +33,5 @@ end
30
33
  require "#{File.dirname(__FILE__)}/mongomatic/cursor"
31
34
  require "#{File.dirname(__FILE__)}/mongomatic/modifiers"
32
35
  require "#{File.dirname(__FILE__)}/mongomatic/errors"
36
+ require "#{File.dirname(__FILE__)}/mongomatic/expectations"
33
37
  require "#{File.dirname(__FILE__)}/mongomatic/base"
@@ -3,45 +3,56 @@ module Mongomatic
3
3
  include Mongomatic::Modifiers
4
4
 
5
5
  class << self
6
+ # Returns this models own db attribute if set, otherwise will return Mongomatic.db
6
7
  def db
7
8
  @db || Mongomatic.db || raise(ArgumentError, "No db supplied")
8
9
  end
9
10
 
11
+ # Override Mongomatic.db with a Mongo::DB instance for this model specifically
12
+ # MyModel.db = Mongo::Connection.new().db('mydb_mymodel')
10
13
  def db=(obj)
11
14
  unless obj.is_a?(Mongo::DB)
12
15
  raise(ArgumentError, "Must supply a Mongo::DB object")
13
16
  end; @db = obj
14
17
  end
15
18
 
19
+ # Override this method on your model if you want to use a different collection name
16
20
  def collection_name
17
21
  self.to_s
18
22
  end
19
23
 
24
+ # Return the raw MongoDB collection for this model
20
25
  def collection
21
26
  @collection ||= self.db.collection(self.collection_name)
22
27
  end
23
28
 
29
+ # Query MongoDB for documents. Same arguments as http://api.mongodb.org/ruby/current/Mongo/Collection.html#find-instance_method
24
30
  def find(query={}, opts={})
25
31
  Mongomatic::Cursor.new(self, collection.find(query, opts))
26
32
  end
27
33
 
34
+ # Query MongoDB and return one document only. Same arguments as http://api.mongodb.org/ruby/current/Mongo/Collection.html#find_one-instance_method
28
35
  def find_one(query={}, opts={})
29
36
  return nil unless doc = self.collection.find_one(query, opts)
30
37
  self.new(doc, false)
31
38
  end
32
39
 
40
+ # Return a Mongomatic::Cursor instance of all documents in the collection.
33
41
  def all
34
42
  find
35
43
  end
36
44
 
45
+ # Iterate over all documents in the collection (uses a Mongomatic::Cursor)
37
46
  def each
38
47
  find.each { |found| yield(found) }
39
48
  end
40
49
 
50
+ # Return the first document in the collection
41
51
  def first
42
52
  find.limit(1).next_document
43
53
  end
44
54
 
55
+ # Return the number of documents in the collection
45
56
  def count
46
57
  find.count
47
58
  end
@@ -56,6 +67,12 @@ module Mongomatic
56
67
  self.errors = Mongomatic::Errors.new
57
68
  end
58
69
 
70
+ # Override this with your own validate() method for validations.
71
+ # Simply push your errors into the self.errors property and
72
+ # if self.errors remains empty your document will be valid.
73
+ # def validate
74
+ # self.errors << ["name", "cannot be blank"]
75
+ # end
59
76
  def validate
60
77
  true
61
78
  end
@@ -76,32 +93,48 @@ module Mongomatic
76
93
  self.is_new == true
77
94
  end
78
95
 
96
+ # Set a field on this document:
97
+ # mydoc["name"] = "Ben"
98
+ # mydoc["address"] = { "city" => "San Francisco" }
79
99
  def []=(k,v)
80
100
  @doc[k.to_s] = v
81
101
  end
82
102
 
103
+ # Fetch a field (just like a hash):
104
+ # mydoc["name"]
105
+ # => "Ben"
83
106
  def [](k)
84
107
  @doc[k.to_s]
85
108
  end
86
109
 
110
+ # Merge this document with the supplied hash. Useful for updates:
111
+ # mydoc.merge(params[:user])
87
112
  def merge(hash)
88
113
  hash.each { |k,v| self[k] = v }; @doc
89
114
  end
90
115
 
116
+ # Will return true if the document has been removed.
91
117
  def removed?
92
118
  self.removed == true
93
119
  end
94
120
 
121
+ # Check equality with another Mongomatic document
95
122
  def ==(obj)
96
123
  obj.is_a?(self.class) && obj.doc["_id"] == @doc["_id"]
97
124
  end
98
125
 
126
+ # Reload the document from the database
99
127
  def reload
100
128
  if obj = self.class.find({"_id" => @doc["_id"]}).next_document
101
129
  @doc = obj.doc; true
102
130
  end
103
131
  end
104
132
 
133
+ # Insert the document into the database. Will return false if the document has
134
+ # already been inserted or is invalid. Returns the generated BSON::ObjectID
135
+ # for the new document. Will silently fail if MongoDB is unable to insert the
136
+ # document, use insert! if you want an error raised instead. Note that this will
137
+ # require an additional call to the db.
105
138
  def insert(opts={})
106
139
  return false unless new? && valid?
107
140
  self.send(:before_insert) if self.respond_to?(:before_insert)
@@ -115,10 +148,16 @@ module Mongomatic
115
148
  ret
116
149
  end
117
150
 
151
+ # Calls insert(...) with {:safe => true} passed in as an option. Will check MongoDB
152
+ # after insert to make sure that the insert was successful, and raise a Mongo::OperationError
153
+ # if there were any problems.
118
154
  def insert!(opts={})
119
155
  insert(opts.merge(:safe => true))
120
156
  end
121
157
 
158
+ # Will persist any changes you have made to the document. Will silently fail if
159
+ # MongoDB is unable to update the document, use update! instead if you want an
160
+ # error raised. Note that this will require an additional call to the db.
122
161
  def update(opts={},update_doc=@doc)
123
162
  return false if new? || removed? || !valid?
124
163
  self.send(:before_update) if self.respond_to?(:before_update)
@@ -129,10 +168,13 @@ module Mongomatic
129
168
  ret
130
169
  end
131
170
 
171
+ # Same as update(...) but will raise a Mongo::OperationError in case of any issues.
132
172
  def update!(opts={},update_doc=@doc)
133
173
  update(opts.merge(:safe => true),update_doc)
134
174
  end
135
175
 
176
+ # Remove this document from the collection. Silently fails on error, use remove!
177
+ # if you want an exception raised.
136
178
  def remove(opts={})
137
179
  return false if new?
138
180
  self.send(:before_remove) if self.respond_to?(:before_remove)
@@ -143,10 +185,13 @@ module Mongomatic
143
185
  ret
144
186
  end
145
187
 
188
+ # Like remove(...) but raises Mongo::OperationError if MongoDB is unable to
189
+ # remove the document.
146
190
  def remove!(opts={})
147
191
  remove(opts.merge(:safe => true))
148
192
  end
149
193
 
194
+ # Return this document as a hash.
150
195
  def to_hash
151
196
  @doc || {}
152
197
  end
@@ -1,4 +1,11 @@
1
1
  module Mongomatic
2
+ # Wraps a Mongo::Cursor for managing result sets from MongoDB:
3
+ # cursor = User.find({"zip" => 94107})
4
+ # user1 = cursor.next
5
+ #
6
+ # User.find({"zip" => 94107}).each { |u| puts u["name"] }
7
+ #
8
+ # User.find({"zip" => 94107}).count
2
9
  class Cursor
3
10
  include Enumerable
4
11
 
@@ -0,0 +1,89 @@
1
+ module Mongomatic
2
+ module Expectations
3
+ module Helper
4
+
5
+ private
6
+
7
+ def define_expectations
8
+ Expectation.subclasses.each do |klass|
9
+ if Expectation.define_to_be?(klass)
10
+ instance_eval %Q{
11
+ def be_#{klass.name.downcase}(value, message, opts = {})
12
+ #{klass}.new(self, value, message, opts).to_be
13
+ end
14
+ }
15
+ end
16
+ if Expectation.define_to_not_be?(klass)
17
+ instance_eval %Q{
18
+ def not_be_#{klass.name.downcase}(value, message, opts = {})
19
+ #{klass}.new(self, value, message, opts).to_not_be
20
+ end
21
+ }
22
+ end
23
+ end
24
+ end
25
+
26
+ def undefine_expectations
27
+ Expectation.subclasses.each do |klass|
28
+ instance_eval %Q{
29
+ if respond_to? "be_#{klass.name.downcase}"
30
+ class << self
31
+ remove_method "be_#{klass.name.downcase}"
32
+ end
33
+ end
34
+ if respond_to? "not_be_#{klass.name.downcase}"
35
+ class << self
36
+ remove_method "not_be_#{klass.name.downcase}"
37
+ end
38
+ end
39
+ }
40
+ end
41
+ end
42
+
43
+ def expectations(&block)
44
+ define_expectations
45
+ block.call
46
+ undefine_expectations
47
+ end
48
+ end
49
+
50
+ class Expectation
51
+
52
+ attr_accessor :instance, :value, :message, :opts
53
+
54
+ class << self
55
+ attr_accessor :subclasses
56
+
57
+ def subclasses
58
+ @subclasses ||= []
59
+ @subclasses
60
+ end
61
+
62
+ def inherited(klass)
63
+ subclasses << klass
64
+ end
65
+
66
+ def define_to_be?(klass)
67
+ klass.new(nil, nil, nil).respond_to? :to_be
68
+ end
69
+
70
+ def define_to_not_be?(klass)
71
+ klass.new(nil, nil, nil).respond_to? :to_not_be
72
+ end
73
+ end
74
+
75
+ def initialize(instance, value, message, opts = {})
76
+ @value = value
77
+ @instance = instance
78
+ @message = message
79
+ @opts = opts
80
+ end
81
+
82
+ def add_error_msg
83
+ instance.errors << [message]
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ Dir["#{File.dirname(__FILE__)}/expectations/*.rb"].each { |f| require f }
@@ -0,0 +1,9 @@
1
+ class Expected < Mongomatic::Expectations::Expectation
2
+ def to_be
3
+ add_error_msg unless value
4
+ end
5
+
6
+ def to_not_be
7
+ add_error_msg if value
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ class IsNumber < Mongomatic::Expectations::Expectation
2
+ def self.name
3
+ "a_number"
4
+ end
5
+
6
+ def to_be
7
+ return true if opts[:allow_nil] && value.nil?
8
+
9
+ add_error_msg if (value.to_s =~ /^\d*\.{0,1}\d+$/).nil?
10
+ end
11
+
12
+ def to_not_be
13
+ add_error_msg unless (value.to_s =~ /^\d*\.{0,1}\d+$/).nil?
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ class Match < Mongomatic::Expectations::Expectation
2
+ def self.name
3
+ "match"
4
+ end
5
+
6
+ def to_be
7
+ return true if opts[:allow_nil] && value.nil?
8
+
9
+ add_error_msg unless opts[:with].match(value.to_s)
10
+ end
11
+
12
+ def to_not_be
13
+ add_error_msg if opts[:with].match(value.to_s)
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ class OfLength < Mongomatic::Expectations::Expectation
2
+ def self.name
3
+ "of_length"
4
+ end
5
+
6
+ def to_be
7
+ return true if opts[:allow_nil] && value.nil?
8
+
9
+ length = (value) ? value.size : value.to_s.size
10
+ add_error_msg if opts[:minimum] && length < opts[:minimum]
11
+ add_error_msg if opts[:maximum] && length > opts[:maximum]
12
+ if opts[:range]
13
+ add_error_msg unless opts[:range].include?(length)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ class Present < Mongomatic::Expectations::Expectation
2
+ def to_be
3
+ add_error_msg if value.blank?
4
+ end
5
+
6
+ def to_not_be
7
+ add_error_msg unless value.blank?
8
+ end
9
+ end
@@ -1,9 +1,11 @@
1
1
  module Mongomatic
2
+ # Provides convenience methods for atomic MongoDB operations.
2
3
  module Modifiers
3
4
 
4
- # { $push : { field : value } }
5
- # appends value to field, if field is an existing array, otherwise sets field to the array [value]
5
+ # MongoDB equivalent: { $push : { field : value } }<br/>
6
+ # Appends value to field, if field is an existing array, otherwise sets field to the array [value]
6
7
  # if field is not present. If field is present but is not an array, an error condition is raised.
8
+ # user.push("interests", "skydiving")
7
9
  def push(field, val, do_reload=true)
8
10
  field = field.to_s
9
11
  if update({}, { "$push" => { field => val } })
@@ -11,10 +13,11 @@ module Mongomatic
11
13
  end
12
14
  end
13
15
 
14
- # { $pushAll : { field : value_array } }
15
- # appends each value in value_array to field, if field is an existing array, otherwise sets field to
16
+ # MongoDB equivalent: { $pushAll : { field : value_array } }<br/>
17
+ # Appends each value in value_array to field, if field is an existing array, otherwise sets field to
16
18
  # the array value_array if field is not present. If field is present but is not an array, an error
17
19
  # condition is raised.
20
+ # user.push("interests", ["skydiving", "coding"])
18
21
  def push_all(field, val, do_reload=true)
19
22
  field = field.to_s
20
23
  val = Array(val)
@@ -23,9 +26,10 @@ module Mongomatic
23
26
  end
24
27
  end
25
28
 
26
- # { $pull : { field : _value } }
27
- # removes all occurrences of value from field, if field is an array. If field is present but is not
29
+ # MongoDB equivalent: { $pull : { field : _value } }<br/>
30
+ # Removes all occurrences of value from field, if field is an array. If field is present but is not
28
31
  # an array, an error condition is raised.
32
+ # user.pull("interests", "watching paint dry")
29
33
  def pull(field, val, do_reload=true)
30
34
  field = field.to_s
31
35
  if update({}, { "$pull" => { field => val } })
@@ -33,18 +37,20 @@ module Mongomatic
33
37
  end
34
38
  end
35
39
 
36
- # { $pullAll : { field : value_array } }
37
- # removes all occurrences of each value in value_array from field, if field is an array. If field is
40
+ # MongoDB equivalent: { $pullAll : { field : value_array } }<br/>
41
+ # Removes all occurrences of each value in value_array from field, if field is an array. If field is
38
42
  # present but is not an array, an error condition is raised.
39
- def pullAll(field, val, do_reload=true)
43
+ # user.pull_all("interests", ["watching paint dry", "sitting on my ass"])
44
+ def pull_all(field, val, do_reload=true)
40
45
  field = field.to_s
41
46
  if update({}, { "$pullAll" => { field => val } })
42
47
  reload if do_reload; true
43
48
  end
44
49
  end
45
50
 
46
- # { $inc : { field : value } }
47
- # increments field by the number value if field is present in the object, otherwise sets field to the number value.
51
+ # MongoDB equivalent: { $inc : { field : value } }<br/>
52
+ # Increments field by the number value if field is present in the object, otherwise sets field to the number value.
53
+ # user.inc("cents_in_wallet", 1000)
48
54
  def inc(field, val, do_reload=true)
49
55
  field = field.to_s
50
56
  if update({}, { "$inc" => { field => val } })
@@ -52,8 +58,9 @@ module Mongomatic
52
58
  end
53
59
  end
54
60
 
55
- # { $set : { field : value } }
56
- # sets field to value. All datatypes are supported with $set.
61
+ # MongoDB equivalent: { $set : { field : value } }<br/>
62
+ # Sets field to value. All datatypes are supported with $set.
63
+ # user.set("name", "Ben")
57
64
  def set(field, val, do_reload=true)
58
65
  field = field.to_s
59
66
  if update({}, { "$set" => { field => val } })
@@ -61,8 +68,9 @@ module Mongomatic
61
68
  end
62
69
  end
63
70
 
64
- # { $unset : { field : 1} }
71
+ # MongoDB equivalent: { $unset : { field : 1} }<br/>
65
72
  # Deletes a given field. v1.3+
73
+ # user.unset("name")
66
74
  def unset(field, do_reload=true)
67
75
  field = field.to_s
68
76
  if update({}, { "$unset" => { field => 1 } })
@@ -70,12 +78,11 @@ module Mongomatic
70
78
  end
71
79
  end
72
80
 
73
- # { $addToSet : { field : value } }
74
- # Adds value to the array only if its not in the array already.
75
- #
76
- # To add many valuest.update
77
- #
81
+ # MongoDB equivalent: { $addToSet : { field : value } }<br/>
82
+ # Adds value to the array only if its not in the array already.<br/>
83
+ # Or to add many values:<br/>
78
84
  # { $addToSet : { a : { $each : [ 3 , 5 , 6 ] } } }
85
+ # user.add_to_set("friend_ids", BSON::ObjectID('...'))
79
86
  def add_to_set(field, val, do_reload=true)
80
87
  field = field.to_s
81
88
  if update({}, { "$addToSet" => { field => val } })
@@ -83,8 +90,9 @@ module Mongomatic
83
90
  end
84
91
  end
85
92
 
86
- # { $pop : { field : 1 } }
87
- # removes the last element in an array (ADDED in 1.1)
93
+ # MongoDB equivalent: { $pop : { field : 1 } }<br/>
94
+ # Removes the last element in an array (ADDED in 1.1)
95
+ # user.pop_last("friend_ids")
88
96
  def pop_last(field, do_reload=true)
89
97
  field = field.to_s
90
98
  if update({}, { "$pop" => { field => 1 } })
@@ -92,8 +100,9 @@ module Mongomatic
92
100
  end
93
101
  end
94
102
 
95
- # { $pop : { field : -1 } }
96
- # removes the first element in an array (ADDED in 1.1)
103
+ # MongoDB equivalent: { $pop : { field : -1 } }<br/>
104
+ # Removes the first element in an array (ADDED in 1.1)
105
+ # user.pop_first("friend_ids")
97
106
  def pop_first(field, do_reload=true)
98
107
  field = field.to_s
99
108
  if update({}, { "$pop" => { field => -1 } })
data/test/helper.rb CHANGED
@@ -10,6 +10,7 @@ require 'mongomatic'
10
10
  Mongomatic.db = Mongo::Connection.new.db("mongomatic_test")
11
11
 
12
12
  class Person < Mongomatic::Base
13
+ include Mongomatic::Expectations::Helper
13
14
  attr_accessor :callback_tests
14
15
 
15
16
  def self.create_indexes
@@ -212,4 +212,169 @@ class TestMongomatic < Test::Unit::TestCase
212
212
  assert_equal found["_id"], "mycustomid"
213
213
  assert_equal 26, found["age"]
214
214
  end
215
+
216
+ should "be able to use the be_expect expectation" do
217
+ p = Person.new
218
+ class << p
219
+ def validate
220
+ expectations do
221
+ be_expected self['alive'], "Alive must be true"
222
+ not_be_expected self['dead'], "Dead must be false"
223
+ end
224
+ end
225
+ end
226
+
227
+ assert !p.valid?
228
+ assert_equal ['Alive must be true'], p.errors.full_messages
229
+
230
+ p['alive'] = true
231
+ assert p.valid?
232
+
233
+ p['dead'] = true
234
+ assert !p.valid?
235
+ assert_equal ['Dead must be false'], p.errors.full_messages
236
+ end
237
+
238
+ should "be able to use the be_present expectation" do
239
+ p = Person.new
240
+ class << p
241
+ def validate
242
+ expectations do
243
+ be_present self['name'], 'name cannot be blank'
244
+ not_be_present self['age'], 'age must be blank'
245
+ end
246
+ end
247
+ end
248
+
249
+ assert !p.valid?
250
+ assert_equal ['name cannot be blank'], p.errors.full_messages
251
+
252
+ p['name'] = "Jordan"
253
+ p['age'] = 21
254
+
255
+
256
+ assert !p.valid?
257
+ assert_equal ['age must be blank'], p.errors.full_messages
258
+
259
+ p['age'] = nil
260
+
261
+ assert p.valid?
262
+
263
+ end
264
+
265
+ should "be able to use be_a_number expectation" do
266
+ p = Person.new
267
+ class << p
268
+ def validate
269
+ expectations do
270
+ be_a_number self['age'], 'Age is not a number'
271
+ not_be_a_number self['name'], 'Name cannot be a number'
272
+ be_a_number self['birth_year'], 'Birth year is not a number', :allow_nil => true
273
+ end
274
+ end
275
+ end
276
+
277
+ assert !p.valid?
278
+ assert_equal ["Age is not a number"], p.errors.full_messages
279
+
280
+ p['age'] = 21
281
+ p['name'] = 65
282
+
283
+ assert !p.valid?
284
+ assert_equal ["Name cannot be a number"], p.errors.full_messages
285
+
286
+ p['name'] = 'Jordan'
287
+
288
+ assert p.valid?
289
+ end
290
+
291
+ should "be able to use be_match expectation" do
292
+ p = Person.new
293
+ class << p
294
+ def validate
295
+ expectations do
296
+ be_match self['name'], "Name must start with uppercase letter", :with => /[A-Z][a-z]*/
297
+ not_be_match self['nickname'], "Nickname cannot start with uppercase letter", :with => /[A-Z][a-z]*/
298
+ be_match self['age'], "Age must only contain digits", :with => /\d+/, :allow_nil => true
299
+ end
300
+ end
301
+ end
302
+
303
+ assert !p.valid?
304
+ assert_equal ["Name must start with uppercase letter"], p.errors.full_messages
305
+
306
+ p['name'] = 'Jordan'
307
+ p['nickname'] = 'Jordan'
308
+
309
+ assert !p.valid?
310
+ assert_equal ["Nickname cannot start with uppercase letter"], p.errors.full_messages
311
+
312
+ p['nickname'] = 'jordan'
313
+
314
+ assert p.valid?
315
+
316
+ p['age'] = 'asd'
317
+
318
+ assert !p.valid?
319
+ assert_equal ["Age must only contain digits"], p.errors.full_messages
320
+
321
+ p['age'] = '21'
322
+
323
+ assert p.valid?
324
+
325
+ end
326
+
327
+ should "be able to use be_of_length expectation" do
328
+ p = Person.new
329
+ class << p
330
+ def validate
331
+ expectations do
332
+ be_of_length self['name'], "Name must be 3 characters long", :minimum => 3
333
+ be_of_length self['nickname'], "Nickname must not be longer than 5 characters", :maximum => 5
334
+ be_of_length self['computers'], "Can only specify between 1 and 3 computers", :range => 1..3
335
+ be_of_length self['status'], "Status must be a minimum of 1 character", :minumum => 1, :allow_nil => true
336
+ end
337
+ end
338
+ end
339
+
340
+ assert !p.valid?
341
+ assert_equal ["Name must be 3 characters long",
342
+ "Can only specify between 1 and 3 computers"], p.errors.full_messages
343
+
344
+ p['name'] = 'Jordan'
345
+ p['nickname'] = 'Jordan'
346
+
347
+ assert !p.valid?
348
+ assert_equal ["Nickname must not be longer than 5 characters",
349
+ "Can only specify between 1 and 3 computers"], p.errors.full_messages
350
+
351
+ p['nickname'] = 'abc'
352
+ p['computers'] = ['comp_a']
353
+
354
+ assert p.valid?
355
+ end
356
+
357
+ should "raise an error if expectations are called outside of helper block" do
358
+ p = Person.new
359
+ class << p
360
+ def validate
361
+ be_present self['name'], ''
362
+ end
363
+ end
364
+
365
+ assert_raise NoMethodError do
366
+ p.valid?
367
+ end
368
+
369
+ class << p
370
+ def validate
371
+ expectations { }
372
+ be_present
373
+ end
374
+ end
375
+
376
+ assert_raise NameError do
377
+ p.valid?
378
+ end
379
+ end
215
380
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
7
+ - 3
8
8
  - 0
9
- version: 0.2.0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ben Myles
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-12 00:00:00 -07:00
17
+ date: 2010-08-14 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -106,13 +106,19 @@ files:
106
106
  - lib/mongomatic/base.rb
107
107
  - lib/mongomatic/cursor.rb
108
108
  - lib/mongomatic/errors.rb
109
+ - lib/mongomatic/expectations.rb
110
+ - lib/mongomatic/expectations/expected.rb
111
+ - lib/mongomatic/expectations/is_number.rb
112
+ - lib/mongomatic/expectations/match.rb
113
+ - lib/mongomatic/expectations/of_length.rb
114
+ - lib/mongomatic/expectations/present.rb
109
115
  - lib/mongomatic/modifiers.rb
110
116
  - LICENSE
111
117
  - README.rdoc
112
118
  - test/helper.rb
113
119
  - test/test_mongomatic.rb
114
120
  has_rdoc: true
115
- homepage: http://github.com/benmyles/mongomatic
121
+ homepage: http://mongomatic.com/
116
122
  licenses: []
117
123
 
118
124
  post_install_message: