seomoz-ripple 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +20 -0
- data/Guardfile +15 -0
- data/Rakefile +88 -0
- data/lib/rails/generators/ripple/configuration/configuration_generator.rb +13 -0
- data/lib/rails/generators/ripple/configuration/templates/ripple.yml +24 -0
- data/lib/rails/generators/ripple/js/js_generator.rb +13 -0
- data/lib/rails/generators/ripple/js/templates/js/contrib.js +63 -0
- data/lib/rails/generators/ripple/js/templates/js/iso8601.js +76 -0
- data/lib/rails/generators/ripple/js/templates/js/ripple.js +132 -0
- data/lib/rails/generators/ripple/model/model_generator.rb +20 -0
- data/lib/rails/generators/ripple/model/templates/model.rb +10 -0
- data/lib/rails/generators/ripple/observer/observer_generator.rb +16 -0
- data/lib/rails/generators/ripple/observer/templates/observer.rb +4 -0
- data/lib/rails/generators/ripple/test/templates/test_server.rb +46 -0
- data/lib/rails/generators/ripple/test/test_generator.rb +39 -0
- data/lib/rails/generators/ripple_generator.rb +78 -0
- data/lib/ripple.rb +79 -0
- data/lib/ripple/associations.rb +356 -0
- data/lib/ripple/associations/embedded.rb +35 -0
- data/lib/ripple/associations/instantiators.rb +26 -0
- data/lib/ripple/associations/linked.rb +65 -0
- data/lib/ripple/associations/many.rb +38 -0
- data/lib/ripple/associations/many_embedded_proxy.rb +38 -0
- data/lib/ripple/associations/many_linked_proxy.rb +66 -0
- data/lib/ripple/associations/many_reference_proxy.rb +93 -0
- data/lib/ripple/associations/many_stored_key_proxy.rb +76 -0
- data/lib/ripple/associations/one.rb +20 -0
- data/lib/ripple/associations/one_embedded_proxy.rb +35 -0
- data/lib/ripple/associations/one_key_proxy.rb +58 -0
- data/lib/ripple/associations/one_linked_proxy.rb +22 -0
- data/lib/ripple/associations/one_stored_key_proxy.rb +43 -0
- data/lib/ripple/associations/proxy.rb +118 -0
- data/lib/ripple/attribute_methods.rb +118 -0
- data/lib/ripple/attribute_methods/dirty.rb +50 -0
- data/lib/ripple/attribute_methods/query.rb +34 -0
- data/lib/ripple/attribute_methods/read.rb +26 -0
- data/lib/ripple/attribute_methods/write.rb +25 -0
- data/lib/ripple/callbacks.rb +74 -0
- data/lib/ripple/conflict/basic_resolver.rb +82 -0
- data/lib/ripple/conflict/document_hooks.rb +20 -0
- data/lib/ripple/conflict/resolver.rb +71 -0
- data/lib/ripple/conflict/test_helper.rb +33 -0
- data/lib/ripple/conversion.rb +28 -0
- data/lib/ripple/core_ext.rb +2 -0
- data/lib/ripple/core_ext/casting.rb +148 -0
- data/lib/ripple/core_ext/object.rb +8 -0
- data/lib/ripple/document.rb +104 -0
- data/lib/ripple/document/bucket_access.rb +25 -0
- data/lib/ripple/document/finders.rb +131 -0
- data/lib/ripple/document/key.rb +43 -0
- data/lib/ripple/document/link.rb +30 -0
- data/lib/ripple/document/persistence.rb +115 -0
- data/lib/ripple/embedded_document.rb +64 -0
- data/lib/ripple/embedded_document/around_callbacks.rb +18 -0
- data/lib/ripple/embedded_document/finders.rb +26 -0
- data/lib/ripple/embedded_document/persistence.rb +77 -0
- data/lib/ripple/i18n.rb +2 -0
- data/lib/ripple/inspection.rb +32 -0
- data/lib/ripple/locale/en.yml +21 -0
- data/lib/ripple/nested_attributes.rb +265 -0
- data/lib/ripple/observable.rb +28 -0
- data/lib/ripple/properties.rb +73 -0
- data/lib/ripple/property_type_mismatch.rb +12 -0
- data/lib/ripple/railtie.rb +13 -0
- data/lib/ripple/serialization.rb +84 -0
- data/lib/ripple/timestamps.rb +27 -0
- data/lib/ripple/translation.rb +14 -0
- data/lib/ripple/validations.rb +67 -0
- data/lib/ripple/validations/associated_validator.rb +43 -0
- data/ripple.gemspec +46 -0
- data/seomoz-ripple.gemspec +46 -0
- data/spec/fixtures/config.yml +8 -0
- data/spec/integration/ripple/associations_spec.rb +220 -0
- data/spec/integration/ripple/conflict_resolution_spec.rb +293 -0
- data/spec/integration/ripple/nested_attributes_spec.rb +264 -0
- data/spec/integration/ripple/persistence_spec.rb +57 -0
- data/spec/integration/ripple/search_associations_spec.rb +42 -0
- data/spec/ripple/associations/many_embedded_proxy_spec.rb +122 -0
- data/spec/ripple/associations/many_linked_proxy_spec.rb +191 -0
- data/spec/ripple/associations/many_reference_proxy_spec.rb +170 -0
- data/spec/ripple/associations/many_stored_key_proxy_spec.rb +158 -0
- data/spec/ripple/associations/one_embedded_proxy_spec.rb +125 -0
- data/spec/ripple/associations/one_key_proxy_spec.rb +82 -0
- data/spec/ripple/associations/one_linked_proxy_spec.rb +91 -0
- data/spec/ripple/associations/one_stored_key_proxy_spec.rb +72 -0
- data/spec/ripple/associations/proxy_spec.rb +84 -0
- data/spec/ripple/associations_spec.rb +129 -0
- data/spec/ripple/attribute_methods/dirty_spec.rb +80 -0
- data/spec/ripple/attribute_methods_spec.rb +230 -0
- data/spec/ripple/bucket_access_spec.rb +25 -0
- data/spec/ripple/callbacks_spec.rb +176 -0
- data/spec/ripple/conflict/resolver_spec.rb +42 -0
- data/spec/ripple/conversion_spec.rb +22 -0
- data/spec/ripple/core_ext_spec.rb +103 -0
- data/spec/ripple/document/link_spec.rb +67 -0
- data/spec/ripple/document_spec.rb +96 -0
- data/spec/ripple/embedded_document/finders_spec.rb +29 -0
- data/spec/ripple/embedded_document/persistence_spec.rb +80 -0
- data/spec/ripple/embedded_document_spec.rb +84 -0
- data/spec/ripple/finders_spec.rb +217 -0
- data/spec/ripple/inspection_spec.rb +51 -0
- data/spec/ripple/key_spec.rb +30 -0
- data/spec/ripple/observable_spec.rb +121 -0
- data/spec/ripple/persistence_spec.rb +326 -0
- data/spec/ripple/properties_spec.rb +262 -0
- data/spec/ripple/ripple_spec.rb +71 -0
- data/spec/ripple/serialization_spec.rb +51 -0
- data/spec/ripple/timestamps_spec.rb +76 -0
- data/spec/ripple/validations/associated_validator_spec.rb +77 -0
- data/spec/ripple/validations_spec.rb +104 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/associations.rb +1 -0
- data/spec/support/associations/proxies.rb +17 -0
- data/spec/support/integration_setup.rb +11 -0
- data/spec/support/mocks.rb +4 -0
- data/spec/support/models.rb +4 -0
- data/spec/support/models/address.rb +12 -0
- data/spec/support/models/box.rb +13 -0
- data/spec/support/models/car.rb +16 -0
- data/spec/support/models/cardboard_box.rb +3 -0
- data/spec/support/models/clock.rb +12 -0
- data/spec/support/models/clock_observer.rb +3 -0
- data/spec/support/models/company.rb +23 -0
- data/spec/support/models/customer.rb +4 -0
- data/spec/support/models/driver.rb +6 -0
- data/spec/support/models/email.rb +4 -0
- data/spec/support/models/engine.rb +5 -0
- data/spec/support/models/family.rb +16 -0
- data/spec/support/models/favorite.rb +4 -0
- data/spec/support/models/invoice.rb +7 -0
- data/spec/support/models/late_invoice.rb +3 -0
- data/spec/support/models/ninja.rb +9 -0
- data/spec/support/models/note.rb +5 -0
- data/spec/support/models/page.rb +4 -0
- data/spec/support/models/paid_invoice.rb +4 -0
- data/spec/support/models/passenger.rb +6 -0
- data/spec/support/models/profile.rb +10 -0
- data/spec/support/models/seat.rb +5 -0
- data/spec/support/models/subscription.rb +27 -0
- data/spec/support/models/tasks.rb +14 -0
- data/spec/support/models/team.rb +11 -0
- data/spec/support/models/transactions.rb +17 -0
- data/spec/support/models/tree.rb +4 -0
- data/spec/support/models/user.rb +10 -0
- data/spec/support/models/wheel.rb +6 -0
- data/spec/support/models/widget.rb +22 -0
- data/spec/support/test_server.rb +18 -0
- data/spec/support/test_server.yml.example +2 -0
- metadata +362 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_model/naming'
|
3
|
+
require 'ripple/conflict/document_hooks'
|
4
|
+
require 'ripple/document/bucket_access'
|
5
|
+
require 'ripple/document/key'
|
6
|
+
require 'ripple/document/persistence'
|
7
|
+
require 'ripple/document/finders'
|
8
|
+
require 'ripple/document/link'
|
9
|
+
require 'ripple/properties'
|
10
|
+
require 'ripple/attribute_methods'
|
11
|
+
require 'ripple/timestamps'
|
12
|
+
require 'ripple/validations'
|
13
|
+
require 'ripple/associations'
|
14
|
+
require 'ripple/callbacks'
|
15
|
+
require 'ripple/observable'
|
16
|
+
require 'ripple/conversion'
|
17
|
+
require 'ripple/inspection'
|
18
|
+
require 'ripple/nested_attributes'
|
19
|
+
require 'ripple/serialization'
|
20
|
+
|
21
|
+
module Ripple
|
22
|
+
# Represents a model stored in Riak, serialized in JSON object (document).
|
23
|
+
# Ripple::Document models aim to be fully ActiveModel compatible, with a keen
|
24
|
+
# eye toward features that developers expect from ActiveRecord, DataMapper and MongoMapper.
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
#
|
28
|
+
# class Email
|
29
|
+
# include Ripple::Document
|
30
|
+
# property :from, String, :presence => true
|
31
|
+
# property :to, String, :presence => true
|
32
|
+
# property :sent, Time, :default => proc { Time.now }
|
33
|
+
# property :body, String
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# email = Email.find("37458abc752f8413e") # GET /riak/emails/37458abc752f8413e
|
37
|
+
# email.from = "someone@nowhere.net"
|
38
|
+
# email.save # PUT /riak/emails/37458abc752f8413e
|
39
|
+
#
|
40
|
+
# reply = Email.new
|
41
|
+
# reply.from = "justin@bashoooo.com"
|
42
|
+
# reply.to = "sean@geeemail.com"
|
43
|
+
# reply.body = "Riak is a good fit for scalable Ruby apps."
|
44
|
+
# reply.save # POST /riak/emails (Riak-assigned key)
|
45
|
+
#
|
46
|
+
module Document
|
47
|
+
extend ActiveSupport::Concern
|
48
|
+
|
49
|
+
included do
|
50
|
+
extend ActiveModel::Naming
|
51
|
+
extend BucketAccess
|
52
|
+
include Ripple::Document::Key
|
53
|
+
include Ripple::Document::Persistence
|
54
|
+
extend Ripple::Properties
|
55
|
+
include Ripple::AttributeMethods
|
56
|
+
include Ripple::Timestamps
|
57
|
+
include Ripple::Validations
|
58
|
+
include Ripple::Associations
|
59
|
+
include Ripple::Callbacks
|
60
|
+
include Ripple::Observable
|
61
|
+
include Ripple::Conversion
|
62
|
+
include Ripple::Document::Finders
|
63
|
+
include Ripple::Inspection
|
64
|
+
include Ripple::NestedAttributes
|
65
|
+
include Ripple::Serialization
|
66
|
+
include Ripple::Conflict::DocumentHooks
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
def embeddable?
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module InstanceMethods
|
76
|
+
def _root_document
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns true if the +comparison_object+ is the same object, or is of the same type and has the same key.
|
81
|
+
def ==(comparison_object)
|
82
|
+
comparison_object.equal?(self) ||
|
83
|
+
(comparison_object.class < Document && (comparison_object.instance_of?(self.class) || comparison_object.class.bucket.name == self.class.bucket.name) &&
|
84
|
+
!new? && comparison_object.key == key && !comparison_object.new?)
|
85
|
+
end
|
86
|
+
|
87
|
+
def eql?(other)
|
88
|
+
return true if other.equal?(self)
|
89
|
+
|
90
|
+
(other.class.equal?(self.class)) &&
|
91
|
+
!other.new? && !new? &&
|
92
|
+
(other.key == key)
|
93
|
+
end
|
94
|
+
|
95
|
+
def hash
|
96
|
+
if new?
|
97
|
+
super # every new document should be treated as a different doc
|
98
|
+
else
|
99
|
+
[self.class, key].hash
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'ripple'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
module Document
|
5
|
+
# Similar to ActiveRecord's tables or MongoMapper's collections, we
|
6
|
+
# provide a sane default bucket in which to store your documents.
|
7
|
+
module BucketAccess
|
8
|
+
# @return [String] The bucket name assigned to the document class. Subclasses will inherit their bucket name from their parent class unless they redefine it.
|
9
|
+
def bucket_name
|
10
|
+
superclass.respond_to?(:bucket_name) ? superclass.bucket_name : model_name.plural
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Riak::Bucket] The bucket assigned to this class.
|
14
|
+
def bucket
|
15
|
+
Ripple.client.bucket(bucket_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set the bucket name for this class and its subclasses.
|
19
|
+
# @param [String] value the new bucket name
|
20
|
+
def bucket_name=(value)
|
21
|
+
(class << self; self; end).send(:define_method, :bucket_name){ value }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'ripple/translation'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require 'active_support/core_ext/hash/except'
|
5
|
+
require 'active_support/core_ext/hash/slice'
|
6
|
+
require 'ripple/conflict/resolver'
|
7
|
+
|
8
|
+
module Ripple
|
9
|
+
|
10
|
+
# Raised by <tt>find!</tt> when a document cannot be found with the given key.
|
11
|
+
# begin
|
12
|
+
# Example.find!('badkey')
|
13
|
+
# rescue Ripple::DocumentNotFound
|
14
|
+
# puts 'No Document here!'
|
15
|
+
# end
|
16
|
+
class DocumentNotFound < StandardError
|
17
|
+
include Translation
|
18
|
+
def initialize(keys, found)
|
19
|
+
if keys.empty?
|
20
|
+
super(t("document_not_found.no_key"))
|
21
|
+
elsif keys.size == 1
|
22
|
+
super(t("document_not_found.one_key", :key => keys.first))
|
23
|
+
else
|
24
|
+
missing = keys - found.compact.map(&:key)
|
25
|
+
super(t("document_not_found.many_keys", :keys => missing.join(', ')))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Document
|
31
|
+
module Finders
|
32
|
+
extend ActiveSupport::Concern
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
# Retrieve single or multiple documents from Riak.
|
36
|
+
# @overload find(key)
|
37
|
+
# Find a single document.
|
38
|
+
# @param [String] key the key of a document to find
|
39
|
+
# @return [Document] the found document, or nil
|
40
|
+
# @overload find(key1, key2, ...)
|
41
|
+
# Find a list of documents.
|
42
|
+
# @param [String] key1 the key of a document to find
|
43
|
+
# @param [String] key2 the key of a document to find
|
44
|
+
# @return [Array<Document>] a list of found documents, including nil for missing documents
|
45
|
+
# @overload find(keylist)
|
46
|
+
# Find a list of documents.
|
47
|
+
# @param [Array<String>] keylist an array of keys to find
|
48
|
+
# @return [Array<Document>] a list of found documents, including nil for missing documents
|
49
|
+
def find(*args)
|
50
|
+
if args.first.is_a?(Array)
|
51
|
+
args.flatten.map {|key| find_one(key) }
|
52
|
+
else
|
53
|
+
args.flatten!
|
54
|
+
return nil if args.empty? || args.all?(&:blank?)
|
55
|
+
return find_one(args.first) if args.size == 1
|
56
|
+
args.map {|key| find_one(key) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Retrieve single or multiple documents from Riak
|
61
|
+
# but raise Ripple::DocumentNotFound if a key can
|
62
|
+
# not be found in the bucket.
|
63
|
+
def find!(*args)
|
64
|
+
found = find(*args)
|
65
|
+
raise DocumentNotFound.new(args, found) if !found || Array(found).include?(nil)
|
66
|
+
found
|
67
|
+
end
|
68
|
+
|
69
|
+
# Find the first object using the first key in the
|
70
|
+
# bucket's keys using find. You should not expect to
|
71
|
+
# actually get the first object you added to the bucket.
|
72
|
+
# This is just a convenience method.
|
73
|
+
def first
|
74
|
+
find(bucket.keys.first)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Find the first object using the first key in the
|
78
|
+
# bucket's keys using find!
|
79
|
+
def first!
|
80
|
+
find!(bucket.keys.first)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Find all documents in the Document's bucket and return them.
|
84
|
+
# @overload list()
|
85
|
+
# Get all documents and return them in an array.
|
86
|
+
# @param [Hash] options options to be passed to the
|
87
|
+
# underlying {Bucket#keys} method.
|
88
|
+
# @return [Array<Document>] all found documents in the bucket
|
89
|
+
# @overload list() {|doc| ... }
|
90
|
+
# Stream all documents in the bucket through the block.
|
91
|
+
# @yield [Document] doc a found document
|
92
|
+
# @note This operation is incredibly expensive and should not
|
93
|
+
# be used in production applications.
|
94
|
+
def list
|
95
|
+
if block_given?
|
96
|
+
bucket.keys do |keys|
|
97
|
+
keys.each do |key|
|
98
|
+
obj = find_one(key)
|
99
|
+
yield obj if obj
|
100
|
+
end
|
101
|
+
end
|
102
|
+
[]
|
103
|
+
else
|
104
|
+
bucket.keys.inject([]) do |acc, k|
|
105
|
+
obj = find_one(k)
|
106
|
+
obj ? acc << obj : acc
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
def find_one(key)
|
113
|
+
instantiate(bucket.get(key, quorums.slice(:r)))
|
114
|
+
rescue Riak::FailedRequest => fr
|
115
|
+
raise fr unless fr.not_found?
|
116
|
+
end
|
117
|
+
|
118
|
+
def instantiate(robject)
|
119
|
+
klass = robject.data['_type'].constantize rescue self
|
120
|
+
klass.new.tap do |doc|
|
121
|
+
doc.key = robject.key
|
122
|
+
doc.__send__(:raw_attributes=, robject.data.except("_type")) if robject.data
|
123
|
+
doc.instance_variable_set(:@new, false)
|
124
|
+
doc.instance_variable_set(:@robject, robject)
|
125
|
+
doc.changed_attributes.clear
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
module Document
|
5
|
+
module Key
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
# Defines the key to be derived from a property.
|
10
|
+
# @param [String,Symbol] prop the property to derive the key from
|
11
|
+
def key_on(prop)
|
12
|
+
class_eval <<-CODE
|
13
|
+
def key
|
14
|
+
#{prop}.to_s
|
15
|
+
end
|
16
|
+
def key=(value)
|
17
|
+
self.#{prop} = value
|
18
|
+
end
|
19
|
+
def key_attr
|
20
|
+
:#{prop}
|
21
|
+
end
|
22
|
+
CODE
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module InstanceMethods
|
27
|
+
# Reads the key for this Document.
|
28
|
+
def key
|
29
|
+
@key
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sets the key for this Document.
|
33
|
+
def key=(value)
|
34
|
+
@key = value.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def key_attr
|
38
|
+
:key
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'riak/link'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
module Document
|
5
|
+
# A Link that is tied to a particular document and tag.
|
6
|
+
# The key is fetched from the document lazily when needed.
|
7
|
+
class Link < Riak::Link
|
8
|
+
attr_reader :document
|
9
|
+
private :document
|
10
|
+
|
11
|
+
def initialize(document, tag)
|
12
|
+
@document = document
|
13
|
+
super(document.class.bucket_name, nil, tag)
|
14
|
+
end
|
15
|
+
|
16
|
+
def key
|
17
|
+
document.key
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash
|
21
|
+
document.hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_link(tag)
|
26
|
+
Link.new(self, tag)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
module Document
|
5
|
+
module Persistence
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
# Instantiates a new record, applies attributes from a block, and saves it
|
11
|
+
def create(attrs={}, &block)
|
12
|
+
new(attrs, &block).tap {|s| s.save }
|
13
|
+
end
|
14
|
+
|
15
|
+
# Destroys all records one at a time.
|
16
|
+
# Place holder while :delete to bucket is being developed.
|
17
|
+
def destroy_all
|
18
|
+
list(&:destroy)
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_writer :quorums
|
22
|
+
alias_method "set_quorums", "quorums="
|
23
|
+
|
24
|
+
def quorums
|
25
|
+
@quorums ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceMethods
|
30
|
+
# @private
|
31
|
+
def initialize
|
32
|
+
super
|
33
|
+
@new = true
|
34
|
+
end
|
35
|
+
|
36
|
+
# Determines whether this is a new document.
|
37
|
+
def new?
|
38
|
+
@new || false
|
39
|
+
end
|
40
|
+
|
41
|
+
# Updates a single attribute and then saves the document
|
42
|
+
# NOTE: THIS SKIPS VALIDATIONS! Use with caution.
|
43
|
+
# @return [true,false] whether the document succeeded in saving
|
44
|
+
def update_attribute(attribute, value)
|
45
|
+
send("#{attribute}=", value)
|
46
|
+
save(:validate => false)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Writes new attributes and then saves the document
|
50
|
+
# @return [true,false] whether the document succeeded in saving
|
51
|
+
def update_attributes(attrs)
|
52
|
+
self.attributes = attrs
|
53
|
+
save
|
54
|
+
end
|
55
|
+
|
56
|
+
# Saves the document in Riak.
|
57
|
+
# @return [true,false] whether the document succeeded in saving
|
58
|
+
def save(*args)
|
59
|
+
really_save(*args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def really_save(*args)
|
63
|
+
update_robject
|
64
|
+
robject.store(self.class.quorums.slice(:w,:dw))
|
65
|
+
self.key = robject.key
|
66
|
+
@new = false
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
# Reloads the document from Riak
|
71
|
+
# @return self
|
72
|
+
def reload
|
73
|
+
return self if new?
|
74
|
+
robject.reload(:force => true)
|
75
|
+
self.__send__(:raw_attributes=, @robject.data.except("_type"))
|
76
|
+
reset_associations
|
77
|
+
self
|
78
|
+
end
|
79
|
+
|
80
|
+
# Deletes the document from Riak and freezes this instance
|
81
|
+
def destroy
|
82
|
+
robject.delete(self.class.quorums.slice(:rw)) unless new?
|
83
|
+
freeze
|
84
|
+
true
|
85
|
+
rescue Riak::FailedRequest
|
86
|
+
false
|
87
|
+
end
|
88
|
+
|
89
|
+
# Freezes the document, preventing further modification.
|
90
|
+
def freeze
|
91
|
+
@attributes.freeze; super
|
92
|
+
end
|
93
|
+
|
94
|
+
attr_writer :robject
|
95
|
+
|
96
|
+
def robject
|
97
|
+
@robject ||= Riak::RObject.new(self.class.bucket, key).tap do |obj|
|
98
|
+
obj.content_type = "application/json"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def update_robject
|
103
|
+
robject.key = key if robject.key != key
|
104
|
+
robject.content_type = 'application/json'
|
105
|
+
robject.data = attributes_for_persistence
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
def attributes_for_persistence
|
110
|
+
raw_attributes.merge("_type" => self.class.name)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|