Class: Risky
- Inherits:
-
Object
- Object
- Risky
- Extended by:
- Enumerable
- Defined in:
- lib/risky.rb,
lib/risky/version.rb
Defined Under Namespace
Modules: CronList, GZip, Indexes, Inflector, ListKeys, Resolver, SecondaryIndexes, Timestamps Classes: Invalid, NotFound, PaginatedCollection
Constant Summary collapse
- VERSION =
"1.1.0"
Instance Attribute Summary collapse
-
#riak_object ⇒ Object
Returns the value of attribute riak_object.
-
#values ⇒ Object
Returns the value of attribute values.
Class Method Summary collapse
-
.[](key, opts = {}) ⇒ Object
Get a model by key.
-
.allow_mult ⇒ Object
Indicates that this model may be multivalued; in which case .merge should also be defined.
-
.bucket(name = nil) ⇒ Object
The Riak::Bucket backing this model.
-
.bucket_name ⇒ Object
The string name of the bucket used for storing instances of this model.
- .bucket_name=(bucket) ⇒ Object
-
.cast(data) ⇒ Object
Casts data to appropriate types for values.
- .content_type ⇒ Object
- .create(key = nil, values = {}, opts = {}) ⇒ Object
-
.delete(key, opts = {}) ⇒ Object
Returns true when record deleted.
-
.exists?(key) ⇒ Boolean
Does the given key exist in our bucket?.
- .find(key, opts = {}) ⇒ Object
- .find_all_by_key(keys) ⇒ Object
-
.from_riak_object(riak_object) ⇒ Object
Fills in values from a Riak::RObject.
-
.get_or_new(*args) ⇒ Object
Gets an existing record or creates one.
-
.link(tag) ⇒ Object
Establishes methods for manipulating a single link with a given tag.
-
.links(tag) ⇒ Object
Establishes methods for manipulating a set of links with a given tag.
-
.map(*args) ⇒ Object
Mapreduce helper.
-
.merge(versions) ⇒ Object
Merges n versions of a record together, for read-repair.
-
.mr(keys = nil) ⇒ Object
Begins a mapreduce on this model’s bucket.
-
.reduce(*args) ⇒ Object
MR helper.
-
.riak ⇒ Object
The Riak::Client backing this model class.
-
.riak! ⇒ Object
Forces Riak client for this thread to be reset.
-
.riak=(client) ⇒ Object
Sets the Riak Client backing this model class.
-
.value(value, opts = {}) ⇒ Object
Add a new value to this model.
-
.values ⇒ Object
A list of all values we track.
Instance Method Summary collapse
- #==(object) ⇒ Object (also: #eql?)
- #===(object) ⇒ Object
-
#[](k) ⇒ Object
Access the values hash.
-
#[]=(k, v) ⇒ Object
Access the values hash.
- #after_create ⇒ Object
- #after_delete ⇒ Object
-
#after_load ⇒ Object
Called when a riak object is used to populate the instance.
- #after_save ⇒ Object
- #as_json(opts = {}) ⇒ Object
-
#before_create ⇒ Object
Called before creation and validation.
-
#before_delete ⇒ Object
Called before deletion.
-
#before_save ⇒ Object
Called before saving and before validation.
-
#delete ⇒ Object
Delete this object in the DB and return self.
-
#errors ⇒ Object
A hash for errors on this object.
- #id ⇒ Object
- #id=(value) ⇒ Object
-
#initialize(key = nil, values = {}) ⇒ Risky
constructor
Create a new instance from a key and a list of values.
- #inspect ⇒ Object
- #key ⇒ Object
- #key=(key) ⇒ Object
-
#load_riak_object(riak_object, opts = {:merge => true}) ⇒ Object
Replaces values and riak_object with data from riak_object.
- #merged=(merged) ⇒ Object
-
#merged? ⇒ Boolean
Has this model been merged from multiple siblings?.
- #new=(new) ⇒ Object
-
#new? ⇒ Boolean
Is this model freshly created; i.e.
-
#reload(opts = {}) ⇒ Object
Reload this model’s data from Riak.
-
#save(opts = {}) ⇒ Object
Saves this model.
-
#to_json(*a) ⇒ Object
This is provided for convenience; #save does not use this method, and you are free to override it.
-
#to_link(*a) ⇒ Object
Returns a Riak::Link object pointing to this record.
- #update_attribute(attribute, value) ⇒ Object
- #update_attributes(attributes) ⇒ Object
-
#valid? ⇒ Boolean
Calls #validate and checks whether the errors hash is empty.
-
#validate ⇒ Object
Determines whether the model is valid.
Constructor Details
#initialize(key = nil, values = {}) ⇒ Risky
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’)
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 |
# 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 |
Instance Attribute Details
#riak_object ⇒ Object
Returns the value of attribute riak_object.
282 283 284 |
# File 'lib/risky.rb', line 282 def riak_object @riak_object end |
#values ⇒ Object
Returns the value of attribute values.
281 282 283 |
# File 'lib/risky.rb', line 281 def values @values end |
Class Method Details
.[](key, opts = {}) ⇒ Object
Get a model by key. Returns nil if not found. You can also pass opts to #reload (e.g. :r, :merge => false).
22 23 24 25 26 27 28 29 30 31 |
# 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 ⇒ Object
Indicates that this model may be multivalued; in which case .merge should also be defined.
51 52 53 54 55 |
# 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) ⇒ Object
The Riak::Bucket backing this model. If name is passed, sets the bucket name.
59 60 61 62 63 64 65 |
# 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 ⇒ Object
The string name of the bucket used for storing instances of this model.
68 69 70 |
# File 'lib/risky.rb', line 68 def bucket_name @bucket_name end |
.bucket_name=(bucket) ⇒ Object
72 73 74 |
# File 'lib/risky.rb', line 72 def bucket_name=(bucket) @bucket_name = name.to_s end |
.cast(data) ⇒ Object
Casts data to appropriate types for values.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# 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 ⇒ Object
76 77 78 |
# File 'lib/risky.rb', line 76 def content_type "application/json" end |
.create(key = nil, values = {}, opts = {}) ⇒ Object
45 46 47 |
# File 'lib/risky.rb', line 45 def create(key = nil, values = {}, opts = {}) new(key, values).save(opts) end |
.delete(key, opts = {}) ⇒ Object
Returns true when record deleted. Returns nil when record was not present to begin with.
100 101 102 103 |
# File 'lib/risky.rb', line 100 def delete(key, opts = {}) return if key.nil? bucket.delete(key.to_s, opts) end |
.exists?(key) ⇒ Boolean
Does the given key exist in our bucket?
106 107 108 109 |
# File 'lib/risky.rb', line 106 def exists?(key) return if key.nil? bucket.exists? key.to_s end |
.find(key, opts = {}) ⇒ Object
33 34 35 |
# File 'lib/risky.rb', line 33 def find(key, opts = {}) self[key.to_s, opts] end |
.find_all_by_key(keys) ⇒ Object
37 38 39 40 41 42 43 |
# 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) ⇒ Object
Fills in values from a Riak::RObject
112 113 114 115 116 117 118 119 120 |
# 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) ⇒ Object
Gets an existing record or creates one.
123 124 125 |
# File 'lib/risky.rb', line 123 def get_or_new(*args) self[*args] or new(args.first) end |
.link(tag) ⇒ Object
Establishes methods for manipulating a single link with a given tag.
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# 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 |
.links(tag) ⇒ Object
Establishes methods for manipulating a set of links with a given tag.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# 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 |
.map(*args) ⇒ Object
Mapreduce helper
185 186 187 |
# File 'lib/risky.rb', line 185 def map(*args) mr.map(*args) end |
.merge(versions) ⇒ Object
Merges n versions of a record together, for read-repair. Returns the merged record.
191 192 193 |
# File 'lib/risky.rb', line 191 def merge(versions) versions.first end |
.mr(keys = nil) ⇒ Object
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.
198 199 200 201 202 203 204 205 206 207 208 209 210 |
# 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 |
.reduce(*args) ⇒ Object
MR helper.
213 214 215 |
# File 'lib/risky.rb', line 213 def reduce(*args) mr.reduce(*args) end |
.riak ⇒ Object
The Riak::Client backing this model class.
218 219 220 221 222 223 224 225 226 227 |
# 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! ⇒ Object
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.
232 233 234 235 |
# File 'lib/risky.rb', line 232 def riak! Thread.current[:riak_client] = nil riak end |
.riak=(client) ⇒ Object
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.
240 241 242 |
# File 'lib/risky.rb', line 240 def riak=(client) @riak = client end |
.value(value, opts = {}) ⇒ Object
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.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# 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 ⇒ Object
A list of all values we track.
274 275 276 |
# File 'lib/risky.rb', line 274 def values @values ||= {} end |
Instance Method Details
#==(object) ⇒ Object Also known as: eql?
320 321 322 323 |
# 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 |
#===(object) ⇒ Object
326 327 328 |
# File 'lib/risky.rb', line 326 def ===(object) object.is_a?(self.class) end |
#[](k) ⇒ Object
Access the values hash.
331 332 333 |
# File 'lib/risky.rb', line 331 def [](k) values[k] end |
#[]=(k, v) ⇒ Object
Access the values hash.
336 337 338 |
# File 'lib/risky.rb', line 336 def []=(k, v) values[k] = v end |
#after_create ⇒ Object
340 341 |
# File 'lib/risky.rb', line 340 def after_create end |
#after_delete ⇒ Object
343 344 |
# File 'lib/risky.rb', line 343 def after_delete end |
#after_load ⇒ Object
Called when a riak object is used to populate the instance.
347 348 |
# File 'lib/risky.rb', line 347 def after_load end |
#after_save ⇒ Object
350 351 |
# File 'lib/risky.rb', line 350 def after_save end |
#as_json(opts = {}) ⇒ Object
353 354 355 356 357 |
# 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 ⇒ Object
Called before creation and validation
360 361 |
# File 'lib/risky.rb', line 360 def before_create end |
#before_delete ⇒ Object
Called before deletion
364 365 |
# File 'lib/risky.rb', line 364 def before_delete end |
#before_save ⇒ Object
Called before saving and before validation
368 369 |
# File 'lib/risky.rb', line 368 def before_save end |
#delete ⇒ Object
Delete this object in the DB and return self.
372 373 374 375 376 377 378 |
# File 'lib/risky.rb', line 372 def delete before_delete @riak_object.delete after_delete self end |
#errors ⇒ Object
A hash for errors on this object
381 382 383 |
# File 'lib/risky.rb', line 381 def errors @errors ||= {} end |
#id ⇒ Object
438 439 440 441 442 |
# File 'lib/risky.rb', line 438 def id Integer(key) rescue ArgumentError key end |
#id=(value) ⇒ Object
444 445 446 |
# File 'lib/risky.rb', line 444 def id=(value) self.key = value end |
#inspect ⇒ Object
422 423 424 |
# File 'lib/risky.rb', line 422 def inspect "#<#{self.class} #{key} #{@values.inspect}>" end |
#key ⇒ Object
434 435 436 |
# File 'lib/risky.rb', line 434 def key @riak_object.key end |
#key=(key) ⇒ Object
426 427 428 429 430 431 432 |
# 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}) ⇒ Object
Replaces values and riak_object with data from riak_object.
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 |
# 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) ⇒ Object
448 449 450 |
# File 'lib/risky.rb', line 448 def merged=(merged) @merged = !!merged end |
#merged? ⇒ Boolean
Has this model been merged from multiple siblings?
453 454 455 |
# File 'lib/risky.rb', line 453 def merged? @merged end |
#new=(new) ⇒ Object
457 458 459 |
# File 'lib/risky.rb', line 457 def new=(new) @new = !!new end |
#new? ⇒ Boolean
Is this model freshly created; i.e. not saved in the database yet?
462 463 464 |
# File 'lib/risky.rb', line 462 def new? @new end |
#reload(opts = {}) ⇒ Object
Reload this model’s data from Riak. opts are passed to Riak::Bucket[]
468 469 470 471 472 473 474 475 476 477 478 |
# 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 = {}) ⇒ Object
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.
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
# 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) ⇒ Object
This is provided for convenience; #save does not use this method, and you are free to override it.
525 526 527 |
# File 'lib/risky.rb', line 525 def to_json(*a) as_json.to_json(*a) end |
#to_link(*a) ⇒ Object
Returns a Riak::Link object pointing to this record.
530 531 532 |
# File 'lib/risky.rb', line 530 def to_link(*a) @riak_object.to_link(*a) end |
#update_attribute(attribute, value) ⇒ Object
511 512 513 514 |
# File 'lib/risky.rb', line 511 def update_attribute(attribute, value) self.send("#{attribute}=", value) self.save end |
#update_attributes(attributes) ⇒ Object
516 517 518 519 520 521 |
# File 'lib/risky.rb', line 516 def update_attributes(attributes) attributes.each do |attribute, value| self.send("#{attribute}=", value) end self.save end |
#valid? ⇒ Boolean
Calls #validate and checks whether the errors hash is empty.
535 536 537 538 539 |
# File 'lib/risky.rb', line 535 def valid? @errors = {} validate @errors.empty? end |
#validate ⇒ Object
Determines whether the model is valid. Sets the contents of #errors if invalid.
543 544 545 546 547 |
# File 'lib/risky.rb', line 543 def validate if key.blank? errors[:key] = 'is missing' end end |