Class: Redpear::Model

Inherits:
Hash
  • Object
show all
Extended by:
Machinist::Machinable
Includes:
Expiration, FactoryGirl, Finders, Schema
Defined in:
lib/redpear/model.rb,
lib/redpear/model/machinist.rb,
lib/redpear/model/factory_girl.rb

Overview

Redis is a simple key/value store, hence storing structured data can be a challenge. Redpear::Model allows you to store/find/associate “records” in a Redis DB very efficiently, minimising IO operations and storage space where possible.

For example:

class Post < Redpear::Model
  column :title
  column :body
end

class Comment < Redpear::Model
  column :body
  index :post_id
end

Redpear::Model is VERY lightweight. It is optimised for raw speed at the expense of convenience.

Defined Under Namespace

Modules: Expiration, FactoryGirl, Finders, Machinist

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from FactoryGirl

#save!

Methods included from Expiration

#expire, #ttl

Methods included from Concern

#append_features

Constructor Details

#initialize(attrs = {}) ⇒ Model

Returns a new instance of Model.



103
104
105
106
107
108
# File 'lib/redpear/model.rb', line 103

def initialize(attrs = {})
  super()
  store 'id', (attrs.delete("id") || attrs.delete(:id) || self.class.pk_counter.next).to_s
  update(attrs)
  after_create(attrs)
end

Class Attribute Details

.connectionRedis

Returns the connection.

Returns:

  • (Redis)

    the connection



43
44
45
# File 'lib/redpear/model.rb', line 43

def connection
  @connection ||= (superclass.respond_to?(:connection) ? superclass.connection : Redis.current)
end

.scopeString

Returns the scope of this model. Example: Comment.scope # => “comments”.

Returns:

  • (String)

    the scope of this model. Example: Comment.scope # => “comments”



49
50
51
# File 'lib/redpear/model.rb', line 49

def scope
  @scope ||= "#{name.split('::').last.downcase}s"
end

Class Method Details

.blueprint_classClass

Used internally by Machinist

Returns:

  • (Class)

    the blueprint class



9
10
11
# File 'lib/redpear/model/machinist.rb', line 9

def self.blueprint_class
  self::Machinist::Blueprint
end

.destroy(id) ⇒ Redpear::Model

Destroys a record. Example:

Parameters:

  • id (String)

    the ID of the record to destroy

Returns:



88
89
90
# File 'lib/redpear/model.rb', line 88

def destroy(id)
  instantiate(id).tap(&:destroy)
end

.instantiate(id) ⇒ Object

Allocate an instance

Parameters:

  • id (String)

    the ID of the record to allocate



94
95
96
97
98
# File 'lib/redpear/model.rb', line 94

def instantiate(id)
  instance = allocate
  instance.send :store, 'id', id.to_s
  instance
end

.membersRedpear::Store::Set

Returns the IDs of all existing records.

Returns:



64
65
66
# File 'lib/redpear/model.rb', line 64

def members
  @_members ||= Redpear::Store::Set.new nested_key(:~), connection
end

.nested_key(*tokens) ⇒ String

Examples:

Comment.nested_key(123) # => "comments:123"
Comment.nested_key("abc", 123) # => "comments:abc:123"

Parameters:

  • tokens (multiple)

    The tokens to add to the scope

Returns:

  • (String)

    the full scope.



59
60
61
# File 'lib/redpear/model.rb', line 59

def nested_key(*tokens)
  [scope, *tokens].join(':')
end

.pipelined { ... } ⇒ Object

Runs a bulk-operation.

Yields:

  • operations that should be run as bulk



81
82
83
# File 'lib/redpear/model.rb', line 81

def pipelined(&block)
  connection.pipelined(&block)
end

.pk_counterRedpear::Store::Counter

Returns the generator of primary keys.

Returns:



69
70
71
# File 'lib/redpear/model.rb', line 69

def pk_counter
  @_pk_counter ||= Redpear::Store::Counter.new nested_key(:+), connection
end

.transaction { ... } ⇒ Object

Runs a transactional bulk-operation.

Yields:

  • operations that should be run in the transaction



75
76
77
# File 'lib/redpear/model.rb', line 75

def transaction(&block)
  connection.multi(&block)
end

Instance Method Details

#==(other) ⇒ Boolean Also known as: eql?

Custom comparator, inspired by ActiveRecord::Base#==

Parameters:

  • other (Object)

    the comparison object

Returns:

  • (Boolean)

    true, if other is persisted and ID



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

def ==(other)
  case other
  when Redpear::Model
    other.instance_of?(self.class) && other.id == id
  else
    super
  end
end

#[](name) ⇒ Object

Reads and (caches) a single value

Parameters:

  • name (String)

    The name of the attributes

Returns:

  • (Object)

    The attribute value



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/redpear/model.rb', line 138

def [](name)
  return if frozen?

  column = self.class.columns[name]
  return super if column.nil? || key?(column)

  value = case column
  when Redpear::Schema::Score
    column.members[id]
  when Redpear::Schema::Column
    attributes[column]
  end

  store column, column.type_cast(value)
end

#[]=(name, value) ⇒ Object

Write a single attribute

Parameters:

  • name (String)

    The name of the attributes

  • value (Object)

    The value to store



159
160
161
162
163
# File 'lib/redpear/model.rb', line 159

def []=(name, value)
  column = self.class.columns[name] || return
  delete column.to_s
  store_attribute attributes, column, value
end

#after_save(attrs) ⇒ Object

Cheap after save callback, override in subclasses and don’t forget to call ‘super()`.

Parameters:

  • attrs (Hash)

    attributes used on save



269
270
# File 'lib/redpear/model.rb', line 269

def after_save(attrs)
end

#attributesRedpear::Store::Hash

Returns the attributes store

Returns:



211
212
213
# File 'lib/redpear/model.rb', line 211

def attributes
  @_attributes ||= Redpear::Store::Hash.new self.class.nested_key("", id), self.class.connection
end

#clearHash

Clear all the cached attributes, but keep ID

Returns:

  • (Hash)

    self



202
203
204
205
206
207
# File 'lib/redpear/model.rb', line 202

def clear
  value = self.id
  super
ensure
  store 'id', value
end

#decrement(name, by = 1) ⇒ Object

Decrements the value of a counter attribute

Parameters:

  • name (String|Symbol)

    The column name to decrement

  • by (Integer) (defaults to: 1)

    Decrement by this value



182
183
184
# File 'lib/redpear/model.rb', line 182

def decrement(name, by = 1)
  increment name, -by
end

#destroyBoolean

Destroy the record.

Returns:

  • (Boolean)

    true if successful



223
224
225
226
227
228
229
# File 'lib/redpear/model.rb', line 223

def destroy
  before_destroy
  lookups.each {|l| l.delete(id) }
  attributes.purge!
  after_destroy
  freeze
end

#hashObject

Use ID as base for hash



129
130
131
# File 'lib/redpear/model.rb', line 129

def hash
  id.hash
end

#idString

Returns the ID of this record.

Returns:

  • (String)

    the ID of this record



111
112
113
# File 'lib/redpear/model.rb', line 111

def id
  fetch 'id'
end

#increment(name, by = 1) ⇒ Object

Increments the value of a counter attribute

Parameters:

  • name (String)

    The column name to increment

  • by (Integer) (defaults to: 1)

    Increment by this value



170
171
172
173
174
175
# File 'lib/redpear/model.rb', line 170

def increment(name, by = 1)
  column = self.class.columns[name]
  return false unless column && column.type == :counter

  store column, attributes.increment(column, by)
end

#inspectString

Show information about this record

Returns:

  • (String)


233
234
235
# File 'lib/redpear/model.rb', line 233

def inspect
  "#<#{self.class.name} #{super}>"
end

#lookupsArray<Redpear::Store::Enumerable>

Return lookups, relevant to this record

Returns:



217
218
219
# File 'lib/redpear/model.rb', line 217

def lookups
  @_lookups ||= [self.class.members] + self.class.columns.indicies.map {|i| i.for(self) }
end

#update(hash) ⇒ Hash

Bulk-updates the model

Returns:

  • (Hash)


188
189
190
191
192
193
194
195
196
197
198
# File 'lib/redpear/model.rb', line 188

def update(hash)
  clear
  bulk = {}
  hash.each do |name, value|
    column = self.class.columns[name] || next
    store_attribute bulk, column, value
  end
  attributes.merge! bulk
  after_save(hash)
  self
end