Class: MongoRecord::Base

Inherits:
Object show all
Defined in:
lib/mongo_record/base.rb

Overview

A superclass for database collection instances. The API is very similar to ActiveRecord. See #find for examples.

If you override initialize, make sure to call the superclass version, passing it the database row or hash that it was given.

Example:

class MP3Track < MongoRecord::Base
  collection_name :mp3_track
  fields :artist, :album, :song, :track
  def to_s
    "artist: #{self.artist}, album: #{self.album}, song: #{self.song}, track: #{track}"
  end
end

track = MP3Track.find_by_song('She Blinded Me With Science')
puts track.to_s

The database connection defaults to the global $db. You can set the connection using MongoRecord::Base.connection= and read it with MongoRecord::Base.connection.

# Set the connection to something besides $db
MongoRecord::Base.connection = connect('my-database')

Direct Known Subclasses

Subobject

Constant Summary collapse

@@connection =
nil

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(row = {}) {|_self| ... } ⇒ Base

Initialize a new object with either a hash of values or a row returned from the database.

Yields:

  • (_self)

Yield Parameters:



625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
# File 'lib/mongo_record/base.rb', line 625

def initialize(row={})
  case row
  when Hash
    row.each { |k, val|
      k = '_id' if k == 'id' # Rails helper
      init_ivar("@#{k}", val)
    }
  else
    row.instance_variables.each { |iv|
      init_ivar(iv, row.instance_variable_get(iv))
    }
  end
  # Default values for remaining fields
  (self.class.field_names + self.class.subobjects.keys).each { |iv|
    iv = "@#{iv}"
    instance_variable_set(iv, nil) unless instance_variable_defined?(iv)
  }
  self.class.arrays.keys.each { |iv|
    iv = "@#{iv}"
    instance_variable_set(iv, []) unless instance_variable_defined?(iv)
  }
  yield self if block_given?
end

Class Method Details

.arraysObject

Return the names of all instance variables that hold objects declared using has_many. The names do not start with ‘@’.



157
# File 'lib/mongo_record/base.rb', line 157

def arrays; @arrays; end

.belongs_to(name, options = {}) ⇒ Object

Tells Mongo that this object belongs to another. A no-op.



205
206
# File 'lib/mongo_record/base.rb', line 205

def belongs_to(name, options={})
end

.collectionObject

The collection object for this class, which will be different for every subclass of MongoRecord::Base.



210
211
212
# File 'lib/mongo_record/base.rb', line 210

def collection
  connection.collection(@coll_name.to_s)
end

.collection_name(coll_name) ⇒ Object

Call this method to set the Mongo collection name for this class. The default value is the class name turned into lower_case_with_underscores.



119
120
121
122
# File 'lib/mongo_record/base.rb', line 119

def collection_name(coll_name)
  @coll_name = coll_name
  field(:_id, :_ns, :_update)
end

.connectionObject

Return the database connection. The default value is # $db.



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

def connection
  conn = @@connection || $db
  raise "connection not defined" unless conn
  conn
end

.connection=(val) ⇒ Object

Set the database connection. If the connection is set to nil, then $db will be used.



97
98
99
100
# File 'lib/mongo_record/base.rb', line 97

def connection=(val)
  @@connection = val
  @@connection.pk_factory = PKFactory.new unless @@connection.pk_factory
end

.count(options = {}) ⇒ Object

Returns the number of matching records.



300
301
302
303
# File 'lib/mongo_record/base.rb', line 300

def count(options={})
  criteria = criteria_from(options[:conditions]).merge!(where_func(options[:where]))
  collection.count(criteria)
end

.create(values_hash) ⇒ Object

Creates, saves, and returns a new database object.



339
340
341
342
343
# File 'lib/mongo_record/base.rb', line 339

def create(values_hash)
  object = self.new(values_hash)
  object.save
  object
end

.delete(id) ⇒ Object

Deletes the record with the given id from the collection.



306
307
308
# File 'lib/mongo_record/base.rb', line 306

def delete(id)
  collection.remove({:_id => id})
end

.delete_all(conditions = nil) ⇒ Object

Deletes all records that match condition, which can be a Mongo-style hash or an ActiveRecord-like hash. Examples:

Person.destroy_all "name like '%fred%'   # SQL WHERE clause
Person.destroy_all ["name = ?", 'Fred']  # Rails condition
Person.destroy_all {:name => 'Fred'}     # Mongo hash


334
335
336
# File 'lib/mongo_record/base.rb', line 334

def delete_all(conditions=nil)
  collection.remove(criteria_from(conditions))
end

.destroy(id) ⇒ Object

Load the object with id and delete it.



312
313
314
# File 'lib/mongo_record/base.rb', line 312

def destroy(id)
  id.is_a?(Array) ? id.each { |oid| destroy(oid) } : find(id).destroy
end

.destroy_all(conditions = nil) ⇒ Object

Destroy all objects that match conditions. Warning: if conditions is nil, all records in the collection will be destroyed.



325
326
327
# File 'lib/mongo_record/base.rb', line 325

def destroy_all(conditions = nil)
  find(:all, :conditions => conditions).each { |object| object.destroy }
end

.field(*fields) ⇒ Object Also known as: fields

Creates one or more collection fields. Each field will be saved to and loaded from the database. The fields named “_id” and “_ns” are automatically saved and loaded.

The method “field” is also called “fields”; you can use either one.



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/mongo_record/base.rb', line 129

def field(*fields)
  fields.each { |field|
    field = field.to_sym
    unless @field_names.include?(field)
      ivar_name = "@" + field.to_s
      define_method(field, lambda { instance_variable_get(ivar_name) })
      define_method("#{field}=".to_sym, lambda { |val| instance_variable_set(ivar_name, val) })
      define_method("#{field}?".to_sym, lambda {
                      val = instance_variable_get(ivar_name)
                      val != nil && (!val.kind_of?(String) || val != '')
                    })
      @field_names << field
    end
  }
end

.field_namesObject

Return the field names.



147
# File 'lib/mongo_record/base.rb', line 147

def field_names; @field_names; end

.find(*args) ⇒ Object

Find one or more database objects.

  • Find by id (a single id or an array of ids) returns one record or a Cursor.

  • Find :first returns the first record that matches the options used or nil if not found.

  • Find :all records; returns a Cursor that can iterate over raw records.

Options:

:conditions - Hash where key is field name and value is field value. Value may be a simple value like a string, number, or regular expression.

:select - Single field name or list of field names. If not specified, all fields are returned. Names may be symbols or strings. The database always returns _id and _ns fields.

:order - If a symbol, orders by that field in ascending order. If a string like “field1 asc, field2 desc, field3”, then sorts those fields in the specified order (default is ascending). If an array, each element is either a field name or symbol (which will be sorted in ascending order) or a hash where key =isfield and value is ‘asc’ or ‘desc’ (case-insensitive), 1 or -1, or if any other value then true == 1 and false/nil == -1.

:limit - Maximum number of records to return.

:offset - Number of records to skip.

:where - A string containing a JavaScript expression. This expression is run by the database server against each record found after the :conditions are run.

Examples for find by id:

Person.find("48e5307114f4abdf00dfeb86")     # returns the object for this ID
Person.find(["a_hex_id", "another_hex_id"]) # returns a Cursor over these two objects
Person.find(["a_hex_id"])                   # returns a Cursor over the object with this ID
Person.find("a_hex_id", :conditions => "admin = 1", :order => "created_on DESC")

Examples for find first:

Person.find(:first) # returns the first object in the collection
Person.find(:first, :conditions => ["user_name = ?", user_name])
Person.find(:first, :order => "created_on DESC", :offset => 5)
Person.find(:first, :order => {:created_on => -1}, :offset => 5) # same as previous example

Examples for find all:

Person.find(:all) # returns a Cursor over all objects in the collection
Person.find(:all, :conditions => ["category = ?, category], :limit => 50)
Person.find(:all, :offset => 10, :limit => 10)
Person.find(:all, :select => :name) # Only returns name (and _id) fields

Find_by_*

Person.find_by_name_and_age("Spongebob", 42)
Person.find_all_by_name("Fred")

Mongo-specific example:

Person.find(:all, :where => "this.address.city == 'New York' || this.age = 42")

As a side note, the :order, :limit, and :offset options are passed on to the Cursor (after the :order option is rewritten to be a hash). So

Person.find(:all, :offset => 10, :limit => 10, :order => :created_on)

is the same as

Person.find(:all).skip(10).limit(10).sort({:created_on => 1})


281
282
283
284
285
286
287
288
289
290
291
# File 'lib/mongo_record/base.rb', line 281

def find(*args)
  options = extract_options_from_args!(args)
  case args.first
  when :first
    find_initial(options)
  when :all
    find_every(options)
  else
    find_from_ids(args, options)
  end
end

.find_by_mql(mql) ⇒ Object Also known as: find_by_sql

Returns all records matching mql. Not yet implemented.



294
295
296
# File 'lib/mongo_record/base.rb', line 294

def find_by_mql(mql)    # :nodoc:
  raise "not implemented"
end

.has_and_belongs_to_many(name, options = {}) ⇒ Object

Tells Mongo that this object has and many belongs to another object. A no-op.



201
202
# File 'lib/mongo_record/base.rb', line 201

def has_and_belongs_to_many(name, options={})
end

.has_many(name, options = {}) ⇒ Object

Tells Mongo about an array of subobjects (which need not be MongoRecord::Subobjects).

Options: :class_name - Name of the class of the subobject.



187
188
189
190
191
192
193
194
195
196
197
# File 'lib/mongo_record/base.rb', line 187

def has_many(name, options={})
  name = name.to_sym
  unless @arrays[name]
    ivar_name = "@" + name.to_s
    define_method(name, lambda { instance_variable_get(ivar_name) })
    define_method("#{name}=".to_sym, lambda { |val| instance_variable_set(ivar_name, val) })
    define_method("#{name}?".to_sym, lambda { !instance_variable_get(ivar_name).empty? })
    klass_name = options[:class_name] || field_name_to_class_name(name)
    @arrays[name] = Kernel.const_get(klass_name)
  end
end

.has_one(name, options = {}) ⇒ Object

Tell Mongo about a subobject (which need not be a MongoRecord::Subobject).

Options: <code>:class_name<code> - Name of the class of the subobject.



167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/mongo_record/base.rb', line 167

def has_one(name, options={})
  name = name.to_sym
  unless @subobjects[name]
    ivar_name = "@" + name.to_s
    define_method(name, lambda { instance_variable_get(ivar_name) })
    define_method("#{name}=".to_sym, lambda { |val| instance_variable_set(ivar_name, val) })
    define_method("#{name}?".to_sym, lambda {
                    val = instance_variable_get(ivar_name)
                    val != nil && (!val.kind_of?(String) || val != '')
                  })
    klass_name = options[:class_name] || field_name_to_class_name(name)
    @subobjects[name] = Kernel.const_get(klass_name)
  end
end

.inherited(subclass) ⇒ Object

Get ready to save information about subclass.



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

def inherited(subclass)
  subclass.instance_variable_set("@coll_name", class_name_to_field_name(subclass.name)) # default name
  subclass.instance_variable_set("@field_names", []) # array of scalars names (symbols)
  subclass.instance_variable_set("@subobjects", {}) # key = name (symbol), value = class
  subclass.instance_variable_set("@arrays", {})     # key = name (symbol), value = class
end

.instantiate(row = {}) ⇒ Object

This method only exists so that MongoRecord::Base and ActiveRecord::Base can live side by side.



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

def instantiate(row={})
  new(row)
end

.method_missing(sym, *args) ⇒ Object

Handles find_* methods such as find_by_name, find_all_by_shoe_size, and find_or_create_by_name.



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/mongo_record/base.rb', line 370

def method_missing(sym, *args)
  if match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(sym.to_s)
    find_how_many = ($1 == 'all_by') ? :all : :first
    field_names = $2.split(/_and_/)
    super unless all_fields_exist?(field_names)
    search = search_from_names_and_values(field_names, args)
    self.find(find_how_many, {:conditions => search}, *args[field_names.length..-1])
  elsif match = /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/.match(sym.to_s)
    create = $1 == 'create'
    field_names = $2.split(/_and_/)
    super unless all_fields_exist?(field_names)
    search = search_from_names_and_values(field_names, args)
    row = self.find(:first, {:conditions => search})
    return self.new(row) if row # found
    obj = self.new(search.merge(args[field_names.length] || {})) # new object using search and remainder of args
    obj.save if create
    obj
  else
    super
  end
end

.mongo_ivar_namesObject

Return the names of all fields, subobjects, and arrays.



160
# File 'lib/mongo_record/base.rb', line 160

def mongo_ivar_names; @field_names + @subobjects.keys + @arrays.keys; end

.removeObject

Deletes the record with the given id from the collection.



309
310
311
# File 'lib/mongo_record/base.rb', line 309

def delete(id)
  collection.remove({:_id => id})
end

.subobjectsObject

Return the names of all instance variables that hold objects declared using has_one. The names do not start with ‘@’.

These are not necessarily MongoRecord::Subobject subclasses.



153
# File 'lib/mongo_record/base.rb', line 153

def subobjects; @subobjects; end

.update(id, attributes) ⇒ Object

Finds the record from the passed id, instantly saves it with the passed attributes (if the validation permits it), and returns it. If the save fails under validations, the unsaved object is still returned.

The arguments may also be given as arrays in which case the update method is called for each pair of id and attributes and an array of objects is returned.

>

Example of updating one record:

Person.update(15, {:user_name => 'Samuel', :group => 'expert'})

Example of updating multiple records:

people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy"} }
Person.update(people.keys, people.values)


357
358
359
360
361
362
363
364
365
366
# File 'lib/mongo_record/base.rb', line 357

def update(id, attributes)
  if id.is_a?(Array)
    i = -1
    id.collect { |id| i += 1; update(id, attributes[i]) }
  else
    object = find(id)
    object.update_attributes(attributes)
    object
  end
end

.update_all(updates, conditions = nil) ⇒ Object

Not yet implemented.



317
318
319
320
# File 'lib/mongo_record/base.rb', line 317

def update_all(updates, conditions = nil)
  # TODO
  raise "not yet implemented"
end

Instance Method Details

#==(comparison_object) ⇒ Object

Return true if the comparison_object is the same object, or is of the same type and has the same id.



657
658
659
660
661
662
# File 'lib/mongo_record/base.rb', line 657

def ==(comparison_object)
  comparison_object.equal?(self) ||
    (comparison_object.instance_of?(self.class) &&
     comparison_object.id == id &&
     !comparison_object.new_record?)
end

#attributes_from_column_definitionObject

Does nothing.



771
# File 'lib/mongo_record/base.rb', line 771

def attributes_from_column_definition; end

#createObject

Save self to the database and set the id.



705
706
707
708
709
710
# File 'lib/mongo_record/base.rb', line 705

def create
  set_create_times
  with_id = self.class.collection.insert(to_mongo_value)
  @_id = with_id['_id'] || with_id[:_id]
  self
end

#deleteObject Also known as: remove

Remove self from the database and set @_id to nil. If self has no @_id, does nothing.



725
726
727
728
729
730
# File 'lib/mongo_record/base.rb', line 725

def delete
  if @_id
    self.class.collection.remove({:_id => self._id})
    @_id = nil
  end
end

#destroyObject

Delete and freeze self.



734
735
736
737
# File 'lib/mongo_record/base.rb', line 734

def destroy
  delete
  freeze
end

#eql?(comparison_object) ⇒ Boolean

Delegate to ==



665
666
667
# File 'lib/mongo_record/base.rb', line 665

def eql?(comparison_object)
  self == (comparison_object)
end

#hashObject

Delegate to id in order to allow two records of the same type and id to work with something like:

[ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]


671
672
673
# File 'lib/mongo_record/base.rb', line 671

def hash
  id.hash
end

#idObject

Return this object’s id.



653
# File 'lib/mongo_record/base.rb', line 653

def id; @_id ? @_id.to_s : nil; end

#id=(val) ⇒ Object

Set the id of this object. Normally not called by user code.



650
# File 'lib/mongo_record/base.rb', line 650

def id=(val); @_id = (val == '' ? nil : val); end

#new_record?Boolean

Return true if this object is new—that is, does not yet have an id.



692
693
694
# File 'lib/mongo_record/base.rb', line 692

def new_record?
  @_id == nil
end

#saveObject

Save self and returns true if the save was successful, false if not.



681
682
683
# File 'lib/mongo_record/base.rb', line 681

def save
  create_or_update
end

#save!Object

Save self and returns true if the save was successful and raises RecordNotSaved if not.



687
688
689
# File 'lib/mongo_record/base.rb', line 687

def save!
  create_or_update || raise(RecordNotSaved)
end

#to_mongo_valueObject

Convert this object to a Mongo value suitable for saving to the database.



698
699
700
701
702
# File 'lib/mongo_record/base.rb', line 698

def to_mongo_value
  h = {}
  self.class.mongo_ivar_names.each {|iv| h[iv] = instance_variable_get("@#{iv}").to_mongo_value }
  h
end

#to_paramObject

Rails convenience method. Return this object’s id as a string.



676
677
678
# File 'lib/mongo_record/base.rb', line 676

def to_param
  @_id.to_s
end

#updateObject

Save self to the database. Return false if there was an error, self if all is well.



714
715
716
717
718
719
720
721
# File 'lib/mongo_record/base.rb', line 714

def update
  set_update_times
  row = self.class.collection.insert(to_mongo_value)
  if row['_id'].to_s != @_id.to_s
    return false
  end
  self
end

#update_attribute(name, value) ⇒ Object

Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records. Note: This method is overwritten by the Validation module that’ll make sure that updates made with this method doesn’t get subjected to validation checks. Hence, attributes can be updated even if the full object isn’t valid.



750
751
752
753
# File 'lib/mongo_record/base.rb', line 750

def update_attribute(name, value)
  send(name.to_s + '=', value)
  save
end

#update_attributes(attributes) ⇒ Object

Updates all the attributes from the passed-in Hash and saves the record. If the object is invalid, the saving will fail and false will be returned.



758
759
760
761
# File 'lib/mongo_record/base.rb', line 758

def update_attributes(attributes)
  self.attributes = attributes
  save
end

#update_attributes!(attributes) ⇒ Object

Updates an object just like Base.update_attributes but calls save! instead of save so an exception is raised if the record is invalid.



765
766
767
768
# File 'lib/mongo_record/base.rb', line 765

def update_attributes!(attributes)
  self.attributes = attributes
  save!
end