Class: Ohm::Model

Inherits:
Object show all
Includes:
Validations
Defined in:
lib/ohm.rb

Overview

This is the class that you need to extend in order to define your own models.

Probably the most magic happening within Model is the catching of const_missing exceptions to allow the use of constants even before they are defined.

Examples:

class Post < Ohm::Model
  reference :author, User # no User definition yet!
end

class User < Ohm::Model
end

See Also:

Defined Under Namespace

Modules: Validations Classes: Collection, Index, IndexNotFound, List, MissingID, Set, Wrapper

Constant Summary

@@attributes =
Hash.new { |hash, key| hash[key] = [] }
@@collections =
Hash.new { |hash, key| hash[key] = [] }
@@counters =
Hash.new { |hash, key| hash[key] = [] }
@@indices =
Hash.new { |hash, key| hash[key] = [] }

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods included from Validations

#assert_unique

Methods included from Validations

#assert, #assert_format, #assert_numeric, #assert_present, #errors, #valid?, #validate

Constructor Details

- (Model) initialize(attrs = {})

Constructor for all subclasses of Ohm::Model, which optionally takes a Hash of attribute value pairs.

Starting with Ohm 0.1.0, you can use custom ids instead of being forced to use auto incrementing numeric ids, but keep in mind that you have to pass in the preferred id during object initialization.

Examples:

class User < Ohm::Model
end

class Post < Ohm::Model
  attribute :title
  reference :user, User
end

user = User.create
p1 = Post.new(:title => "Redis", :user_id => user.id)
p1.save

p1.user_id == user.id
# => true

p1.user == user
# => true

# You can also just pass the actual User object, which is the better
# way to do it:
Post.new(:title => "Different way", :user => user).user == user
# => true

# Let's try and generate custom ids
p2 = Post.new(:id => "ohm-redis-library", :title => "Lib")
p2 == Post["ohm-redis-library"]
# => true

Parameters:

  • attrs (Hash) (defaults to: {})

    Attribute value pairs.



1436
1437
1438
1439
1440
1441
# File 'lib/ohm.rb', line 1436

def initialize(attrs = {})
  @id = nil
  @_memo = {}
  @_attributes = Hash.new { |hash, key| hash[key] = read_remote(key) }
  update_attributes(attrs)
end

Instance Attribute Details

- (Object) id



1048
1049
1050
# File 'lib/ohm.rb', line 1048

def id
  @id or raise MissingID
end

Class Method Details

+ (Ohm::Model?) [](id)

Allows you to find an Ohm::Model instance by its id.

Parameters:

  • id (#to_s)

    The id of the model you want to find.

Returns:

  • (Ohm::Model, nil)

    The instance of Ohm::Model or nil of it does not exist.



1284
1285
1286
# File 'lib/ohm.rb', line 1284

def self.[](id)
  new(:id => id) if id && exists?(id)
end

+ (Object) all

Returns a set containing all the members of a given class.

Examples:

class Post < Ohm::Model
end

post = Post.create

Post.all.include?(post)
# => true

post.delete

Post.all.include?(post)
# => false


1310
1311
1312
# File 'lib/ohm.rb', line 1310

def self.all
  Ohm::Model::Index.new(key[:all], Wrapper.wrap(self))
end

+ (Object) attribute(name)

Defines a string attribute for the model. This attribute will be persisted by Redis as a string. Any value stored here will be retrieved in its string representation.

If you’re looking to have typecasting built in, you may want to look at Ohm::Typecast in Ohm::Contrib.

Parameters:

  • name (Symbol)

    Name of the attribute.

See Also:



1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
# File 'lib/ohm.rb', line 1061

def self.attribute(name)
  define_method(name) do
    read_local(name)
  end

  define_method(:#{name}=") do |value|
    write_local(name, value)
  end

  attributes << name unless attributes.include?(name)
end

+ (Object) attributes

All the defined attributes within a class.

See Also:



1316
1317
1318
# File 'lib/ohm.rb', line 1316

def self.attributes
  @@attributes[self]
end

+ (Object) collection(name, model, reference = to_reference)

Define a collection of objects which have a reference to this model.

  class Comment < Ohm::Model
    attribute :content
    reference :post, Post
  end

  class Post < Ohm::Model
    attribute  :content
    collection :comments, Comment
    reference  :author, Person
  end

  class Person < Ohm::Model
    attribute  :name

    # When the name of the reference cannot be inferred,
    # you need to specify it in the third param.
    collection :posts, Post, :author
  end

  @person = Person.create :name => "Albert"
  @post = Post.create :content => "Interesting stuff",
                      :author => @person
  @comment = Comment.create :content => "Indeed!", :post => @post

  @post.comments.first.content
  # => "Indeed!"

  @post.author.name
  # => "Albert"

Important: Please note that even though a collection is a set, you should not add or remove objects from this collection directly.

Parameters:

  • name (Symbol)

    Name of the collection.

  • model (Constant)

    Model where the reference is defined.

  • reference (Symbol) (defaults to: to_reference)

    Reference as defined in the associated model.

See Also:



1254
1255
1256
1257
1258
1259
# File 'lib/ohm.rb', line 1254

def self.collection(name, model, reference = to_reference)
  model = Wrapper.wrap(model)
  define_method(name) {
    model.unwrap.find(:#{reference}_id" => send(:id))
  }
end

+ (Boolean) collection?(value) (private)

Returns:

  • (Boolean)


1879
1880
1881
1882
# File 'lib/ohm.rb', line 1879

def self.collection?(value)
  value.kind_of?(Enumerable) &&
  value.kind_of?(String) == false
end

+ (Object) collections

All the defined collections within a class. This will be comprised of all sets and lists defined within your class.

Examples:

class Post < Ohm::Model
  set  :authors, Author
  list :comments, Comment
end

Post.collections == [:authors, :comments]
# => true

See Also:



1341
1342
1343
# File 'lib/ohm.rb', line 1341

def self.collections
  @@collections[self]
end

+ (Object) connect(*options)

Makes the model connect to a different Redis instance. This is useful for scaling a large application, where one model can be stored in a different Redis instance, and some other groups of models can be in another Redis instance.

This approach of splitting models is a lot simpler than doing a distributed Redis solution and may well be the right solution for certain cases.

Examples:

class Post < Ohm::Model
  connect :port => 6380, :db => 2

  attribute :body
end

# Since these settings are usually environment-specific,
# you may want to call this method from outside of the class
# definition:
Post.connect(:port => 6380, :db => 2)

See Also:



1717
1718
1719
# File 'lib/ohm.rb', line 1717

def self.connect(*options)
  self.db = Ohm.connection(*options)
end

+ (Object) const_missing(name) (protected)

Wraps any missing constants lazily in Wrapper delaying the evaluation of constants until they are actually needed.



1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
# File 'lib/ohm.rb', line 1790

def self.const_missing(name)
  wrapper = Wrapper.new(name) { const_get(name) }

  # Allow others to hook to const_missing.
  begin
    super(name)
  rescue NameError
  end

  wrapper
end

+ (Object) counter(name)

Defines a counter attribute for the model. This attribute can’t be assigned, only incremented or decremented. It will be zero by default.

Parameters:

  • name (Symbol)

    Name of the counter.



1077
1078
1079
1080
1081
1082
1083
# File 'lib/ohm.rb', line 1077

def self.counter(name)
  define_method(name) do
    read_local(name).to_i
  end

  counters << name unless counters.include?(name)
end

+ (Object) counters

All the defined counters within a class.

See Also:



1322
1323
1324
# File 'lib/ohm.rb', line 1322

def self.counters
  @@counters[self]
end

+ (Ohm::Model) create(*args)

Convenience method to create and return the newly created object.

Examples:

class Post < Ohm::Model
  attribute :title
end

post = Post.create(:title => "A new post")

Parameters:

  • args (Hash)

    attribute-value pairs for the object.

Returns:

  • (Ohm::Model)

    an instance of the class you’re trying to create.



1363
1364
1365
1366
1367
# File 'lib/ohm.rb', line 1363

def self.create(*args)
  model = new(*args)
  model.create
  model
end

+ (Object) db (private)

Provides access to the Redis database. This is shared accross all models and instances.



1805
1806
1807
# File 'lib/ohm.rb', line 1805

def self.db
  Ohm.threaded[self] || Ohm.redis
end

+ (Object) db=(connection) (private)



1809
1810
1811
# File 'lib/ohm.rb', line 1809

def self.db=(connection)
  Ohm.threaded[self] = connection
end

+ (String) encode(value)

Encode a value, making it safe to use as a key. Internally used by index_key_for to canonicalize the indexed values.

Parameters:

  • value (#to_s)

    Any object you want to be able to use as a key.

Returns:

  • (String)

    A string which is safe to use as a key.

See Also:



1394
1395
1396
# File 'lib/ohm.rb', line 1394

def self.encode(value)
  Base64.encode64(value.to_s).gsub("\n", "")
end

+ (Boolean) exists?(id) (private)

Returns:

  • (Boolean)


1818
1819
1820
# File 'lib/ohm.rb', line 1818

def self.exists?(id)
  key[:all].sismember(id)
end

+ (Object) find(hash)

Search across multiple indices and return the intersection of the sets.

Examples:

Finds all the user events for the supplied days

event1 = Event.create day: "2009-09-09", author: "Albert"
event2 = Event.create day: "2009-09-09", author: "Benoit"
event3 = Event.create day: "2009-09-10", author: "Albert"

[event1] == Event.find(author: "Albert", day: "2009-09-09").to_a
# => true


1378
1379
1380
1381
1382
1383
1384
1385
1386
# File 'lib/ohm.rb', line 1378

def self.find(hash)
  unless hash.kind_of?(Hash)
    raise ArgumentError,
      "You need to supply a hash with filters. " +
      "If you want to find by ID, use #{self}[id] instead."
  end

  all.find(hash)
end

+ (Object) index(att)

Creates an index (a set) that will be used for finding instances.

If you want to find a model instance by some attribute value, then an index for that attribute must exist.

Examples:

class User < Ohm::Model
  attribute :email
  index :email
end

# Now this is possible:
User.find :email => "ohm@example.com"

Parameters:

  • name (Symbol)

    Name of the attribute to be indexed.



1144
1145
1146
# File 'lib/ohm.rb', line 1144

def self.index(att)
  indices << att unless indices.include?(att)
end

+ (Ohm::Key) index_key_for(name, value) (private)

Get the index name for a specific index and value pair. The return value is an instance of Key, which you can readily do Redis operations on.

Examples:

class Post < Ohm::Model
  attribute :title
  index :title
end

post = Post.create(:title => "Foo")
key = Post.index_key_for(:title, "Foo")
key == "Post:title:Rm9v"
key.scard == 1
key.smembers == [post.id]
# => true

Parameters:

  • name (Symbol)

    The name of the index.

  • value (#to_s)

    The value for the index.

Returns:

  • (Ohm::Key)

    A key which you can treat as a string, but also do Redis operations on.

Raises:



1966
1967
1968
1969
# File 'lib/ohm.rb', line 1966

def self.index_key_for(name, value)
  raise IndexNotFound, name unless indices.include?(name)
  key[name][encode(value)]
end

+ (Object) indices

All the defined indices within a class.

See Also:



1347
1348
1349
# File 'lib/ohm.rb', line 1347

def self.indices
  @@indices[self]
end

+ (Object) key (private)

Allows you to do key manipulations scoped solely to your class.



1814
1815
1816
# File 'lib/ohm.rb', line 1814

def self.key
  Key.new(self, db)
end

+ (Object) list(name, model)

Defines a list attribute for the model. It can be accessed only after the model instance is created, or if you assign an :id during object construction.

Examples:

class Post < Ohm::Model
  list :comments, Comment
end

class Comment < Ohm::Model
end

# WRONG!!!
post = Post.new
post.comments << Comment.create

# Right :-)
post = Post.create
post.comments << Comment.create

# Alternative way if you want to have custom ids.
post = Post.new(:id => "my-id")
post.comments << Comment.create
post.create

Parameters:

  • name (Symbol)

    Name of the list.



1112
1113
1114
1115
# File 'lib/ohm.rb', line 1112

def self.list(name, model)
  define_memoized_method(name) { List.new(key[name], Wrapper.wrap(model)) }
  collections << name unless collections.include?(name)
end

+ (Object) reference(name, model)

Define a reference to another object.

Examples:

class Comment < Ohm::Model
  attribute :content
  reference :post, Post
end

@post = Post.create :content => "Interesting stuff"

@comment = Comment.create(:content => "Indeed!", :post => @post)

@comment.post.content
# => "Interesting stuff"

@comment.post = Post.create(:content => "Wonderful stuff")

@comment.post.content
# => "Wonderful stuff"

@comment.post.update(:content => "Magnific stuff")

@comment.post.content
# => "Magnific stuff"

@comment.post = nil

@comment.post
# => nil

See Also:



1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
# File 'lib/ohm.rb', line 1181

def self.reference(name, model)
  model = Wrapper.wrap(model)

  reader = :#{name}_id"
  writer = :#{name}_id="

  attributes << reader unless attributes.include?(reader)

  index reader

  define_memoized_method(name) do
    model.unwrap[send(reader)]
  end

  define_method(:#{name}=") do |value|
    @_memo.delete(name)
    send(writer, value ? value.id : nil)
  end

  define_method(reader) do
    read_local(reader)
  end

  define_method(writer) do |value|
    @_memo.delete(name)
    write_local(reader, value)
  end
end

+ (Object) set(name, model)

Defines a set attribute for the model. It can be accessed only after the model instance is created. Sets are recommended when insertion and retreival order is irrelevant, and operations like union, join, and membership checks are important.

Parameters:

  • name (Symbol)

    Name of the set.



1123
1124
1125
1126
# File 'lib/ohm.rb', line 1123

def self.set(name, model)
  define_memoized_method(name) { Set.new(key[name], Wrapper.wrap(model)) }
  collections << name unless collections.include?(name)
end

+ (Symbol) to_reference

Used by collection to infer the reference.

Returns:

  • (Symbol)

    Representation of this class in an all-lowercase format, separated by underscores and demodulized.



1265
1266
1267
1268
1269
1270
# File 'lib/ohm.rb', line 1265

def self.to_reference
  name.to_s.
    match(/^(?:.*::)*(.*)$/)[1].
    gsub(/([a-z\d])([A-Z])/, '\1_\2').
    downcase.to_sym
end

Instance Method Details

- (true, false) ==(other) Also known as: eql?

Implementation of equality checking. Equality is defined by two simple rules:

  1. They have the same class.

  2. They have the same key (Redis key e.g. Post:1 == Post:1).

Returns:

  • (true, false)

    Whether or not the passed object is equal.



1624
1625
1626
1627
1628
# File 'lib/ohm.rb', line 1624

def ==(other)
  other.kind_of?(self.class) && other.key == key
rescue MissingID
  false
end

- (Object) add_to_index(att, value = send(att)) (private)



1884
1885
1886
1887
1888
# File 'lib/ohm.rb', line 1884

def add_to_index(att, value = send(att))
  index = index_key_for(att, value)
  index.sadd(id)
  key[:_indices].sadd(index)
end

- (Object) add_to_indices (private)



1868
1869
1870
1871
1872
1873
# File 'lib/ohm.rb', line 1868

def add_to_indices
  indices.each do |att|
    next add_to_index(att) unless collection?(send(att))
    send(att).each { |value| add_to_index(att, value) }
  end
end

- (Object) attributes

Convenience wrapper for attributes.



1598
1599
1600
# File 'lib/ohm.rb', line 1598

def attributes
  self.class.attributes
end

- (Boolean) collection?(value) (private)

Returns:

  • (Boolean)


1875
1876
1877
# File 'lib/ohm.rb', line 1875

def collection?(value)
  self.class.collection?(value)
end

- (Object) collections

Convenience wrapper for collections.



1608
1609
1610
# File 'lib/ohm.rb', line 1608

def collections
  self.class.collections
end

- (Object) counters

Convenience wrapper for counters.



1603
1604
1605
# File 'lib/ohm.rb', line 1603

def counters
  self.class.counters
end

- (Ohm::Model?) create

Create this model if it passes all validations.

Returns:

  • (Ohm::Model, nil)

    The newly created object or nil if it fails validation.



1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
# File 'lib/ohm.rb', line 1452

def create
  return unless valid?
  initialize_id

  mutex do
    create_model_membership
    write
    add_to_indices
  end
end

- (Object) create_model_membership (private)



1854
1855
1856
# File 'lib/ohm.rb', line 1854

def create_model_membership
  self.class.all << self
end

- (Object) db (private)



1846
1847
1848
# File 'lib/ohm.rb', line 1846

def db
  self.class.db
end

- (Object) decr(att, count = 1)

Decrement the counter denoted by :att.

Parameters:

  • att (Symbol)

    Attribute to decrement.

  • count (Fixnum) (defaults to: 1)

    An optional decrement step to use.



1526
1527
1528
# File 'lib/ohm.rb', line 1526

def decr(att, count = 1)
  incr(att, -count)
end

- (Ohm::Model) delete

Delete this object from the Redis datastore, ensuring that all indices, attributes, collections, etc are also deleted with it.

Returns:



1503
1504
1505
1506
1507
1508
# File 'lib/ohm.rb', line 1503

def delete
  delete_from_indices
  delete_attributes(collections) unless collections.empty?
  delete_model_membership
  self
end

- (Object) delete_attributes(atts) (private)



1850
1851
1852
# File 'lib/ohm.rb', line 1850

def delete_attributes(atts)
  db.del(*atts.map { |att| key[att] })
end

- (Object) delete_from_indices (private)



1890
1891
1892
1893
1894
1895
1896
# File 'lib/ohm.rb', line 1890

def delete_from_indices
  key[:_indices].smembers.each do |index|
    db.srem(index, id)
  end

  key[:_indices].del
end

- (Object) delete_model_membership (private)



1858
1859
1860
1861
# File 'lib/ohm.rb', line 1858

def delete_model_membership
  key.del
  self.class.all.delete(self)
end

- (Fixnum) hash

Allows you to safely use an instance of Ohm::Model as a key in a Ruby hash without running into weird scenarios.

Examples:

class Post < Ohm::Model
end

h = {}
p1 = Post.new
h[p1] = "Ruby"
h[p1] == "Ruby"
# => true

p1.save
h[p1] == "Ruby"
# => false

Returns:

  • (Fixnum)

    An integer representing this object to be used as the index for hashes in Ruby.



1651
1652
1653
# File 'lib/ohm.rb', line 1651

def hash
  new? ? super : key.hash
end

- (Object) incr(att, count = 1)

Increment the counter denoted by :att.

Parameters:

  • att (Symbol)

    Attribute to increment.

  • count (Fixnum) (defaults to: 1)

    An optional increment step to use.



1514
1515
1516
1517
1518
1519
1520
# File 'lib/ohm.rb', line 1514

def incr(att, count = 1)
  unless counters.include?(att)
    raise ArgumentError, "#{att.inspect} is not a counter."
  end

  write_local(att, key.hincrby(att, count))
end

- (Object) index_key_for(att, value) (private)

Thin wrapper around index_key_for.



1972
1973
1974
# File 'lib/ohm.rb', line 1972

def index_key_for(att, value)
  self.class.index_key_for(att, value)
end

- (Object) indices

Convenience wrapper for indices.



1613
1614
1615
# File 'lib/ohm.rb', line 1613

def indices
  self.class.indices
end

- (Object) initialize_id (private)

The meat of the ID generation code for Ohm. For cases where you want to customize ID generation (i.e. use GUIDs or Base62 ids) then you simply override this method in your model.

Examples:

module UUID
  def self.new
    `uuidgen`.strip
  end
end

class Post < Ohm::Model

private
  def initialize_id
    @id ||= UUID.new
  end
end


1842
1843
1844
# File 'lib/ohm.rb', line 1842

def initialize_id
  @id ||= self.class.key[:id].incr.to_s
end

- (Object) inspect

Returns everything, including attributes, collections, counters, and the id of this object.

Useful for debugging and for doing irb work.



1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
# File 'lib/ohm.rb', line 1676

def inspect
  everything = (attributes + collections + counters).map do |att|
    value = begin
              send(att)
            rescue MissingID
              nil
            end

    [att, value.inspect]
  end

  sprintf("#<%s:%s %s>",
          self.class,
          new? ? "?" : id,
          everything.map {|e| e.join("=") }.join(" ")
  )
end

- (Ohm::Key) key

A key scoped to the model which uses this object’s id.

Returns:

  • (Ohm::Key)

    A key scoped to the model which uses this object’s id.

See Also:



1725
1726
1727
# File 'lib/ohm.rb', line 1725

def key
  self.class.key[id]
end

- (Object) lock! (private)

Lock the object so no other instances can modify it. This method implements the design pattern for locks described at: code.google.com/p/redis/wiki/SetnxCommand

See Also:



1981
1982
1983
1984
1985
1986
1987
1988
1989
# File 'lib/ohm.rb', line 1981

def lock!
  until key[:_lock].setnx(Time.now.to_f + 0.5)
    next unless timestamp = key[:_lock].get
    sleep(0.1) and next unless lock_expired?(timestamp)

    break unless timestamp = key[:_lock].getset(Time.now.to_f + 0.5)
    break if lock_expired?(timestamp)
  end
end

- (Boolean) lock_expired?(timestamp) (private)

Returns:

  • (Boolean)


1997
1998
1999
# File 'lib/ohm.rb', line 1997

def lock_expired?(timestamp)
  timestamp.to_f < Time.now.to_f
end

- (Object) mutex

Lock the object before executing the block, and release it once the block is done.

This is used during #create and #save to ensure that no race conditions occur.



1663
1664
1665
1666
1667
1668
1669
# File 'lib/ohm.rb', line 1663

def mutex
  lock!
  yield
  self
ensure
  unlock!
end

- (true, false) new?

Whether or not this object has an id.

Returns:

  • (true, false)

    Whether or not this object has an id.



1444
1445
1446
# File 'lib/ohm.rb', line 1444

def new?
  !@id
end

- (String) read_local(att) (private)

Get the value of a specific attribute. An important fact about attributes in Ohm is that they are all loaded lazily.

Parameters:

  • att (Symbol)

    The attribute you you want to get.

Returns:

  • (String)

    The value of att.



1903
1904
1905
# File 'lib/ohm.rb', line 1903

def read_local(att)
  @_attributes[att]
end

- (Object) read_locals(attrs) (private)

Read attributes en masse locally.



1931
1932
1933
1934
1935
# File 'lib/ohm.rb', line 1931

def read_locals(attrs)
  attrs.map do |att|
    send(att)
  end
end

- (String) read_remote(att) (private)

Used internally be the @_attributes hash to lazily load attributes when you need them. You may also use this in your code if you know what you are doing.

Parameters:

  • att (Symbol)

    The attribute you you want to get.

Returns:

  • (String)

    The value of att.



1921
1922
1923
1924
1925
1926
1927
1928
# File 'lib/ohm.rb', line 1921

def read_remote(att)
  unless new?
    value = key.hget(att)
    value.respond_to?(:force_encoding) ?
      value.force_encoding("UTF-8") :
      value
  end
end

- (Object) read_remotes(attrs) (private)

Read attributes en masse remotely.



1938
1939
1940
1941
1942
# File 'lib/ohm.rb', line 1938

def read_remotes(attrs)
  attrs.map do |att|
    read_remote(att)
  end
end

- (Ohm::Model?) save

Create or update this object based on the state of #new?.

Returns:

  • (Ohm::Model, nil)

    The saved object or nil if it fails validation.



1467
1468
1469
1470
1471
1472
1473
1474
1475
# File 'lib/ohm.rb', line 1467

def save
  return create if new?
  return unless valid?

  mutex do
    write
    update_indices
  end
end

- (Object) to_hash

Export the id and errors of the object. The `to_hash` takes the opposite approach of providing all the attributes and instead favors a white listed approach.

Examples:

person = Person.create(:name => "John Doe")
person.to_hash == { :id => '1' }
# => true

# if the person asserts presence of name, the errors will be included
person = Person.create(:name => "John Doe")
person.name = nil
person.valid?
# => false

person.to_hash == { :id => '1', :errors => [[:name, :not_present]] }
# => true

# for cases where you want to provide white listed attributes just do:

class Person < Ohm::Model
  def to_hash
    super.merge(:name => name)
  end
end

# now we have the name when doing a to_hash
person = Person.create(:name => "John Doe")
person.to_hash == { :id => '1', :name => "John Doe" }
# => true


1561
1562
1563
1564
1565
1566
# File 'lib/ohm.rb', line 1561

def to_hash
  attrs = {}
  attrs[:id] = id unless new?
  attrs[:errors] = errors unless errors.empty?
  attrs
end

- (String) to_json(*args)

Returns the JSON representation of the #to_hash for this object. Defining a custom #to_hash method will also affect this and return a corresponding JSON representation of whatever you have in your #to_hash.

Examples:

require "json"

class Post < Ohm::Model
  attribute :title

  def to_hash
    super.merge(:title => title)
  end
end

p1 = Post.create(:title => "Delta Force")
p1.to_hash == { :id => "1", :title => "Delta Force" }
# => true

p1.to_json == "{\"id\":\"1\",\"title\":\"Delta Force\"}"
# => true

Returns:

  • (String)

    The JSON representation of this object defined in terms of #to_hash.



1593
1594
1595
# File 'lib/ohm.rb', line 1593

def to_json(*args)
  to_hash.to_json(*args)
end

- (Object) unlock! (private)

Release the lock.

See Also:



1993
1994
1995
# File 'lib/ohm.rb', line 1993

def unlock!
  key[:_lock].del
end

- (Ohm::Model?) update(attrs)

Update this object, optionally accepting new attributes.

Parameters:

  • attrs (Hash)

    Attribute value pairs to use for the updated version

Returns:

  • (Ohm::Model, nil)

    The updated object or nil if it fails validation.



1483
1484
1485
1486
# File 'lib/ohm.rb', line 1483

def update(attrs)
  update_attributes(attrs)
  save
end

- (Object) update_attributes(attrs)

Locally update all attributes without persisting the changes. Internally used by #initialize and #update to set attribute value pairs.

Parameters:

  • attrs (Hash)

    Attribute value pairs.



1493
1494
1495
1496
1497
# File 'lib/ohm.rb', line 1493

def update_attributes(attrs)
  attrs.each do |key, value|
    send(:#{key}=", value)
  end
end

- (Object) update_indices (private)



1863
1864
1865
1866
# File 'lib/ohm.rb', line 1863

def update_indices
  delete_from_indices
  add_to_indices
end

- (Object) write (protected)

Write all the attributes and counters of this object. The operation is actually a 2-step process:

  1. Delete the current key, e.g. Post:2.

  2. Set all of the new attributes (using HMSET).

The DEL and HMSET operations are wrapped in a MULTI EXEC block to ensure the atomicity of the write operation.



1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
# File 'lib/ohm.rb', line 1748

def write
  unless (attributes + counters).empty?
    atts = (attributes + counters).inject([]) { |ret, att|
      value = send(att).to_s

      ret.push(att, value) if not value.empty?
      ret
    }

    db.multi do
      key.del
      key.hmset(*atts.flatten) if atts.any?
    end
  end
end

- (Object) write_local(att, value) (private)

Write the value of an attribute locally, without persisting it.

Parameters:

  • att (Symbol)

    The attribute you want to set.

  • value (#to_s)

    The value of the attribute you want to set.



1911
1912
1913
# File 'lib/ohm.rb', line 1911

def write_local(att, value)
  @_attributes[att] = value
end

- (Object) write_remote(att, value) (protected)

Write a single attribute both locally and remotely. It’s very important to know that this method skips validation checks, therefore you must ensure data integrity and validity in your application code.

Parameters:

  • att (Symbol, String)

    The name of the attribute to write.

  • value (#to_s)

    The value of the attribute to write.

See Also:



1775
1776
1777
1778
1779
1780
1781
1782
1783
# File 'lib/ohm.rb', line 1775

def write_remote(att, value)
  write_local(att, value)

  if value.to_s.empty?
    key.hdel(att)
  else
    key.hset(att, value)
  end
end