Class: Tractor::Model::Base

Inherits:
Object
  • Object
show all
Includes:
Dirty
Defined in:
lib/tractor/model/base.rb

Direct Known Subclasses

Mapper

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Dirty

key, #mark

Constructor Details

#initialize(attributes = {}) ⇒ Base



105
106
107
108
109
110
111
# File 'lib/tractor/model/base.rb', line 105

def initialize(attributes={})
  @attribute_store = {}
  @association_store = {}
  attributes.each do |k,v|
    send("#{k}=", v)
  end
end

Class Attribute Details

.associationsObject (readonly)

Returns the value of attribute associations.



177
178
179
# File 'lib/tractor/model/base.rb', line 177

def associations
  @associations
end

.attributesObject (readonly)

Returns the value of attribute attributes.



177
178
179
# File 'lib/tractor/model/base.rb', line 177

def attributes
  @attributes
end

.callbacksObject (readonly)

Returns the value of attribute callbacks.



177
178
179
# File 'lib/tractor/model/base.rb', line 177

def callbacks
  @callbacks
end

.indicesObject (readonly)

Returns the value of attribute indices.



177
178
179
# File 'lib/tractor/model/base.rb', line 177

def indices
  @indices
end

Class Method Details

.after_create(name) ⇒ Object



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

def after_create(name)
  callbacks[:after_create] << name
end

.after_destroy(name) ⇒ Object



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

def after_destroy(name)
  callbacks[:after_destroy] << name
end

.after_save(name) ⇒ Object



187
188
189
# File 'lib/tractor/model/base.rb', line 187

def after_save(name)
  callbacks[:after_save] << name
end

.allObject



267
268
269
# File 'lib/tractor/model/base.rb', line 267

def all
  ids.inject([]){|a, id| a << find_by_id(id); a }
end

.association(name, klass) ⇒ Object

make an assumption about the foreign_key… probably bad :)



251
252
253
254
255
256
257
# File 'lib/tractor/model/base.rb', line 251

def association(name, klass)
  foreign_key_name = "#{self.to_s.gsub(/^.*::/, '').downcase}_id"
  klass.associations[self.name] = {:foreign_key_name => foreign_key_name, :set_name => name}
  define_method(name) do
    @association_store[name] = Association.new("#{self.class}:#{self.id}:#{name}", klass)
  end
end

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



238
239
240
241
242
243
244
# File 'lib/tractor/model/base.rb', line 238

def attribute(name, options={})
  options[:map] = name unless options[:map]
  attributes[name] = options
  setter(name, options[:type])
  getter(name, options[:type])
  index(name) if options[:index]
end

.countObject



263
264
265
# File 'lib/tractor/model/base.rb', line 263

def count
  Tractor.redis.scard("#{self}:all")
end

.create(attributes = {}) ⇒ Object

Raises:



197
198
199
200
201
202
203
# File 'lib/tractor/model/base.rb', line 197

def create(attributes={})
  raise DuplicateKeyError, "Duplicate value for #{self} 'id'" if Tractor.redis.sismember("#{self}:all", attributes[:id])
  m = new(attributes)
  m.save
  run_callbacks(:after_create, m)
  m
end

.exists?(id) ⇒ Boolean



205
206
207
# File 'lib/tractor/model/base.rb', line 205

def exists?(id)
  Tractor.redis.sismember("#{self}:all", id)
end

.find(options = {}) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/tractor/model/base.rb', line 221

def find(options = {})
  return [] if options.empty?
  unions = []
  sets = options.map do |name, value|
    if value.is_a?(Array) && value.any?
      unions << union_name = "#{value}-#{Time.now.to_f}"
      Tractor.redis.sunionstore(union_name, *value.map{|v| Index.key_for(self, name, v) })
      union_name
    else
      Index.key_for(self, name, value)
    end
  end
  ids = Tractor.redis.sinter(*sets) || []
  Tractor.redis.del(unions.join(","))
  ids.map {|id| find_by_id(id) }
end

.find_by_attribute(name, value) ⇒ Object

use method missing to do craziness, or define a find_by on each index (BETTER)

Raises:



216
217
218
219
# File 'lib/tractor/model/base.rb', line 216

def find_by_attribute(name, value)
  raise MissingIndexError, "No index on '#{name}'" unless indices.include?(name)
  find({name => value})
end

.find_by_id(id) ⇒ Object



209
210
211
212
213
# File 'lib/tractor/model/base.rb', line 209

def find_by_id(id)
  obj_data = Tractor.redis.mapped_hmget("#{self}:#{id}", *attributes.keys)
  return nil if obj_data.values.compact.empty?
  new(obj_data)
end

.getter(name, type) ⇒ Object

Minions



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

def getter(name, type)
  define_method(name) do
    value = @attribute_store[name]
    if type == :integer
      value.to_i
    elsif type == :boolean
      value.to_s.match(/(true|1)$/i) != nil
    else
      value
    end
  end
end

.idsObject



259
260
261
# File 'lib/tractor/model/base.rb', line 259

def ids
  Tractor.redis.smembers("#{self}:all") || []
end

.index(name) ⇒ Object



246
247
248
# File 'lib/tractor/model/base.rb', line 246

def index(name)
  indices << name unless indices.include?(name)
end

.run_callbacks(type, obj) ⇒ Object



191
192
193
194
195
# File 'lib/tractor/model/base.rb', line 191

def run_callbacks(type, obj)
  callbacks[type].each do |cb|
    obj.send(cb)
  end
end

.setter(name, type) ⇒ Object



288
289
290
291
292
293
294
295
# File 'lib/tractor/model/base.rb', line 288

def setter(name, type)
  define_method(:"#{name}=") do |value|
    if type == :boolean
      value = value.to_s
    end
    @attribute_store[name] = value
  end
end

Instance Method Details

#add_to_associationsObject



141
142
143
144
145
# File 'lib/tractor/model/base.rb', line 141

def add_to_associations
  for_each_associated_foreign_instance do |foreign_instance|
    foreign_instance.push(self)
  end
end

#add_to_indicesObject



153
154
155
156
157
158
# File 'lib/tractor/model/base.rb', line 153

def add_to_indices
  self.class.indices.each do |name|
    index = Index.new(self.class, name, send(name))
    index.insert(self.id)
  end
end

#delete_from_indices(attributes) ⇒ Object



160
161
162
163
164
165
166
167
# File 'lib/tractor/model/base.rb', line 160

def delete_from_indices(attributes)
  attributes.each do |name, value|
    if self.class.indices.include?(name.to_sym)
      index = Index.new(self.class, name, self.send(name))
      index.delete(self.id)
    end
  end
end

#destroyObject



126
127
128
129
130
131
132
# File 'lib/tractor/model/base.rb', line 126

def destroy
  delete_from_indices(attribute_store)
  remove_from_associations
  Tractor.redis.srem("#{self.class}:all", self.id)
  Tractor.redis.del "#{self.class}:#{self.id}"
  self.class.run_callbacks(:after_destroy, self)
end

#remove_from_associationsObject



147
148
149
150
151
# File 'lib/tractor/model/base.rb', line 147

def remove_from_associations
  for_each_associated_foreign_instance do |foreign_instance|
    foreign_instance.delete(self.id)
  end
end

#saveObject

Raises:



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/tractor/model/base.rb', line 113

def save
  raise MissingIdError, "Probably wanna set an id" if self.id.nil? || self.id.to_s.empty?

  Tractor.redis.mapped_hmset("#{self.class}:#{self.id}", self.send(:attribute_store))
  Tractor.redis.sadd "#{self.class}:all", self.id
  add_to_indices
  add_to_associations
  mark
  self.class.run_callbacks(:after_save, self)
  
  return self
end

#to_hObject



169
170
171
172
173
174
# File 'lib/tractor/model/base.rb', line 169

def to_h
  self.class.attributes.keys.inject({}) do |h, attribute|
    h[attribute.to_sym] = self.send(attribute)
    h
  end
end

#update(attributes = {}) ⇒ Object



134
135
136
137
138
139
# File 'lib/tractor/model/base.rb', line 134

def update(attributes = {})
  attributes.delete(:id)
  delete_from_indices(attributes)
  attributes.each{|k,v| self.send("#{k}=", v) }
  save
end