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.



91
92
93
94
95
96
97
# File 'lib/mongomatic/base.rb', line 91

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.



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

def errors
  @errors
end

#is_newObject

Returns the value of attribute is_new.



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

def is_new
  @is_new
end

#removedObject

Returns the value of attribute removed.



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

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.tableize
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

.insert(doc_hash, opts = {}) ⇒ Object



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

def insert(doc_hash, opts={})
  d = new(doc_hash)
  d.insert(opts)
end

.insert!(doc_hash, opts = {}) ⇒ Object



84
85
86
# File 'lib/mongomatic/base.rb', line 84

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

Instance Method Details

#==(obj) ⇒ Object

Check equality with another Mongomatic document



188
189
190
# File 'lib/mongomatic/base.rb', line 188

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"


172
173
174
# File 'lib/mongomatic/base.rb', line 172

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

#[]=(k, v) ⇒ Object

Set a field on this document:

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


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

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


165
166
167
# File 'lib/mongomatic/base.rb', line 165

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

#do_callback(meth) ⇒ Object



315
316
317
318
319
320
# File 'lib/mongomatic/base.rb', line 315

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



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

def doc
  @doc
end

#doc=(hash) ⇒ Object



99
100
101
102
# File 'lib/mongomatic/base.rb', line 99

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)


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

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



302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/mongomatic/base.rb', line 302

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


206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/mongomatic/base.rb', line 206

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


231
232
233
# File 'lib/mongomatic/base.rb', line 231

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

#is_new?Boolean

Returns:

  • (Boolean)


127
128
129
# File 'lib/mongomatic/base.rb', line 127

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])


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

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

#new?Boolean

Returns:

  • (Boolean)


131
132
133
# File 'lib/mongomatic/base.rb', line 131

def new?
  self.is_new == true
end

#reloadObject

Reload the document from the database



193
194
195
196
197
# File 'lib/mongomatic/base.rb', line 193

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


273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/mongomatic/base.rb', line 273

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


293
294
295
# File 'lib/mongomatic/base.rb', line 293

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

#removed?Boolean

Will return true if the document has been removed.

Returns:

  • (Boolean)


183
184
185
# File 'lib/mongomatic/base.rb', line 183

def removed?
  self.removed == true
end

#set_value_for_key(key, value) ⇒ Object



148
149
150
151
# File 'lib/mongomatic/base.rb', line 148

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.



298
299
300
# File 'lib/mongomatic/base.rb', line 298

def to_hash
  @doc || {}
end

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



322
323
324
325
326
327
328
329
330
# File 'lib/mongomatic/base.rb', line 322

def transaction(key=nil, duration=5, &block)
  raise Mongomatic::Exceptions::DocumentIsNew if new?
  if key.is_a?(Hash) && key[:scope]
    key = [self.class.name, self["_id"].to_s, key[:scope]].join("-")
  else
    key ||= [self.class.name, self["_id"].to_s].join("-")
  end
  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


242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/mongomatic/base.rb', line 242

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


264
265
266
# File 'lib/mongomatic/base.rb', line 264

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

#valid?Boolean

Returns:

  • (Boolean)


118
119
120
121
122
123
124
125
# File 'lib/mongomatic/base.rb', line 118

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


114
115
116
# File 'lib/mongomatic/base.rb', line 114

def validate
  true
end

#value_for_key(key) ⇒ Object



153
154
155
156
# File 'lib/mongomatic/base.rb', line 153

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