dynamoid 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|