Class: Mongomatic::Base

Inherits:
Object
  • Object
show all
Includes:
ActiveModelCompliancy, Modifiers, TypedFields, Util
Defined in:
lib/mongomatic/base.rb

Direct Known Subclasses

TransactionLock

Constant Summary

Constants included from TypedFields

TypedFields::KNOWN_TYPES

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TypedFields

included

Methods included from ActiveModelCompliancy

#destroyed?, #new_record?, #persisted?, #to_key, #to_model, #to_param

Methods included from Util

#create_array

Methods included from Modifiers

#add_to_set, #add_to_set!, #inc, #inc!, #pop_first, #pop_first!, #pop_last, #pop_last!, #pull, #pull!, #pull_all, #pull_all!, #push, #push!, #push_all, #push_all!, #set, #set!, #unset, #unset!

Constructor Details

#initialize(doc_hash = Mongomatic::MHash.new, is_new = true) ⇒ Base

Returns a new instance of Base.



82
83
84
85
86
87
88
# File 'lib/mongomatic/base.rb', line 82

def initialize(doc_hash=Mongomatic::MHash.new, is_new=true)
  self.doc = doc_hash
  self.removed = false
  self.is_new  = is_new
  self.errors  = Mongomatic::Errors.new
  do_callback(:after_initialize)
end

Instance Attribute Details

#errorsObject

Returns the value of attribute errors.



80
81
82
# File 'lib/mongomatic/base.rb', line 80

def errors
  @errors
end

#is_newObject

Returns the value of attribute is_new.



80
81
82
# File 'lib/mongomatic/base.rb', line 80

def is_new
  @is_new
end

#removedObject

Returns the value of attribute removed.



80
81
82
# File 'lib/mongomatic/base.rb', line 80

def removed
  @removed
end

Class Method Details

.allObject

Return a Mongomatic::Cursor instance of all documents in the collection.



44
45
46
# File 'lib/mongomatic/base.rb', line 44

def all
  find
end

.collectionObject

Return the raw MongoDB collection for this model



28
29
30
# File 'lib/mongomatic/base.rb', line 28

def collection
  @collection ||= self.db.collection(self.collection_name)
end

.collection_nameObject

Override this method on your model if you want to use a different collection name



23
24
25
# File 'lib/mongomatic/base.rb', line 23

def collection_name
  self.to_s
end

.countObject

Return the number of documents in the collection



64
65
66
# File 'lib/mongomatic/base.rb', line 64

def count
  find.count
end

.dbObject

Returns this models own db attribute if set, otherwise will return Mongomatic.db



10
11
12
# File 'lib/mongomatic/base.rb', line 10

def db
  @db || Mongomatic.db || raise(ArgumentError, "No db supplied")
end

.db=(obj) ⇒ Object

Override Mongomatic.db with a Mongo::DB instance for this model specifically

MyModel.db = Mongo::Connection.new().db('mydb_mymodel')


16
17
18
19
20
# File 'lib/mongomatic/base.rb', line 16

def db=(obj)
  unless obj.is_a?(Mongo::DB)
    raise(ArgumentError, "Must supply a Mongo::DB object")
  end; @db = obj
end

.do_callback(meth) ⇒ Object



74
75
76
77
# File 'lib/mongomatic/base.rb', line 74

def do_callback(meth)
  return false unless respond_to?(meth, true)
  send(meth)
end

.dropObject



68
69
70
71
72
# File 'lib/mongomatic/base.rb', line 68

def drop
  do_callback(:before_drop)
  collection.drop
  do_callback(:after_drop)
end

.eachObject

Iterate over all documents in the collection (uses a Mongomatic::Cursor)



49
50
51
# File 'lib/mongomatic/base.rb', line 49

def each
  find.each { |found| yield(found) }
end

.empty?Boolean

Is the collection empty? This method is much more efficient than doing Collection.count == 0

Returns:

  • (Boolean)


59
60
61
# File 'lib/mongomatic/base.rb', line 59

def empty?
  find.limit(1).has_next? == false
end

.find(query = {}, opts = {}) ⇒ Object

Query MongoDB for documents. Same arguments as api.mongodb.org/ruby/current/Mongo/Collection.html#find-instance_method



33
34
35
# File 'lib/mongomatic/base.rb', line 33

def find(query={}, opts={})
  Mongomatic::Cursor.new(self, collection.find(query, opts))
end

.find_one(query = {}, opts = {}) ⇒ Object

Query MongoDB and return one document only. Same arguments as api.mongodb.org/ruby/current/Mongo/Collection.html#find_one-instance_method



38
39
40
41
# File 'lib/mongomatic/base.rb', line 38

def find_one(query={}, opts={})
  return nil unless doc = self.collection.find_one(query, opts)
  self.new(doc, false)
end

.firstObject

Return the first document in the collection



54
55
56
# File 'lib/mongomatic/base.rb', line 54

def first
  find.limit(1).next_document
end

Instance Method Details

#==(obj) ⇒ Object

Check equality with another Mongomatic document



179
180
181
# File 'lib/mongomatic/base.rb', line 179

def ==(obj)
  obj.is_a?(self.class) && obj.doc["_id"] == @doc["_id"]
end

#[](k) ⇒ Object

Fetch a field (just like a hash):

mydoc["name"]
 => "Ben"


163
164
165
# File 'lib/mongomatic/base.rb', line 163

def [](k)
  @doc[k.to_s]
end

#[]=(k, v) ⇒ Object

Set a field on this document:

mydoc["name"] = "Ben"
mydoc["address"] = { "city" => "San Francisco" }


129
130
131
# File 'lib/mongomatic/base.rb', line 129

def []=(k,v)
  @doc[k.to_s] = v
end

#delete(key) ⇒ Object

Same as Hash#delete

mydoc.delete(“name”)

=> "Ben"

mydoc.has_hey?(“name”)

=> false


156
157
158
# File 'lib/mongomatic/base.rb', line 156

def delete(key)
  @doc.delete(key)
end

#do_callback(meth) ⇒ Object



306
307
308
309
310
311
# File 'lib/mongomatic/base.rb', line 306

def do_callback(meth)
  notify(meth) if self.class.included_modules.include?(Mongomatic::Observable) # TODO entire block is smelly, doesnt belong here 
  
  return false unless respond_to?(meth, true)
  send(meth)
end

#docObject



95
96
97
# File 'lib/mongomatic/base.rb', line 95

def doc
  @doc
end

#doc=(hash) ⇒ Object



90
91
92
93
# File 'lib/mongomatic/base.rb', line 90

def doc=(hash)
  hash = Mongomatic::MHash.new(hash) unless hash.is_a?(Mongomatic::MHash)
  @doc = hash
end

#has_key?(key) ⇒ Boolean

Returns true if document contains key

Returns:

  • (Boolean)


134
135
136
137
# File 'lib/mongomatic/base.rb', line 134

def has_key?(key)
  field, hash = hash_for_field(key.to_s, true)
  hash.has_key?(field)
end

#hash_for_field(field, break_if_dne = false) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/mongomatic/base.rb', line 293

def hash_for_field(field, break_if_dne=false)
  parts = field.split(".")
  curr_hash = self.doc
  return [parts[0], curr_hash] if parts.size == 1
  field = parts.pop # last one is the field
  parts.each_with_index do |part, i|
    return [part, curr_hash] if break_if_dne && !curr_hash.has_key?(part)
    curr_hash[part] ||= {}
    return [field, curr_hash[part]] if parts.size == i+1
    curr_hash = curr_hash[part]
  end
end

#insert(opts = {}) ⇒ Object

Insert the document into the database. Will return false if the document has already been inserted or is invalid. Returns the generated BSON::ObjectId for the new document. Will silently fail if MongoDB is unable to insert the document, use insert! or send in => true if you want a Mongo::OperationError. If you want to raise the following errors also, pass in => true

* Raises Mongomatic::Exceptions::DocumentNotNew if document is not new
* Raises Mongomatic::Exceptions::DocumentNotValid if there are validation errors


197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/mongomatic/base.rb', line 197

def insert(opts={})
  if opts[:raise] == true
    raise Mongomatic::Exceptions::DocumentWasRemoved if removed?
    raise Mongomatic::Exceptions::DocumentNotNew unless new?
    raise Mongomatic::Exceptions::DocumentNotValid unless valid?
  else
    return false unless new? && valid?
  end

  do_callback(:before_insert)
  do_callback(:before_insert_or_update)
  if ret = self.class.collection.insert(@doc,opts)
    @doc["_id"] = @doc.delete(:_id) if @doc[:_id]
    self.is_new = false
  end
  do_callback(:after_insert)
  do_callback(:after_insert_or_update)
  ret
end

#insert!(opts = {}) ⇒ Object

Calls insert(…) with => true passed in as an option.

* Raises Mongo::OperationError if there was a DB error on inserting

If you want to raise the following errors also, pass in => true

* Raises Mongomatic::Exceptions::DocumentNotNew if document is not new
* Raises Mongomatic::Exceptions::DocumentNotValid if there are validation errors


222
223
224
# File 'lib/mongomatic/base.rb', line 222

def insert!(opts={})
  insert(opts.merge(:safe => true))
end

#is_new?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/mongomatic/base.rb', line 118

def is_new?
  self.is_new == true
end

#merge(hash) ⇒ Object

Merge this document with the supplied hash. Useful for updates:

mydoc.merge(params[:user])


169
170
171
# File 'lib/mongomatic/base.rb', line 169

def merge(hash)
  hash.each { |k,v| self[k] = v }; @doc
end

#new?Boolean

Returns:

  • (Boolean)


122
123
124
# File 'lib/mongomatic/base.rb', line 122

def new?
  self.is_new == true
end

#reloadObject

Reload the document from the database



184
185
186
187
188
# File 'lib/mongomatic/base.rb', line 184

def reload
  if obj = self.class.find({"_id" => @doc["_id"]}).next_document
    self.doc = obj.doc; true
  end
end

#remove(opts = {}) ⇒ Object

Remove this document from the collection. Silently fails on db error, use remove! or pass in => true if you want an exception raised. If you want to raise the following errors also, pass in => true

* Raises Mongomatic::Exceptions::DocumentIsNew if document is new
* Raises Mongomatic::Exceptions::DocumentWasRemoved if document has been already removed


264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/mongomatic/base.rb', line 264

def remove(opts={})
  if opts[:raise] == true
    raise Mongomatic::Exceptions::DocumentWasRemoved if removed?
    raise Mongomatic::Exceptions::DocumentIsNew      if new?
  else
    return false if new? || removed?
  end
  do_callback(:before_remove)
  if ret = self.class.collection.remove({"_id" => @doc["_id"]})
    self.removed = true; freeze; ret
  end
  do_callback(:after_remove)
  ret
end

#remove!(opts = {}) ⇒ Object

Calls remove(…) with => true passed in as an option.

* Raises Mongo::OperationError if there was a DB error on removing

If you want to raise the following errors also, pass in => true

* Raises Mongomatic::Exceptions::DocumentIsNew if document is new
* Raises Mongomatic::Exceptions::DocumentWasRemoved if document has been already removed


284
285
286
# File 'lib/mongomatic/base.rb', line 284

def remove!(opts={})
  remove(opts.merge(:safe => true))
end

#removed?Boolean

Will return true if the document has been removed.

Returns:

  • (Boolean)


174
175
176
# File 'lib/mongomatic/base.rb', line 174

def removed?
  self.removed == true
end

#set_value_for_key(key, value) ⇒ Object



139
140
141
142
# File 'lib/mongomatic/base.rb', line 139

def set_value_for_key(key, value)
  field, hash = hash_for_field(key.to_s)
  hash[field] = value
end

#to_hashObject

Return this document as a hash.



289
290
291
# File 'lib/mongomatic/base.rb', line 289

def to_hash
  @doc || {}
end

#transaction(key = nil, duration = 5, &block) ⇒ Object



313
314
315
316
317
# File 'lib/mongomatic/base.rb', line 313

def transaction(key=nil, duration=5, &block)
  raise Mongomatic::Exceptions::DocumentIsNew if new?
  key ||= [self.class.name, self["_id"].to_s].join("-")
  TransactionLock.start(key, duration, &block)
end

#update(opts = {}, update_doc = @doc) ⇒ Object

Will persist any changes you have made to the document. Silently fails on db update error. Use update! or pass in => true to raise a Mongo::OperationError if that’s what you want. If you want to raise the following errors also, pass in => true

* Raises Mongomatic::Exceptions::DocumentIsNew if document is new
* Raises Mongomatic::Exceptions::DocumentNotValid if there are validation errors
* Raises Mongomatic::Exceptions::DocumentWasRemoved if document has been removed


233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/mongomatic/base.rb', line 233

def update(opts={},update_doc=@doc)
  if opts[:raise] == true
    raise Mongomatic::Exceptions::DocumentWasRemoved if removed?
    raise Mongomatic::Exceptions::DocumentIsNew      if new?
    raise Mongomatic::Exceptions::DocumentNotValid   unless valid?
  else
    return false if new? || removed? || !valid?
  end
  do_callback(:before_update)
  do_callback(:before_insert_or_update)
  ret = self.class.collection.update({"_id" => @doc["_id"]}, update_doc, opts)
  do_callback(:after_update)
  do_callback(:after_insert_or_update)
  ret
end

#update!(opts = {}, update_doc = @doc) ⇒ Object

Calls update(…) with => true passed in as an option.

* Raises Mongo::OperationError if there was a DB error on updating

If you want to raise the following errors also, pass in => true

* Raises Mongomatic::Exceptions::DocumentIsNew if document is new
* Raises Mongomatic::Exceptions::DocumentNotValid if there are validation errors
* Raises Mongomatic::Exceptions::DocumentWasRemoved if document has been removed


255
256
257
# File 'lib/mongomatic/base.rb', line 255

def update!(opts={},update_doc=@doc)
  update(opts.merge(:safe => true),update_doc)
end

#valid?Boolean

Returns:

  • (Boolean)


109
110
111
112
113
114
115
116
# File 'lib/mongomatic/base.rb', line 109

def valid?
  check_typed_fields!
  self.errors = Mongomatic::Errors.new
  do_callback(:before_validate)
  validate
  do_callback(:after_validate)
  self.errors.empty?
end

#validateObject

Override this with your own validate() method for validations. Simply push your errors into the self.errors property and if self.errors remains empty your document will be valid.

def validate
  self.errors.add "name", "cannot be blank"
end


105
106
107
# File 'lib/mongomatic/base.rb', line 105

def validate
  true
end

#value_for_key(key) ⇒ Object



144
145
146
147
# File 'lib/mongomatic/base.rb', line 144

def value_for_key(key)
  field, hash = hash_for_field(key.to_s, true)
  hash[field]
end