mongomatic 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. data/README.rdoc +90 -26
  2. data/lib/mongomatic/base.rb +20 -11
  3. data/lib/mongomatic/errors.rb +7 -0
  4. data/lib/mongomatic.rb +1 -2
  5. data/test/helper.rb +4 -1
  6. data/test/test_mongomatic.rb +3 -3
  7. metadata +13 -25
  8. data/lib/mongomatic/validatable/child_validation.rb +0 -17
  9. data/lib/mongomatic/validatable/errors.rb +0 -108
  10. data/lib/mongomatic/validatable/included_validation.rb +0 -11
  11. data/lib/mongomatic/validatable/macros.rb +0 -316
  12. data/lib/mongomatic/validatable/object_extension.rb +0 -21
  13. data/lib/mongomatic/validatable/requireable.rb +0 -28
  14. data/lib/mongomatic/validatable/understandable.rb +0 -33
  15. data/lib/mongomatic/validatable/validatable_class_methods.rb +0 -89
  16. data/lib/mongomatic/validatable/validatable_instance_methods.rb +0 -111
  17. data/lib/mongomatic/validatable/validations/validates_acceptance_of.rb +0 -16
  18. data/lib/mongomatic/validatable/validations/validates_associated.rb +0 -15
  19. data/lib/mongomatic/validatable/validations/validates_confirmation_of.rb +0 -25
  20. data/lib/mongomatic/validatable/validations/validates_each.rb +0 -16
  21. data/lib/mongomatic/validatable/validations/validates_exclusion_of.rb +0 -19
  22. data/lib/mongomatic/validatable/validations/validates_format_of.rb +0 -18
  23. data/lib/mongomatic/validatable/validations/validates_inclusion_of.rb +0 -19
  24. data/lib/mongomatic/validatable/validations/validates_length_of.rb +0 -32
  25. data/lib/mongomatic/validatable/validations/validates_numericality_of.rb +0 -28
  26. data/lib/mongomatic/validatable/validations/validates_presence_of.rb +0 -18
  27. data/lib/mongomatic/validatable/validations/validates_true_for.rb +0 -15
  28. data/lib/mongomatic/validatable/validations/validation_base.rb +0 -93
  29. data/lib/mongomatic/validatable.rb +0 -31
data/README.rdoc CHANGED
@@ -2,12 +2,23 @@
2
2
 
3
3
  Mongomatic allows you to map your Ruby objects to Mongo documents. It is designed to be fast and simple.
4
4
 
5
+ == What's different about Mongomatic?
6
+
7
+ * Follows Mongo idioms wherever possible.
8
+ * Only strives to do "just enough" and never too much.
9
+ * When possible, we defer to Mongo. For example, there's no Mongomatic query API, instead you use the same query hash syntax you would with the Ruby Mongo driver.
10
+ * No complex relationship management: if you want to model relationships then add your own finder methods.
11
+ * Minimal dependencies.
12
+
5
13
  == Basic Usage
6
14
 
7
15
  require 'mongomatic'
8
16
 
9
17
  class User < Mongomatic::Base
10
- validates_presence_of :name, :email
18
+ def validate
19
+ self.errors << ["Name", "can't be empty"] if self["name"].blank?
20
+ self.errors << ["Email", "can't be empty"] if self["email"].blank?
21
+ end
11
22
  end
12
23
 
13
24
  # set the db for all models:
@@ -24,42 +35,33 @@ Mongomatic allows you to map your Ruby objects to Mongo documents. It is designe
24
35
  => true
25
36
  u.insert
26
37
  => BSON::ObjectID('4c32834f0218236321000001')
27
- u
28
- => #<User:0x00000100d0cbf8 @doc={"name"=>"Ben", "email"=>"me@somewhere.com", "_id"=>BSON::ObjectID('4c32834f0218236321000001')}, @removed=false, @validation_context=nil, @errors={}, @_initialized_validate_callbacks=true>
38
+
29
39
  u["name"] = "Ben Myles"
30
40
  => "Ben Myles"
31
41
  u.update
32
- => 142
42
+ => 137
43
+
44
+ found = User.find_one({"name" => "Ben Myles"})
45
+ => #<User:0x00000101939a48 @doc={"_id"=>BSON::ObjectID('4c32834f0218236321000001'), "name"=>"Ben Myles", "email"=>"me@somewhere.com"}, @removed=false, @is_new=false, @errors=[]>
46
+ User.find_one(BSON::ObjectID('4c32834f0218236321000001')) == found
47
+ => true
33
48
 
34
49
  cursor = User.find({"name" => "Ben Myles"})
35
- => #<Mongomatic::Cursor:0x00000100cf5110 @obj_class=User, @mongo_cursor=DBResponse(flags=, cursor_id=, start=)> cursor.count
36
- cursor.count
37
- => 1
50
+ => #<Mongomatic::Cursor:0x0000010195b4e0 @obj_class=User, @mongo_cursor=<Mongo::Cursor:0x80cadac0 namespace='mongomatic_test.User' @selector={"name"=>"Ben Myles"}>>
38
51
  found = cursor.next
39
- => #<User:0x00000100ccd408 @doc={"_id"=>BSON::ObjectID('4c32c3720218236526000001'), "name"=>"Ben Myles", "email"=>"me@somewhere.com"}, @removed=false>
52
+ => #<User:0x00000101939a48 @doc={"_id"=>BSON::ObjectID('4c32834f0218236321000001'), "name"=>"Ben Myles", "email"=>"me@somewhere.com"}, @removed=false, @is_new=false, @errors=[]>
40
53
  found.remove
41
- => 72
42
- found
43
- => #<User:0x00000101091f80 @doc={"_id"=>BSON::ObjectID('4c32c4480218236538000001'), "name"=>"Ben Myles", "email"=>"me@somewhere.com"}, @removed=true>
44
- cursor = User.find({"name" => "Ben Myles"})
45
- => #<Mongomatic::Cursor:0x00000100d9eb20 @obj_class=User, @mongo_cursor=DBResponse(flags=, cursor_id=, start=)>
46
- cursor.count
47
- => 0
48
- cursor.next
54
+ => 67
55
+ User.count
56
+ => 0
57
+ User.find({"name" => "Ben Myles"}).next
49
58
  => nil
50
-
51
- found = User.find_one({"name" => "Ben Myles"})
52
- => #<User:0x00000100ccd408 @doc={"_id"=>BSON::ObjectID('4c32c3720218236526000001'), "name"=>"Ben Myles", "email"=>"me@somewhere.com"}, @removed=false>
53
- found = User.find_one(BSON::ObjectID('4c32c3720218236526000001'))
54
- => #<User:0x00000100ccd408 @doc={"_id"=>BSON::ObjectID('4c32c3720218236526000001'), "name"=>"Ben Myles", "email"=>"me@somewhere.com"}, @removed=false>
55
-
59
+
56
60
  == Indexes
57
61
 
58
62
  Mongomatic doesn't do anything special to support indexes, but here's the suggested convention:
59
63
 
60
- class Person < Mongomatic::Base
61
- validates_presence_of :name, :email
62
-
64
+ class Person < Mongomatic::Base
63
65
  class << self
64
66
  def create_indexes
65
67
  collection.create_index("email", :unique => true)
@@ -69,7 +71,69 @@ Mongomatic doesn't do anything special to support indexes, but here's the sugges
69
71
 
70
72
  You can run Person.create_indexes whenever you add new indexes, it won't throw an error if they already exist.
71
73
 
72
- If you have defined a unique index and want Mongomatic to raise an exception on a duplicate insert you need to use insert_safe or update_safe. The error thrown will be Mongo::OperationFailure. See the test suite for examples.
74
+ If you have defined a unique index and want Mongomatic to raise an exception on a duplicate insert you need to use insert! or update!. The error thrown will be Mongo::OperationFailure. See the test suite for examples.
75
+
76
+ == Validations
77
+
78
+ You can add validations to your model by creating a validate method. If your validate method pushes anything into the self.errors object your model will fail to validate, otherwise if self.errors remains empty the validations will be taken to have passed.
79
+
80
+ class Person < Mongomatic::Base
81
+ def validate
82
+ self.errors << ["name", "blank"] if self["name"].blank?
83
+ self.errors << ["email", "blank"] if self["email"].blank?
84
+ self.errors << ["address.zip", "blank"] if (self["address"] || {})["zip"].blank?
85
+ end
86
+ end
87
+
88
+ p = Person.new
89
+ => #<Person:0x000001018c2d58 @doc={}, @removed=false, @is_new=true, @errors=[]>
90
+ p.valid?
91
+ => false
92
+ p.errors
93
+ => [["name", "blank"], ["email", "blank"], ["address.zip", "blank"]]
94
+ p.errors.full_messages
95
+ => ["name blank", "email blank", "address.zip blank"]
96
+ p["name"] = "Ben"
97
+ p["email"] = "Myles"
98
+ p["address"] = { "zip" => 94107 }
99
+ p.valid?
100
+ => true
101
+
102
+ == Relationships
103
+
104
+ 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:
105
+
106
+ class User < Mongomatic::Base
107
+ class << self
108
+ def create_indexes
109
+ self.collection.create_index("following_ids")
110
+ end
111
+ end
112
+
113
+ def follow(user)
114
+ self.push("following_ids", user["_id"])
115
+ end
116
+
117
+ def unfollow(user)
118
+ self.pull("following_ids", user["_id"])
119
+ end
120
+
121
+ def following
122
+ return nil if new?
123
+ self.class.find( { "_id" => { "$in" => (self["following_ids"] || []) } } )
124
+ end
125
+
126
+ def followers
127
+ return nil if new?
128
+ self.class.find( { "following_ids" => self["_id"] } )
129
+ end
130
+
131
+ def friends
132
+ return nil if new?
133
+ self.class.find( { "following_ids" => self["_id"],
134
+ "_id" => { "$in" => (self["following_ids"] || []) } } )
135
+ end
136
+ end
73
137
 
74
138
  == Note on Patches/Pull Requests
75
139
 
@@ -1,7 +1,6 @@
1
1
  module Mongomatic
2
2
  class Base
3
3
  include Mongomatic::Modifiers
4
- include Mongomatic::Validatable
5
4
 
6
5
  class << self
7
6
  def db
@@ -48,12 +47,25 @@ module Mongomatic
48
47
  end
49
48
  end
50
49
 
51
- attr_accessor :removed, :is_new
50
+ attr_accessor :removed, :is_new, :errors
52
51
 
53
52
  def initialize(doc={}, is_new=true)
54
53
  @doc = doc.stringify_keys
55
54
  self.removed = false
56
55
  self.is_new = is_new
56
+ self.errors = Mongomatic::Errors.new
57
+ end
58
+
59
+ def validate
60
+ true
61
+ end
62
+
63
+ def valid?
64
+ self.errors = Mongomatic::Errors.new
65
+ self.send(:before_validate) if self.respond_to?(:before_validate)
66
+ validate
67
+ self.send(:after_validate) if self.respond_to?(:after_validate)
68
+ self.errors.empty?
57
69
  end
58
70
 
59
71
  def is_new?
@@ -103,9 +115,8 @@ module Mongomatic
103
115
  ret
104
116
  end
105
117
 
106
- def insert_safe(opts={})
107
- opts.merge!(:safe => true)
108
- insert(opts)
118
+ def insert!(opts={})
119
+ insert(opts.merge(:safe => true))
109
120
  end
110
121
 
111
122
  def update(opts={},update_doc=@doc)
@@ -118,9 +129,8 @@ module Mongomatic
118
129
  ret
119
130
  end
120
131
 
121
- def update_safe(opts={},update_doc=@doc)
122
- opts.merge!(:safe => true)
123
- update(opts,update_doc)
132
+ def update!(opts={},update_doc=@doc)
133
+ update(opts.merge(:safe => true),update_doc)
124
134
  end
125
135
 
126
136
  def remove(opts={})
@@ -133,9 +143,8 @@ module Mongomatic
133
143
  ret
134
144
  end
135
145
 
136
- def remove_safe(opts={})
137
- opts.merge!(:safe => true)
138
- remove(opts)
146
+ def remove!(opts={})
147
+ remove(opts.merge(:safe => true))
139
148
  end
140
149
 
141
150
  def to_hash
@@ -0,0 +1,7 @@
1
+ module Mongomatic
2
+ class Errors < Array
3
+ def full_messages(sep=" ")
4
+ collect { |e| e.join(sep) }
5
+ end
6
+ end
7
+ end
data/lib/mongomatic.rb CHANGED
@@ -27,8 +27,7 @@ module Mongomatic
27
27
  end
28
28
  end
29
29
 
30
- require "#{File.dirname(__FILE__)}/mongomatic/validatable"
31
-
32
30
  require "#{File.dirname(__FILE__)}/mongomatic/cursor"
33
31
  require "#{File.dirname(__FILE__)}/mongomatic/modifiers"
32
+ require "#{File.dirname(__FILE__)}/mongomatic/errors"
34
33
  require "#{File.dirname(__FILE__)}/mongomatic/base"
data/test/helper.rb CHANGED
@@ -10,13 +10,16 @@ require 'mongomatic'
10
10
  Mongomatic.db = Mongo::Connection.new.db("mongomatic_test")
11
11
 
12
12
  class Person < Mongomatic::Base
13
- validates_presence_of :name
14
13
  attr_accessor :callback_tests
15
14
 
16
15
  def self.create_indexes
17
16
  collection.create_index("name", :unique => true)
18
17
  end
19
18
 
19
+ def validate
20
+ self.errors << ["Name", "can't be empty"] if self["name"].blank?
21
+ end
22
+
20
23
  def before_validate
21
24
  self.callback_tests ||= []
22
25
  self.callback_tests << :before_validate
@@ -22,7 +22,7 @@ class TestMongomatic < Test::Unit::TestCase
22
22
  Person.create_indexes
23
23
 
24
24
  p = Person.new(:name => "Ben1", :birth_year => 1984, :created_at => Time.now.utc, :admin => true)
25
- assert p.insert_safe.is_a?(BSON::ObjectID)
25
+ assert p.insert!.is_a?(BSON::ObjectID)
26
26
  assert_equal 1, Person.count
27
27
 
28
28
  found = Person.find({"_id" => BSON::ObjectID(p["_id"].to_s)}).next
@@ -175,11 +175,11 @@ class TestMongomatic < Test::Unit::TestCase
175
175
  Person.create_indexes
176
176
 
177
177
  p = Person.new(:name => "Ben1", :birth_year => 1984, :created_at => Time.now.utc, :admin => true)
178
- assert p.insert_safe.is_a?(BSON::ObjectID)
178
+ assert p.insert!.is_a?(BSON::ObjectID)
179
179
  assert_equal 1, Person.count
180
180
 
181
181
  p = Person.new(:name => "Ben1", :birth_year => 1984, :created_at => Time.now.utc, :admin => true)
182
- assert_raise(Mongo::OperationFailure) { p.insert_safe }
182
+ assert_raise(Mongo::OperationFailure) { p.insert! }
183
183
 
184
184
  assert_equal 1, Person.count
185
185
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 3
9
- version: 0.1.3
8
+ - 4
9
+ version: 0.1.4
10
10
  platform: ruby
11
11
  authors:
12
12
  - Ben Myles
@@ -21,6 +21,7 @@ dependencies:
21
21
  name: shoulda
22
22
  prerelease: false
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
24
25
  requirements:
25
26
  - - ">="
26
27
  - !ruby/object:Gem::Version
@@ -35,6 +36,7 @@ dependencies:
35
36
  name: bson
36
37
  prerelease: false
37
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
38
40
  requirements:
39
41
  - - ">="
40
42
  - !ruby/object:Gem::Version
@@ -49,6 +51,7 @@ dependencies:
49
51
  name: bson_ext
50
52
  prerelease: false
51
53
  requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
52
55
  requirements:
53
56
  - - ">="
54
57
  - !ruby/object:Gem::Version
@@ -63,6 +66,7 @@ dependencies:
63
66
  name: mongo
64
67
  prerelease: false
65
68
  requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
66
70
  requirements:
67
71
  - - ">="
68
72
  - !ruby/object:Gem::Version
@@ -77,6 +81,7 @@ dependencies:
77
81
  name: activesupport
78
82
  prerelease: false
79
83
  requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
80
85
  requirements:
81
86
  - - ">="
82
87
  - !ruby/object:Gem::Version
@@ -100,31 +105,12 @@ files:
100
105
  - lib/mongomatic.rb
101
106
  - lib/mongomatic/base.rb
102
107
  - lib/mongomatic/cursor.rb
108
+ - lib/mongomatic/errors.rb
103
109
  - lib/mongomatic/modifiers.rb
104
- - lib/mongomatic/validatable.rb
105
- - lib/mongomatic/validatable/child_validation.rb
106
- - lib/mongomatic/validatable/errors.rb
107
- - lib/mongomatic/validatable/included_validation.rb
108
- - lib/mongomatic/validatable/macros.rb
109
- - lib/mongomatic/validatable/object_extension.rb
110
- - lib/mongomatic/validatable/requireable.rb
111
- - lib/mongomatic/validatable/understandable.rb
112
- - lib/mongomatic/validatable/validatable_class_methods.rb
113
- - lib/mongomatic/validatable/validatable_instance_methods.rb
114
- - lib/mongomatic/validatable/validations/validates_acceptance_of.rb
115
- - lib/mongomatic/validatable/validations/validates_associated.rb
116
- - lib/mongomatic/validatable/validations/validates_confirmation_of.rb
117
- - lib/mongomatic/validatable/validations/validates_each.rb
118
- - lib/mongomatic/validatable/validations/validates_exclusion_of.rb
119
- - lib/mongomatic/validatable/validations/validates_format_of.rb
120
- - lib/mongomatic/validatable/validations/validates_inclusion_of.rb
121
- - lib/mongomatic/validatable/validations/validates_length_of.rb
122
- - lib/mongomatic/validatable/validations/validates_numericality_of.rb
123
- - lib/mongomatic/validatable/validations/validates_presence_of.rb
124
- - lib/mongomatic/validatable/validations/validates_true_for.rb
125
- - lib/mongomatic/validatable/validations/validation_base.rb
126
110
  - LICENSE
127
111
  - README.rdoc
112
+ - test/helper.rb
113
+ - test/test_mongomatic.rb
128
114
  has_rdoc: true
129
115
  homepage: http://github.com/benmyles/mongomatic
130
116
  licenses: []
@@ -135,6 +121,7 @@ rdoc_options:
135
121
  require_paths:
136
122
  - lib
137
123
  required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
138
125
  requirements:
139
126
  - - ">="
140
127
  - !ruby/object:Gem::Version
@@ -142,6 +129,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
129
  - 0
143
130
  version: "0"
144
131
  required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
145
133
  requirements:
146
134
  - - ">="
147
135
  - !ruby/object:Gem::Version
@@ -151,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
139
  requirements: []
152
140
 
153
141
  rubyforge_project:
154
- rubygems_version: 1.3.6
142
+ rubygems_version: 1.3.7
155
143
  signing_key:
156
144
  specification_version: 3
157
145
  summary: Mongomatic is a simple Ruby object mapper for Mongo
@@ -1,17 +0,0 @@
1
- module Mongomatic
2
- module Validatable
3
- class ChildValidation #:nodoc:
4
- attr_accessor :attribute, :map, :should_validate_proc
5
-
6
- def initialize(attribute, map, should_validate_proc)
7
- @attribute = attribute
8
- @map = map
9
- @should_validate_proc = should_validate_proc
10
- end
11
-
12
- def should_validate?(instance)
13
- instance.instance_eval &should_validate_proc
14
- end
15
- end
16
- end
17
- end
@@ -1,108 +0,0 @@
1
- module Mongomatic
2
- module Validatable
3
- class Errors
4
- extend Forwardable
5
- include Enumerable
6
-
7
- def_delegators :errors, :clear, :each, :each_pair, :empty?, :length, :size
8
-
9
- # Returns true if the specified +attribute+ has errors associated with it.
10
- #
11
- # class Company < ActiveRecord::Base
12
- # validates_presence_of :name, :address, :email
13
- # validates_length_of :name, :in => 5..30
14
- # end
15
- #
16
- # company = Company.create(:address => '123 First St.')
17
- # company.errors.invalid?(:name) # => true
18
- # company.errors.invalid?(:address) # => false
19
- def invalid?(attribute)
20
- !@errors[attribute.to_sym].nil?
21
- end
22
-
23
- # Adds an error to the base object instead of any particular attribute. This is used
24
- # to report errors that don't tie to any specific attribute, but rather to the object
25
- # as a whole. These error messages don't get prepended with any field name when iterating
26
- # with +each_full+, so they should be complete sentences.
27
- def add_to_base(msg)
28
- add(:base, msg)
29
- end
30
-
31
- # Returns errors assigned to the base object through +add_to_base+ according to the normal rules of <tt>on(attribute)</tt>.
32
- def on_base
33
- on(:base)
34
- end
35
-
36
- # call-seq: on(attribute)
37
- #
38
- # * Returns nil, if no errors are associated with the specified +attribute+.
39
- # * Returns the error message, if one error is associated with the specified +attribute+.
40
- # * Returns an array of error messages, if more than one error is associated with the specified +attribute+.
41
- def on(attribute)
42
- return nil if errors[attribute.to_sym].nil?
43
- errors[attribute.to_sym].size == 1 ? errors[attribute.to_sym].first : errors[attribute.to_sym]
44
- end
45
-
46
- # Rails 3 API for errors, always return array.
47
- def [](attribute)
48
- errors[attribute.to_sym] || []
49
- end
50
-
51
- def add(attribute, message) #:nodoc:
52
- errors[attribute.to_sym] = [] if errors[attribute.to_sym].nil?
53
- errors[attribute.to_sym] << message
54
- end
55
-
56
- def merge!(errors) #:nodoc:
57
- errors.each_pair{|k, v| add(k,v)}
58
- self
59
- end
60
-
61
- # call-seq: replace(attribute)
62
- #
63
- # * Replaces the errors value for the given +attribute+
64
- def replace(attribute, value)
65
- errors[attribute.to_sym] = value
66
- end
67
-
68
- # call-seq: raw(attribute)
69
- #
70
- # * Returns an array of error messages associated with the specified +attribute+.
71
- def raw(attribute)
72
- errors[attribute.to_sym]
73
- end
74
-
75
- def errors #:nodoc:
76
- @errors ||= {}
77
- end
78
-
79
- def count #:nodoc:
80
- errors.values.flatten.size
81
- end
82
-
83
- # call-seq: full_messages -> an_array_of_messages
84
- #
85
- # Returns an array containing the full list of error messages.
86
- def full_messages
87
- full_messages = []
88
-
89
- errors.each_key do |attribute|
90
- errors[attribute].each do |msg|
91
- next if msg.nil?
92
-
93
- if attribute.to_s == "base"
94
- full_messages << msg
95
- else
96
- full_messages << humanize(attribute.to_s) + " " + msg
97
- end
98
- end
99
- end
100
- full_messages
101
- end
102
-
103
- def humanize(lower_case_and_underscored_word) #:nodoc:
104
- lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
105
- end
106
- end
107
- end
108
- end
@@ -1,11 +0,0 @@
1
- module Mongomatic
2
- module Validatable
3
- class IncludedValidation #:nodoc:
4
- attr_accessor :attribute
5
-
6
- def initialize(attribute)
7
- @attribute = attribute
8
- end
9
- end
10
- end
11
- end