dynamoid 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Dynamoid.gemspec +65 -3
- data/Gemfile +3 -0
- data/Gemfile.lock +6 -0
- data/README.markdown +117 -22
- data/Rakefile +22 -9
- data/VERSION +1 -1
- data/doc/.nojekyll +0 -0
- data/doc/Dynamoid.html +300 -0
- data/doc/Dynamoid/Adapter.html +1387 -0
- data/doc/Dynamoid/Adapter/AwsSdk.html +1561 -0
- data/doc/Dynamoid/Adapter/Local.html +1487 -0
- data/doc/Dynamoid/Associations.html +131 -0
- data/doc/Dynamoid/Associations/Association.html +1706 -0
- data/doc/Dynamoid/Associations/BelongsTo.html +339 -0
- data/doc/Dynamoid/Associations/ClassMethods.html +723 -0
- data/doc/Dynamoid/Associations/HasAndBelongsToMany.html +339 -0
- data/doc/Dynamoid/Associations/HasMany.html +339 -0
- data/doc/Dynamoid/Associations/HasOne.html +339 -0
- data/doc/Dynamoid/Components.html +202 -0
- data/doc/Dynamoid/Config.html +395 -0
- data/doc/Dynamoid/Config/Options.html +609 -0
- data/doc/Dynamoid/Criteria.html +131 -0
- data/doc/Dynamoid/Criteria/Chain.html +759 -0
- data/doc/Dynamoid/Criteria/ClassMethods.html +98 -0
- data/doc/Dynamoid/Document.html +512 -0
- data/doc/Dynamoid/Document/ClassMethods.html +581 -0
- data/doc/Dynamoid/Errors.html +118 -0
- data/doc/Dynamoid/Errors/DocumentNotValid.html +210 -0
- data/doc/Dynamoid/Errors/Error.html +130 -0
- data/doc/Dynamoid/Errors/InvalidField.html +133 -0
- data/doc/Dynamoid/Errors/MissingRangeKey.html +133 -0
- data/doc/Dynamoid/Fields.html +649 -0
- data/doc/Dynamoid/Fields/ClassMethods.html +264 -0
- data/doc/Dynamoid/Finders.html +128 -0
- data/doc/Dynamoid/Finders/ClassMethods.html +502 -0
- data/doc/Dynamoid/Indexes.html +308 -0
- data/doc/Dynamoid/Indexes/ClassMethods.html +351 -0
- data/doc/Dynamoid/Indexes/Index.html +1089 -0
- data/doc/Dynamoid/Persistence.html +653 -0
- data/doc/Dynamoid/Persistence/ClassMethods.html +568 -0
- data/doc/Dynamoid/Validations.html +399 -0
- data/doc/_index.html +439 -0
- data/doc/class_list.html +47 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +55 -0
- data/doc/css/style.css +322 -0
- data/doc/file.LICENSE.html +66 -0
- data/doc/file.README.html +279 -0
- data/doc/file_list.html +52 -0
- data/doc/frames.html +13 -0
- data/doc/index.html +279 -0
- data/doc/js/app.js +205 -0
- data/doc/js/full_list.js +173 -0
- data/doc/js/jquery.js +16 -0
- data/doc/method_list.html +1054 -0
- data/doc/top-level-namespace.html +105 -0
- data/lib/dynamoid.rb +2 -1
- data/lib/dynamoid/adapter.rb +77 -6
- data/lib/dynamoid/adapter/aws_sdk.rb +96 -16
- data/lib/dynamoid/adapter/local.rb +84 -15
- data/lib/dynamoid/associations.rb +53 -4
- data/lib/dynamoid/associations/association.rb +154 -26
- data/lib/dynamoid/associations/belongs_to.rb +32 -6
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +29 -3
- data/lib/dynamoid/associations/has_many.rb +30 -4
- data/lib/dynamoid/associations/has_one.rb +26 -3
- data/lib/dynamoid/components.rb +7 -5
- data/lib/dynamoid/config.rb +15 -2
- data/lib/dynamoid/config/options.rb +8 -0
- data/lib/dynamoid/criteria.rb +7 -2
- data/lib/dynamoid/criteria/chain.rb +55 -8
- data/lib/dynamoid/document.rb +68 -7
- data/lib/dynamoid/errors.rb +17 -2
- data/lib/dynamoid/fields.rb +44 -1
- data/lib/dynamoid/finders.rb +32 -6
- data/lib/dynamoid/indexes.rb +22 -2
- data/lib/dynamoid/indexes/index.rb +48 -7
- data/lib/dynamoid/persistence.rb +111 -51
- data/lib/dynamoid/validations.rb +36 -0
- data/spec/app/models/address.rb +2 -1
- data/spec/app/models/camel_case.rb +11 -0
- data/spec/app/models/magazine.rb +4 -1
- data/spec/app/models/sponsor.rb +3 -1
- data/spec/app/models/subscription.rb +5 -1
- data/spec/app/models/user.rb +10 -1
- data/spec/dynamoid/associations/association_spec.rb +67 -1
- data/spec/dynamoid/associations/belongs_to_spec.rb +16 -1
- data/spec/dynamoid/associations/has_and_belongs_to_many_spec.rb +7 -0
- data/spec/dynamoid/associations/has_many_spec.rb +14 -0
- data/spec/dynamoid/associations/has_one_spec.rb +10 -1
- data/spec/dynamoid/criteria_spec.rb +5 -1
- data/spec/dynamoid/document_spec.rb +23 -3
- data/spec/dynamoid/fields_spec.rb +10 -1
- data/spec/dynamoid/indexes/index_spec.rb +19 -0
- data/spec/dynamoid/persistence_spec.rb +24 -0
- data/spec/dynamoid/validations_spec.rb +36 -0
- metadata +105 -4
data/lib/dynamoid/document.rb
CHANGED
@@ -12,34 +12,95 @@ module Dynamoid #:nodoc:
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
+
|
16
|
+
# Initialize a new object and immediately save it to the database.
|
17
|
+
#
|
18
|
+
# @param [Hash] attrs Attributes with which to create the object.
|
19
|
+
#
|
20
|
+
# @return [Dynamoid::Document] the saved document
|
21
|
+
#
|
22
|
+
# @since 0.2.0
|
15
23
|
def create(attrs = {})
|
16
24
|
obj = self.new(attrs)
|
17
25
|
obj.run_callbacks(:create) do
|
18
|
-
obj.save
|
26
|
+
obj.save
|
27
|
+
end
|
28
|
+
obj
|
29
|
+
end
|
30
|
+
|
31
|
+
# Initialize a new object and immediately save it to the database. Raise an exception if persistence failed.
|
32
|
+
#
|
33
|
+
# @param [Hash] attrs Attributes with which to create the object.
|
34
|
+
#
|
35
|
+
# @return [Dynamoid::Document] the saved document
|
36
|
+
#
|
37
|
+
# @since 0.2.0
|
38
|
+
def create!(attrs = {})
|
39
|
+
obj = self.new(attrs)
|
40
|
+
obj.run_callbacks(:create) do
|
41
|
+
obj.save!
|
19
42
|
end
|
20
43
|
obj
|
21
44
|
end
|
22
45
|
|
46
|
+
# Initialize a new object.
|
47
|
+
#
|
48
|
+
# @param [Hash] attrs Attributes with which to create the object.
|
49
|
+
#
|
50
|
+
# @return [Dynamoid::Document] the new document
|
51
|
+
#
|
52
|
+
# @since 0.2.0
|
23
53
|
def build(attrs = {})
|
24
54
|
self.new(attrs)
|
25
55
|
end
|
56
|
+
|
57
|
+
# Does this object exist?
|
58
|
+
#
|
59
|
+
# @param [String] id the id of the object
|
60
|
+
#
|
61
|
+
# @return [Boolean] true/false
|
62
|
+
#
|
63
|
+
# @since 0.2.0
|
64
|
+
def exists?(id)
|
65
|
+
!! find(id)
|
66
|
+
end
|
26
67
|
end
|
27
|
-
|
68
|
+
|
69
|
+
# Initialize a new object.
|
70
|
+
#
|
71
|
+
# @param [Hash] attrs Attributes with which to create the object.
|
72
|
+
#
|
73
|
+
# @return [Dynamoid::Document] the new document
|
74
|
+
#
|
75
|
+
# @since 0.2.0
|
28
76
|
def initialize(attrs = {})
|
29
77
|
@new_record = true
|
30
78
|
@attributes ||= {}
|
31
|
-
|
32
|
-
|
79
|
+
incoming_attributes = self.class.undump(attrs)
|
80
|
+
|
81
|
+
self.class.attributes.keys.each do |attribute|
|
82
|
+
send "#{attribute}=", incoming_attributes[attribute]
|
83
|
+
end
|
33
84
|
end
|
34
|
-
|
85
|
+
|
86
|
+
# An object is equal to another object if their ids are equal.
|
87
|
+
#
|
88
|
+
# @since 0.2.0
|
35
89
|
def ==(other)
|
90
|
+
return false if other.nil?
|
36
91
|
other.respond_to?(:id) && other.id == self.id
|
37
92
|
end
|
38
|
-
|
93
|
+
|
94
|
+
# Reload an object from the database -- if you suspect the object has changed in the datastore and you need those
|
95
|
+
# changes to be reflected immediately, you would call this method.
|
96
|
+
#
|
97
|
+
# @return [Dynamoid::Document] the document this method was called on
|
98
|
+
#
|
99
|
+
# @since 0.2.0
|
39
100
|
def reload
|
40
101
|
self.attributes = self.class.find(self.id).attributes
|
41
102
|
self
|
42
103
|
end
|
43
104
|
end
|
44
105
|
|
45
|
-
end
|
106
|
+
end
|
data/lib/dynamoid/errors.rb
CHANGED
@@ -1,8 +1,23 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Dynamoid
|
3
|
+
|
4
|
+
# All the error specific to Dynamoid.
|
3
5
|
module Errors
|
6
|
+
|
7
|
+
# Generic error class.
|
8
|
+
class Error < StandardError; end
|
9
|
+
|
10
|
+
# InvalidField is raised when an attribute is specified for an index, but the attribute does not exist.
|
11
|
+
class InvalidField < Error; end
|
12
|
+
|
13
|
+
# MissingRangeKey is raised when a table that requires a range key is quieried without one.
|
14
|
+
class MissingRangeKey < Error; end
|
4
15
|
|
5
|
-
|
6
|
-
class
|
16
|
+
# DocumentNotValid is raised when the document fails validation.
|
17
|
+
class DocumentNotValid < Error
|
18
|
+
def initialize(document)
|
19
|
+
super("Validation failed: #{document.errors.full_messages.join(", ")}")
|
20
|
+
end
|
21
|
+
end
|
7
22
|
end
|
8
23
|
end
|
data/lib/dynamoid/fields.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Dynamoid #:nodoc:
|
3
3
|
|
4
|
+
# All fields on a Dynamoid::Document must be explicitly defined -- if you have fields in the database that are not
|
5
|
+
# specified with field, then they will be ignored.
|
4
6
|
module Fields
|
5
7
|
extend ActiveSupport::Concern
|
6
8
|
|
9
|
+
# Initialize the attributes we know the class has, in addition to our magic attributes: id, created_at, and updated_at.
|
7
10
|
included do
|
8
11
|
class_attribute :attributes
|
9
12
|
|
@@ -14,6 +17,15 @@ module Dynamoid #:nodoc:
|
|
14
17
|
end
|
15
18
|
|
16
19
|
module ClassMethods
|
20
|
+
|
21
|
+
# Specify a field for a document. Its type determines how it is coerced when read in and out of the datastore:
|
22
|
+
# default is string, but you can also specify :integer, :float, :set, :array, :datetime, and :serialized.
|
23
|
+
#
|
24
|
+
# @param [Symbol] name the name of the field
|
25
|
+
# @param [Symbol] type the type of the field (one of :integer, :float, :set, :array, :datetime, or :serialized)
|
26
|
+
# @param [Hash] options any additional options for the field
|
27
|
+
#
|
28
|
+
# @since 0.2.0
|
17
29
|
def field(name, type = :string, options = {})
|
18
30
|
named = name.to_s
|
19
31
|
self.attributes[name] = {:type => type}.merge(options)
|
@@ -26,27 +38,52 @@ module Dynamoid #:nodoc:
|
|
26
38
|
define_method("#{named}?") do
|
27
39
|
!read_attribute(named).nil?
|
28
40
|
end
|
41
|
+
define_attribute_methods(self.attributes.keys)
|
29
42
|
end
|
30
43
|
end
|
31
44
|
|
45
|
+
# You can access the attributes of an object directly on its attributes method, which is by default an empty hash.
|
32
46
|
attr_accessor :attributes
|
33
47
|
alias :raw_attributes :attributes
|
34
48
|
|
49
|
+
# Write an attribute on the object. Also marks the previous value as dirty.
|
50
|
+
#
|
51
|
+
# @param [Symbol] name the name of the field
|
52
|
+
# @param [Object] value the value to assign to that field
|
53
|
+
#
|
54
|
+
# @since 0.2.0
|
35
55
|
def write_attribute(name, value)
|
56
|
+
self.send("#{name}_will_change!".to_sym) unless self.read_attribute(name) == value
|
36
57
|
attributes[name.to_sym] = value
|
37
58
|
end
|
38
59
|
alias :[]= :write_attribute
|
39
60
|
|
61
|
+
# Read an attribute from an object.
|
62
|
+
#
|
63
|
+
# @param [Symbol] name the name of the field
|
64
|
+
#
|
65
|
+
# @since 0.2.0
|
40
66
|
def read_attribute(name)
|
41
67
|
attributes[name.to_sym]
|
42
68
|
end
|
43
69
|
alias :[] :read_attribute
|
44
70
|
|
71
|
+
# Updates multiple attibutes at once, saving the object once the updates are complete.
|
72
|
+
#
|
73
|
+
# @param [Hash] attributes a hash of attributes to update
|
74
|
+
#
|
75
|
+
# @since 0.2.0
|
45
76
|
def update_attributes(attributes)
|
46
77
|
attributes.each {|attribute, value| self.write_attribute(attribute, value)}
|
47
78
|
save
|
48
79
|
end
|
49
80
|
|
81
|
+
# Update a single attribute, saving the object afterwards.
|
82
|
+
#
|
83
|
+
# @param [Symbol] attribute the attribute to update
|
84
|
+
# @param [Object] value the value to assign it
|
85
|
+
#
|
86
|
+
# @since 0.2.0
|
50
87
|
def update_attribute(attribute, value)
|
51
88
|
write_attribute(attribute, value)
|
52
89
|
save
|
@@ -54,10 +91,16 @@ module Dynamoid #:nodoc:
|
|
54
91
|
|
55
92
|
private
|
56
93
|
|
94
|
+
# Automatically called during the created callback to set the created_at time.
|
95
|
+
#
|
96
|
+
# @since 0.2.0
|
57
97
|
def set_created_at
|
58
98
|
self.created_at = DateTime.now
|
59
99
|
end
|
60
|
-
|
100
|
+
|
101
|
+
# Automatically called during the save callback to set the updated_at time.
|
102
|
+
#
|
103
|
+
# @since 0.2.0
|
61
104
|
def set_updated_at
|
62
105
|
self.updated_at = DateTime.now
|
63
106
|
end
|
data/lib/dynamoid/finders.rb
CHANGED
@@ -1,22 +1,37 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
module Dynamoid
|
2
|
+
module Dynamoid
|
3
3
|
|
4
4
|
# This module defines the finder methods that hang off the document at the
|
5
|
-
# class level.
|
5
|
+
# class level, like find, find_by_id, and the method_missing style finders.
|
6
6
|
module Finders
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
module ClassMethods
|
10
|
+
|
11
|
+
# Find one or many objects, specified by one id or an array of ids.
|
12
|
+
#
|
13
|
+
# @param [Array/String] *id an array of ids or one single id
|
14
|
+
#
|
15
|
+
# @return [Dynamoid::Document] one object or an array of objects, depending on whether the input was an array or not
|
16
|
+
#
|
17
|
+
# @since 0.2.0
|
10
18
|
def find(*id)
|
11
19
|
id = Array(id.flatten.uniq)
|
12
20
|
if id.count == 1
|
13
21
|
self.find_by_id(id.first)
|
14
22
|
else
|
15
23
|
items = Dynamoid::Adapter.read(self.table_name, id)
|
16
|
-
items[self.table_name].collect{|i|
|
24
|
+
items[self.table_name].collect{|i| self.build(i).tap { |o| o.new_record = false } }
|
17
25
|
end
|
18
26
|
end
|
19
|
-
|
27
|
+
|
28
|
+
# Find one object directly by id.
|
29
|
+
#
|
30
|
+
# @param [String] id the id of the object to find
|
31
|
+
#
|
32
|
+
# @return [Dynamoid::Document] the found object, or nil if nothing was found
|
33
|
+
#
|
34
|
+
# @since 0.2.0
|
20
35
|
def find_by_id(id)
|
21
36
|
if item = Dynamoid::Adapter.read(self.table_name, id)
|
22
37
|
obj = self.new(item)
|
@@ -26,7 +41,18 @@ module Dynamoid #:nodoc:
|
|
26
41
|
return nil
|
27
42
|
end
|
28
43
|
end
|
29
|
-
|
44
|
+
|
45
|
+
# Find using exciting method_missing finders attributes. Uses criteria chains under the hood to accomplish this neatness.
|
46
|
+
#
|
47
|
+
# @example find a user by a first name
|
48
|
+
# User.find_by_first_name('Josh')
|
49
|
+
#
|
50
|
+
# @example find all users by first and last name
|
51
|
+
# User.find_all_by_first_name_and_last_name('Josh', 'Symonds')
|
52
|
+
#
|
53
|
+
# @return [Dynamoid::Document/Array] the found object, or an array of found objects if all was somewhere in the method
|
54
|
+
#
|
55
|
+
# @since 0.2.0
|
30
56
|
def method_missing(method, *args)
|
31
57
|
if method =~ /find/
|
32
58
|
finder = method.to_s.split('_by_').first
|
@@ -47,4 +73,4 @@ module Dynamoid #:nodoc:
|
|
47
73
|
end
|
48
74
|
end
|
49
75
|
|
50
|
-
end
|
76
|
+
end
|
data/lib/dynamoid/indexes.rb
CHANGED
@@ -3,10 +3,13 @@ require 'dynamoid/indexes/index'
|
|
3
3
|
|
4
4
|
module Dynamoid #:nodoc:
|
5
5
|
|
6
|
-
#
|
6
|
+
# Indexes are quick ways of performing queries by anything other than id in DynamoDB. They are denormalized tables;
|
7
|
+
# that is, data is duplicated in the initial table (where the object is saved) and the index table (where
|
8
|
+
# we perform indexing).
|
7
9
|
module Indexes
|
8
10
|
extend ActiveSupport::Concern
|
9
11
|
|
12
|
+
# Make some helpful attributes to persist indexes.
|
10
13
|
included do
|
11
14
|
class_attribute :indexes
|
12
15
|
|
@@ -14,16 +17,27 @@ module Dynamoid #:nodoc:
|
|
14
17
|
end
|
15
18
|
|
16
19
|
module ClassMethods
|
20
|
+
|
21
|
+
# The call to create an index. Generates a new index with the specified options -- for more information, see Dynamoid::Indexes::Index.
|
22
|
+
# This function also attempts to immediately create the indexing table if it does not exist already.
|
23
|
+
#
|
24
|
+
# @since 0.2.0
|
17
25
|
def index(name, options = {})
|
18
26
|
index = Dynamoid::Indexes::Index.new(self, name, options)
|
19
27
|
self.indexes[index.name] = index
|
20
28
|
create_indexes
|
21
29
|
end
|
22
30
|
|
31
|
+
# Helper function to find indexes.
|
32
|
+
#
|
33
|
+
# @since 0.2.0
|
23
34
|
def find_index(index)
|
24
35
|
self.indexes[Array(index).collect(&:to_s).sort.collect(&:to_sym)]
|
25
36
|
end
|
26
37
|
|
38
|
+
# Helper function to create indexes (if they don't exist already).
|
39
|
+
#
|
40
|
+
# @since 0.2.0
|
27
41
|
def create_indexes
|
28
42
|
self.indexes.each do |name, index|
|
29
43
|
opts = index.range_key? ? {:range_key => :range} : {}
|
@@ -32,12 +46,18 @@ module Dynamoid #:nodoc:
|
|
32
46
|
end
|
33
47
|
end
|
34
48
|
|
49
|
+
# Callback for an object to save itself to each of a class' indexes.
|
50
|
+
#
|
51
|
+
# @since 0.2.0
|
35
52
|
def save_indexes
|
36
53
|
self.class.indexes.each do |name, index|
|
37
54
|
index.save(self)
|
38
55
|
end
|
39
56
|
end
|
40
|
-
|
57
|
+
|
58
|
+
# Callback for an object to delete itself from each of a class' indexes.
|
59
|
+
#
|
60
|
+
# @since 0.2.0
|
41
61
|
def delete_indexes
|
42
62
|
self.class.indexes.each do |name, index|
|
43
63
|
index.delete(self)
|
@@ -2,11 +2,17 @@
|
|
2
2
|
module Dynamoid #:nodoc:
|
3
3
|
module Indexes
|
4
4
|
|
5
|
-
# The class
|
5
|
+
# The class contains all the information an index contains, including its keys and which attributes it covers.
|
6
6
|
class Index
|
7
7
|
attr_accessor :source, :name, :hash_keys, :range_keys
|
8
8
|
alias_method :range_key?, :range_keys
|
9
9
|
|
10
|
+
# Create a new index. Pass either :range => true or :range => :column_name to create a ranged index on that column.
|
11
|
+
#
|
12
|
+
# @param [Class] source the source class for the index
|
13
|
+
# @param [Symbol] name the name of the index
|
14
|
+
#
|
15
|
+
# @since 0.2.0
|
10
16
|
def initialize(source, name, options = {})
|
11
17
|
@source = source
|
12
18
|
|
@@ -21,19 +27,45 @@ module Dynamoid #:nodoc:
|
|
21
27
|
raise Dynamoid::Errors::InvalidField, 'A key specified for an index is not a field' unless keys.all?{|n| source.attributes.include?(n)}
|
22
28
|
end
|
23
29
|
|
30
|
+
# Sort objects into alphabetical strings, used for composing index names correctly (since we always assume they're alphabetical).
|
31
|
+
#
|
32
|
+
# @example find all users by first and last name
|
33
|
+
# sort([:gamma, :alpha, :beta, :omega]) # => [:alpha, :beta, :gamma, :omega]
|
34
|
+
#
|
35
|
+
# @since 0.2.0
|
24
36
|
def sort(objs)
|
25
37
|
Array(objs).flatten.compact.uniq.collect(&:to_s).sort.collect(&:to_sym)
|
26
38
|
end
|
27
|
-
|
39
|
+
|
40
|
+
# Return the array of keys this index uses for its table.
|
41
|
+
#
|
42
|
+
# @since 0.2.0
|
28
43
|
def keys
|
29
44
|
[Array(hash_keys) + Array(range_keys)].flatten.uniq
|
30
45
|
end
|
31
46
|
|
47
|
+
# Return the table name for this index.
|
48
|
+
#
|
49
|
+
# @since 0.2.0
|
32
50
|
def table_name
|
33
51
|
"#{Dynamoid::Config.namespace}_index_#{source.to_s.downcase}_#{name.collect(&:to_s).collect(&:pluralize).join('_and_')}"
|
34
52
|
end
|
35
|
-
|
36
|
-
|
53
|
+
|
54
|
+
# Given either an object or a list of attributes, generate a hash key and a range key for the index. Optionally pass in
|
55
|
+
# true to changed_attributes for a list of all the object's dirty attributes in convenient index form (for deleting stale
|
56
|
+
# information from the indexes).
|
57
|
+
#
|
58
|
+
# @param [Object] attrs either an object that responds to :attributes, or a hash of attributes
|
59
|
+
#
|
60
|
+
# @return [Hash] a hash with the keys :hash_value and :range_value
|
61
|
+
#
|
62
|
+
# @since 0.2.0
|
63
|
+
def values(attrs, changed_attributes = false)
|
64
|
+
if changed_attributes
|
65
|
+
hash = {}
|
66
|
+
attrs.changes.each {|k, v| hash[k.to_sym] = (v.first || v.last)}
|
67
|
+
attrs = hash
|
68
|
+
end
|
37
69
|
attrs = attrs.send(:attributes) if attrs.respond_to?(:attributes)
|
38
70
|
{}.tap do |hash|
|
39
71
|
hash[:hash_value] = hash_keys.collect{|key| attrs[key]}.join('.')
|
@@ -41,16 +73,25 @@ module Dynamoid #:nodoc:
|
|
41
73
|
end
|
42
74
|
end
|
43
75
|
|
76
|
+
# Save an object to this index, merging it with existing ids if there's already something present at this index location.
|
77
|
+
# First, though, delete this object from its old indexes (so the object isn't listed in an erroneous index).
|
78
|
+
#
|
79
|
+
# @since 0.2.0
|
44
80
|
def save(obj)
|
81
|
+
self.delete(obj, true)
|
45
82
|
values = values(obj)
|
46
83
|
return true if values[:hash_value].blank? || (!values[:range_value].nil? && values[:range_value].blank?)
|
47
84
|
existing = Dynamoid::Adapter.read(self.table_name, values[:hash_value], values[:range_value])
|
48
85
|
ids = ((existing and existing[:ids]) or Set.new)
|
49
86
|
Dynamoid::Adapter.write(self.table_name, {:id => values[:hash_value], :ids => ids.merge([obj.id]), :range => values[:range_value]})
|
50
87
|
end
|
51
|
-
|
52
|
-
|
53
|
-
|
88
|
+
|
89
|
+
# Delete an object from this index, preserving existing ids if there are any, and failing gracefully if for some reason the
|
90
|
+
# index doesn't already have this object in it.
|
91
|
+
#
|
92
|
+
# @since 0.2.0
|
93
|
+
def delete(obj, changed_attributes = false)
|
94
|
+
values = values(obj, changed_attributes)
|
54
95
|
return true if values[:hash_value].blank? || (!values[:range_value].nil? && values[:range_value].blank?)
|
55
96
|
existing = Dynamoid::Adapter.read(self.table_name, values[:hash_value], values[:range_value])
|
56
97
|
return true unless existing && existing[:ids] && existing[:ids].include?(obj.id)
|