class Risky

Constants

VERSION

Attributes

riak_object[RW]
values[RW]

Public Class Methods

[](key, opts = {}) click to toggle source

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
allow_mult() click to toggle source

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
bucket(name = nil) click to toggle source

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
bucket_name() click to toggle source

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
bucket_name=(bucket) click to toggle source
# File lib/risky.rb, line 72
def bucket_name=(bucket)
  @bucket_name = name.to_s
end
cast(data) click to toggle source

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
content_type() click to toggle source
# File lib/risky.rb, line 76
def content_type
  "application/json"
end
create(key = nil, values = {}, opts = {}) click to toggle source
# File lib/risky.rb, line 45
def create(key = nil, values = {}, opts = {})
  new(key, values).save(opts)
end
delete(key, opts = {}) click to toggle source

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
exists?(key) click to toggle source

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
find(key, opts = {}) click to toggle source
# File lib/risky.rb, line 33
def find(key, opts = {})
  self[key.to_s, opts]
end
find_all_by_key(keys) click to toggle source
# 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
from_riak_object(riak_object) click to toggle source

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
get_or_new(*args) click to toggle source

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
map(*args) click to toggle source

Mapreduce helper

# File lib/risky.rb, line 185
def map(*args)
  mr.map(*args)
end
merge(versions) click to toggle source

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
mr(keys = nil) click to toggle source

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
new(key = nil, values = {}) click to toggle source

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’)

Calls superclass method
# 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
reduce(*args) click to toggle source

MR helper.

# File lib/risky.rb, line 213
def reduce(*args)
  mr.reduce(*args)
end
riak() click to toggle source

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
riak!() click to toggle source

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
riak=(client) click to toggle source

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
value(value, opts = {}) click to toggle source

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
values() click to toggle source

A list of all values we track.

# File lib/risky.rb, line 274
def values
  @values ||= {}
end

Public Instance Methods

==(object) click to toggle source
# 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
Also aliased as: eql?
===(object) click to toggle source
# File lib/risky.rb, line 326
def ===(object)
  object.is_a?(self.class)
end
[](k) click to toggle source

Access the values hash.

# File lib/risky.rb, line 331
def [](k)
  values[k]
end
[]=(k, v) click to toggle source

Access the values hash.

# File lib/risky.rb, line 336
def []=(k, v)
  values[k] = v
end
after_create() click to toggle source
# File lib/risky.rb, line 340
def after_create
end
after_delete() click to toggle source
# File lib/risky.rb, line 343
def after_delete
end
after_load() click to toggle source

Called when a riak object is used to populate the instance.

# File lib/risky.rb, line 347
def after_load
end
after_save() click to toggle source
# File lib/risky.rb, line 350
def after_save
end
as_json(opts = {}) click to toggle source
# File lib/risky.rb, line 353
def as_json(opts = {})
  h = @values.merge(:key => key)
  h[:errors] = errors unless errors.empty?
  h
end
before_create() click to toggle source

Called before creation and validation

# File lib/risky.rb, line 360
def before_create
end
before_delete() click to toggle source

Called before deletion

# File lib/risky.rb, line 364
def before_delete
end
before_save() click to toggle source

Called before saving and before validation

# File lib/risky.rb, line 368
def before_save
end
delete() click to toggle source

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
eql?(object)
Alias for: ==
errors() click to toggle source

A hash for errors on this object

# File lib/risky.rb, line 381
def errors
  @errors ||= {}
end
id() click to toggle source
# File lib/risky.rb, line 438
def id
  Integer(key)
rescue ArgumentError
  key
end
id=(value) click to toggle source
# File lib/risky.rb, line 444
def id=(value)
  self.key = value
end
inspect() click to toggle source
# File lib/risky.rb, line 422
def inspect
  "#<#{self.class} #{key} #{@values.inspect}>"
end
key() click to toggle source
# File lib/risky.rb, line 434
def key
  @riak_object.key
end
key=(key) click to toggle source
# 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
load_riak_object(riak_object, opts = {:merge => true}) click to toggle source

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
merged=(merged) click to toggle source
# File lib/risky.rb, line 448
def merged=(merged)
  @merged = !!merged
end
merged?() click to toggle source

Has this model been merged from multiple siblings?

# File lib/risky.rb, line 453
def merged?
  @merged
end
new=(new) click to toggle source
# File lib/risky.rb, line 457
def new=(new)
  @new = !!new
end
new?() click to toggle source

Is this model freshly created; i.e. not saved in the database yet?

# File lib/risky.rb, line 462
def new?
  @new
end
reload(opts = {}) click to toggle source

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
save(opts = {}) click to toggle source

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
to_json(*a) click to toggle source

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
update_attribute(attribute, value) click to toggle source
# File lib/risky.rb, line 511
def update_attribute(attribute, value)
  self.send("#{attribute}=", value)
  self.save
end
update_attributes(attributes) click to toggle source
# File lib/risky.rb, line 516
def update_attributes(attributes)
  attributes.each do |attribute, value|
    self.send("#{attribute}=", value)
  end
  self.save
end
valid?() click to toggle source

Calls validate and checks whether the errors hash is empty.

# File lib/risky.rb, line 535
def valid?
  @errors = {}
  validate
  @errors.empty?
end
validate() click to toggle source

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