class Risky
Constants
- VERSION
Attributes
Public Class Methods
Get a model by key. Returns nil if not found. You can also pass opts to reload
(e.g. :r, :merge => false).
# File lib/risky.rb, line 22 def [](key, opts = {}) return nil unless key begin new(key).reload(opts) rescue Riak::FailedRequest => e raise e unless e.not_found? nil end end
Indicates that this model may be multivalued; in which case .merge should also be defined.
# File lib/risky.rb, line 51 def allow_mult unless bucket.props['allow_mult'] bucket.props = bucket.props.merge('allow_mult' => true) end end
The Riak::Bucket backing this model. If name is passed, sets the bucket name.
# File lib/risky.rb, line 59 def bucket(name = nil) if name @bucket_name = name.to_s end riak.bucket(@bucket_name) end
The string name of the bucket used for storing instances of this model.
# File lib/risky.rb, line 68 def bucket_name @bucket_name end
# File lib/risky.rb, line 72 def bucket_name=(bucket) @bucket_name = name.to_s end
Casts data to appropriate types for values.
# File lib/risky.rb, line 81 def cast(data) casted = {} data.each do |k, v| c = @values[k][:class] rescue nil casted[k] = begin if c == Time Time.iso8601(v) else v end rescue v end end casted end
# File lib/risky.rb, line 76 def content_type "application/json" end
# File lib/risky.rb, line 45 def create(key = nil, values = {}, opts = {}) new(key, values).save(opts) end
Returns true when record deleted. Returns nil when record was not present to begin with.
# File lib/risky.rb, line 100 def delete(key, opts = {}) return if key.nil? bucket.delete(key.to_s, opts) end
Does the given key exist in our bucket?
# File lib/risky.rb, line 106 def exists?(key) return if key.nil? bucket.exists? key.to_s end
# File lib/risky.rb, line 33 def find(key, opts = {}) self[key.to_s, opts] end
# File lib/risky.rb, line 37 def find_all_by_key(keys) return [] if keys.blank? keys.map!(&:to_s) results = bucket.get_many(keys) keys.map { |key| from_riak_object(results[key]) }.compact end
Fills in values from a Riak::RObject
# File lib/risky.rb, line 112 def from_riak_object(riak_object) return nil if riak_object.nil? n = new.load_riak_object riak_object # Callback n.after_load n end
Gets an existing record or creates one.
# File lib/risky.rb, line 123 def get_or_new(*args) self[*args] or new(args.first) end
Establishes methods for manipulating a single link with a given tag.
# File lib/risky.rb, line 128 def link(tag) tag = tag.to_s class_eval %Q{ def #{tag} begin @riak_object.links.find do |l| l.tag == #{tag.inspect} end.key rescue NoMethodError nil end end def #{tag}=(link) @riak_object.links.reject! do |l| l.tag == #{tag.inspect} end if link @riak_object.links << link.to_link(#{tag.inspect}) end end } end
Establishes methods for manipulating a set of links with a given tag.
# File lib/risky.rb, line 153 def links(tag) tag = tag.to_s class_eval %Q{ def #{tag} @riak_object.links.select do |l| l.tag == #{tag.inspect} end.map do |l| l.key end end def add_#{tag}(link) @riak_object.links << link.to_link(#{tag.inspect}) end def remove_#{tag}(link) @riak_object.links.delete link.to_link(#{tag.inspect}) end def clear_#{tag} @riak_object.links.delete_if do |l| l.tag == #{tag.inspect} end end def #{tag}_count @riak_object.links.select{|l| l.tag == #{tag.inspect}}.length end } end
Mapreduce helper
# File lib/risky.rb, line 185 def map(*args) mr.map(*args) end
Merges n versions of a record together, for read-repair. Returns the merged record.
# File lib/risky.rb, line 191 def merge(versions) versions.first end
Begins a mapreduce on this model’s bucket. If no keys are given, operates on the entire bucket. If keys are given, operates on those keys first.
# File lib/risky.rb, line 198 def mr(keys = nil) mr = Riak::MapReduce.new(riak) if keys # Add specific keys [*keys].compact.inject(mr) do |mr, key| mr.add @bucket_name, key.to_s end else # Add whole bucket mr.add @bucket_name end end
Create a new instance from a key and a list of values.
Values will be passed to attr= methods where possible, so you can write def password=(p)
self['password'] = md5sum p
end User.new(‘me’, :password => ‘baggins’)
# File lib/risky.rb, line 291 def initialize(key = nil, values = {}) super() key = key.to_s unless key.nil? @riak_object ||= Riak::RObject.new(self.class.bucket, key) @riak_object.content_type = self.class.content_type @new = true @merged = false @values = {} # Load values values.each do |k,v| begin send(k.to_s + '=', v) rescue NoMethodError self[k] = v end end # Fill in defults. self.class.values.each do |k,v| if self[k].nil? self[k] = (v[:default].clone rescue v[:default]) end end end
MR helper.
# File lib/risky.rb, line 213 def reduce(*args) mr.reduce(*args) end
The Riak::Client backing this model class.
# File lib/risky.rb, line 218 def riak Thread.current[:riak_client] ||= if @riak and @riak.respond_to?(:call) @riak.call(self) elsif @riak @riak else superclass.riak end end
Forces Riak client for this thread to be reset. If your @riak proc can choose between multiple hosts, calling this on failure will allow subsequent requests to proceed on another host.
# File lib/risky.rb, line 232 def riak! Thread.current[:riak_client] = nil riak end
Sets the Riak Client backing this model class. If client is a lambda (or anything responding to call), it will be invoked to generate a new client every time Risky
feels it is appropriate.
# File lib/risky.rb, line 240 def riak=(client) @riak = client end
Add a new value to this model. Values aren’t necessary; you can use Risky#[]
, but if you would like to cast values to/from JSON or specify defaults, you may:
:default => object (clone is called for each new instance) :class => Time, Integer, etc. Inferred from default.class if present.
# File lib/risky.rb, line 250 def value(value, opts = {}) value = value.to_s klass = if opts[:class] opts[:class] elsif opts.include? :default opts[:default].class else nil end values[value] = opts.merge(:class => klass) class_eval %Q{ def #{value} @values[#{value.inspect}] end def #{value}=(value) @values[#{value.inspect}] = value end } end
A list of all values we track.
# File lib/risky.rb, line 274 def values @values ||= {} end
Public Instance Methods
# File lib/risky.rb, line 320 def ==(object) object.class == self.class && (object.key.present? && object.key == self.key || object.object_id == self.object_id) end
# File lib/risky.rb, line 326 def ===(object) object.is_a?(self.class) end
Access the values hash.
# File lib/risky.rb, line 331 def [](k) values[k] end
Access the values hash.
# File lib/risky.rb, line 336 def []=(k, v) values[k] = v end
# File lib/risky.rb, line 340 def after_create end
# File lib/risky.rb, line 343 def after_delete end
Called when a riak object is used to populate the instance.
# File lib/risky.rb, line 347 def after_load end
# File lib/risky.rb, line 350 def after_save end
# File lib/risky.rb, line 353 def as_json(opts = {}) h = @values.merge(:key => key) h[:errors] = errors unless errors.empty? h end
Called before creation and validation
# File lib/risky.rb, line 360 def before_create end
Called before deletion
# File lib/risky.rb, line 364 def before_delete end
Called before saving and before validation
# File lib/risky.rb, line 368 def before_save end
Delete this object in the DB and return self.
# File lib/risky.rb, line 372 def delete before_delete @riak_object.delete after_delete self end
A hash for errors on this object
# File lib/risky.rb, line 381 def errors @errors ||= {} end
# File lib/risky.rb, line 438 def id Integer(key) rescue ArgumentError key end
# File lib/risky.rb, line 444 def id=(value) self.key = value end
# File lib/risky.rb, line 422 def inspect "#<#{self.class} #{key} #{@values.inspect}>" end
# File lib/risky.rb, line 434 def key @riak_object.key end
# File lib/risky.rb, line 426 def key=(key) if key.nil? @riak_object.key = nil else @riak_object.key = key.to_s end end
Replaces values and riak_object
with data from riak_object.
# File lib/risky.rb, line 386 def load_riak_object(riak_object, opts = {:merge => true}) if opts[:merge] and riak_object.conflict? and siblings = riak_object.siblings # Engage conflict resolution mode final = self.class.merge( siblings.map do |sibling| robject = Riak::RObject.new(sibling.bucket, sibling.key) robject.raw_data = sibling.raw_data robject.content_type = sibling.content_type # robject.siblings = [sibling] robject.vclock = sibling.vclock self.class.new.load_riak_object(robject, :merge => false) end ) # Copy final values to self. final.instance_variables.each do |var| self.instance_variable_set(var, final.instance_variable_get(var)) end self.merged = true else # Not merging self.values = self.class.cast(riak_object.data) rescue {} self.class.values.each do |k, v| if values[k].nil? values[k] = (v[:default].clone rescue v[:default]) end end self.riak_object = riak_object self.new = false self.merged = false end self end
# File lib/risky.rb, line 448 def merged=(merged) @merged = !!merged end
Has this model been merged from multiple siblings?
# File lib/risky.rb, line 453 def merged? @merged end
# File lib/risky.rb, line 457 def new=(new) @new = !!new end
Is this model freshly created; i.e. not saved in the database yet?
# File lib/risky.rb, line 462 def new? @new end
Reload this model’s data from Riak. opts are passed to Riak::Bucket[]
# File lib/risky.rb, line 468 def reload(opts = {}) # Get object from riak. riak_object = self.class.bucket[key, opts] # Load load_riak_object riak_object # Callback after_load self end
Saves this model.
Calls validate
and valid?
unless :validate is false.
Converts @values to_json
and saves it to riak.
:w and :dw are also supported.
# File lib/risky.rb, line 487 def save(opts = {}) before_create if @new before_save unless opts[:validate] == false return false unless valid? end @riak_object.data = @values @riak_object.content_type = self.class.content_type store_opts = {} store_opts[:w] = opts[:w] if opts[:w] store_opts[:dw] = opts[:dw] if opts[:dw] @riak_object.store store_opts after_create if @new after_save @new = false self end
This is provided for convenience; save
does not use this method, and you are free to override it.
# File lib/risky.rb, line 525 def to_json(*a) as_json.to_json(*a) end
Returns a Riak::Link object pointing to this record.
# File lib/risky.rb, line 530 def to_link(*a) @riak_object.to_link(*a) end
# File lib/risky.rb, line 511 def update_attribute(attribute, value) self.send("#{attribute}=", value) self.save end
# File lib/risky.rb, line 516 def update_attributes(attributes) attributes.each do |attribute, value| self.send("#{attribute}=", value) end self.save end
Calls validate
and checks whether the errors hash is empty.
# File lib/risky.rb, line 535 def valid? @errors = {} validate @errors.empty? end
Determines whether the model is valid. Sets the contents of errors
if invalid.
# File lib/risky.rb, line 543 def validate if key.blank? errors[:key] = 'is missing' end end