dynamoid 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Dynamoid.gemspec +3 -1
- data/VERSION +1 -1
- data/lib/dynamoid/associations.rb +2 -0
- data/lib/dynamoid/associations/association.rb +9 -162
- data/lib/dynamoid/associations/belongs_to.rb +4 -32
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +9 -28
- data/lib/dynamoid/associations/has_many.rb +2 -21
- data/lib/dynamoid/associations/has_one.rb +9 -37
- data/lib/dynamoid/associations/many_association.rb +188 -0
- data/lib/dynamoid/associations/single_association.rb +66 -0
- data/lib/dynamoid/document.rb +3 -11
- data/lib/dynamoid/fields.rb +8 -12
- data/lib/dynamoid/persistence.rb +11 -13
- data/spec/dynamoid/associations/belongs_to_spec.rb +1 -3
- data/spec/dynamoid/associations/has_one_spec.rb +0 -1
- data/spec/dynamoid/document_spec.rb +8 -0
- data/spec/dynamoid/persistence_spec.rb +28 -2
- metadata +4 -2
data/Dynamoid.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "dynamoid"
|
8
|
-
s.version = "0.3.
|
8
|
+
s.version = "0.3.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Josh Symonds"]
|
@@ -86,6 +86,8 @@ Gem::Specification.new do |s|
|
|
86
86
|
"lib/dynamoid/associations/has_and_belongs_to_many.rb",
|
87
87
|
"lib/dynamoid/associations/has_many.rb",
|
88
88
|
"lib/dynamoid/associations/has_one.rb",
|
89
|
+
"lib/dynamoid/associations/many_association.rb",
|
90
|
+
"lib/dynamoid/associations/single_association.rb",
|
89
91
|
"lib/dynamoid/components.rb",
|
90
92
|
"lib/dynamoid/config.rb",
|
91
93
|
"lib/dynamoid/config/options.rb",
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.1
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'dynamoid/associations/association'
|
3
|
+
require 'dynamoid/associations/single_association'
|
4
|
+
require 'dynamoid/associations/many_association'
|
3
5
|
require 'dynamoid/associations/has_many'
|
4
6
|
require 'dynamoid/associations/belongs_to'
|
5
7
|
require 'dynamoid/associations/has_one'
|
@@ -1,19 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module Dynamoid #:nodoc:
|
3
3
|
|
4
|
-
# The base association module which all associations include. Every association has two very important components: the source and
|
4
|
+
# The base association module which all associations include. Every association has two very important components: the source and
|
5
5
|
# the target. The source is the object which is calling the association information. It always has the target_ids inside of an attribute on itself.
|
6
6
|
# The target is the object which is referencing by this association.
|
7
7
|
module Associations
|
8
8
|
module Association
|
9
|
-
attr_accessor :name, :options, :source
|
10
|
-
include Enumerable
|
11
|
-
|
12
|
-
# Delegate methods to the records the association represents.
|
13
|
-
delegate :first, :last, :empty?, :size, :to => :records
|
9
|
+
attr_accessor :name, :options, :source
|
14
10
|
|
15
11
|
# Create a new association.
|
16
|
-
#
|
12
|
+
#
|
17
13
|
# @param [Class] source the source record of the association; that is, the record that you already have
|
18
14
|
# @param [Symbol] name the name of the association
|
19
15
|
# @param [Hash] options optional parameters for the association
|
@@ -28,158 +24,9 @@ module Dynamoid #:nodoc:
|
|
28
24
|
@name = name
|
29
25
|
@options = options
|
30
26
|
@source = source
|
31
|
-
@query = {}
|
32
|
-
end
|
33
|
-
|
34
|
-
# Alias convenience methods for the associations.
|
35
|
-
alias :nil? :empty?
|
36
|
-
alias :count :size
|
37
|
-
|
38
|
-
# The records associated to the source.
|
39
|
-
#
|
40
|
-
# @return the association records; depending on which association this is, either a single instance or an array
|
41
|
-
#
|
42
|
-
# @since 0.2.0
|
43
|
-
def records
|
44
|
-
results = Array(target_class.find(source_ids.to_a))
|
45
|
-
|
46
|
-
if query.empty?
|
47
|
-
results
|
48
|
-
else
|
49
|
-
results_with_query(results)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
alias :all :records
|
53
|
-
|
54
|
-
# Delegate include? to the records.
|
55
|
-
def include?(object)
|
56
|
-
records.include?(object)
|
57
|
-
end
|
58
|
-
|
59
|
-
# @todo Improve the two methods below to not have quite so much duplicated code.
|
60
|
-
|
61
|
-
# Deletes an object or array of objects from the association. This removes their records from the association field on the source,
|
62
|
-
# and attempts to remove the source from the target association if it is detected to exist.
|
63
|
-
#
|
64
|
-
# @param [Dynamoid::Document] object the object (or array of objects) to remove from the association
|
65
|
-
#
|
66
|
-
# @return [Dynamoid::Document] the deleted object
|
67
|
-
#
|
68
|
-
# @since 0.2.0
|
69
|
-
def delete(object)
|
70
|
-
source.update_attribute(source_attribute, source_ids - Array(object).collect(&:id))
|
71
|
-
Array(object).collect{|o| self.send(:disassociate_target, o)} if target_association
|
72
|
-
object
|
73
|
-
end
|
74
|
-
|
75
|
-
# Add an object or array of objects to an association. This preserves the current records in the association (if any)
|
76
|
-
# and adds the object to the target association if it is detected to exist.
|
77
|
-
#
|
78
|
-
# @param [Dynamoid::Document] object the object (or array of objects) to add to the association
|
79
|
-
#
|
80
|
-
# @return [Dynamoid::Document] the added object
|
81
|
-
#
|
82
|
-
# @since 0.2.0
|
83
|
-
def <<(object)
|
84
|
-
source.update_attribute(source_attribute, source_ids.merge(Array(object).collect(&:id)))
|
85
|
-
Array(object).collect{|o| self.send(:associate_target, o)} if target_association
|
86
|
-
object
|
87
|
-
end
|
88
|
-
|
89
|
-
# Replace an association with object or array of objects. This removes all of the existing associated records and replaces them with
|
90
|
-
# the passed object(s), and associates the target association if it is detected to exist.
|
91
|
-
#
|
92
|
-
# @param [Dynamoid::Document] object the object (or array of objects) to add to the association
|
93
|
-
#
|
94
|
-
# @return [Dynamoid::Document] the added object
|
95
|
-
#
|
96
|
-
# @since 0.2.0
|
97
|
-
def setter(object)
|
98
|
-
records.each {|o| delete(o)}
|
99
|
-
self << (object)
|
100
|
-
object
|
101
|
-
end
|
102
|
-
|
103
|
-
# Create a new instance of the target class and add it directly to the association.
|
104
|
-
#
|
105
|
-
# @param [Hash] attribute hash for the new object
|
106
|
-
#
|
107
|
-
# @return [Dynamoid::Document] the newly-created object
|
108
|
-
#
|
109
|
-
# @since 0.2.0
|
110
|
-
def create(attributes = {})
|
111
|
-
self << target_class.create(attributes)
|
112
|
-
end
|
113
|
-
|
114
|
-
# Create a new instance of the target class and add it directly to the association. If the create fails an exception will be raised.
|
115
|
-
#
|
116
|
-
# @param [Hash] attribute hash for the new object
|
117
|
-
#
|
118
|
-
# @return [Dynamoid::Document] the newly-created object
|
119
|
-
#
|
120
|
-
# @since 0.2.0
|
121
|
-
def create!(attributes = {})
|
122
|
-
self << target_class.create!(attributes)
|
123
|
-
end
|
124
|
-
|
125
|
-
|
126
|
-
# Naive association filtering.
|
127
|
-
#
|
128
|
-
# @param [Hash] A hash of attributes; each must match every returned object's attribute exactly.
|
129
|
-
#
|
130
|
-
# @return [Dynamoid::Association] the association this method was called on (for chaining purposes)
|
131
|
-
#
|
132
|
-
# @since 0.2.0
|
133
|
-
def where(args)
|
134
|
-
args.each {|k, v| query[k] = v}
|
135
|
-
self
|
136
|
-
end
|
137
|
-
|
138
|
-
# Create a new instance of the target class and add it directly to the association. If the create fails an exception will be raised.
|
139
|
-
#
|
140
|
-
# @param [Hash] attribute hash for the new object
|
141
|
-
#
|
142
|
-
# @return [Dynamoid::Document] the newly-created object
|
143
|
-
#
|
144
|
-
# @since 0.2.0
|
145
|
-
def each(&block)
|
146
|
-
records.each(&block)
|
147
|
-
end
|
148
|
-
|
149
|
-
# Destroys all members of the association and removes them from the association.
|
150
|
-
#
|
151
|
-
# @since 0.2.0
|
152
|
-
def destroy_all
|
153
|
-
objs = records
|
154
|
-
source.update_attribute(source_attribute, nil)
|
155
|
-
objs.each(&:destroy)
|
156
27
|
end
|
157
28
|
|
158
|
-
# Deletes all members of the association and removes them from the association.
|
159
|
-
#
|
160
|
-
# @since 0.2.0
|
161
|
-
def delete_all
|
162
|
-
objs = records
|
163
|
-
source.update_attribute(source_attribute, nil)
|
164
|
-
objs.each(&:delete)
|
165
|
-
end
|
166
|
-
|
167
29
|
private
|
168
|
-
|
169
|
-
# If a query exists, filter all existing results based on that query.
|
170
|
-
#
|
171
|
-
# @param [Array] results the raw results for the association
|
172
|
-
#
|
173
|
-
# @return [Array] the filtered results for the query
|
174
|
-
#
|
175
|
-
# @since 0.2.0
|
176
|
-
def results_with_query(results)
|
177
|
-
results.find_all do |result|
|
178
|
-
query.all? do |attribute, value|
|
179
|
-
result.send(attribute) == value
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
30
|
|
184
31
|
# The target class name, either inferred through the association's name or specified in options.
|
185
32
|
#
|
@@ -194,7 +41,7 @@ module Dynamoid #:nodoc:
|
|
194
41
|
def target_class
|
195
42
|
options[:class] || target_class_name.constantize
|
196
43
|
end
|
197
|
-
|
44
|
+
|
198
45
|
# The target attribute: that is, the attribute on each object of the association that should reference the source.
|
199
46
|
#
|
200
47
|
# @since 0.2.0
|
@@ -204,25 +51,25 @@ module Dynamoid #:nodoc:
|
|
204
51
|
|
205
52
|
# The ids in the target association.
|
206
53
|
#
|
207
|
-
# @since 0.2.0
|
54
|
+
# @since 0.2.0
|
208
55
|
def target_ids
|
209
56
|
target.send(target_attribute) || Set.new
|
210
57
|
end
|
211
58
|
|
212
59
|
# The ids in the target association.
|
213
60
|
#
|
214
|
-
# @since 0.2.0
|
61
|
+
# @since 0.2.0
|
215
62
|
def source_class
|
216
63
|
source.class
|
217
64
|
end
|
218
|
-
|
65
|
+
|
219
66
|
# The source's association attribute: the name of the association with _ids afterwards, like "users_ids".
|
220
67
|
#
|
221
68
|
# @since 0.2.0
|
222
69
|
def source_attribute
|
223
70
|
"#{name}_ids".to_sym
|
224
71
|
end
|
225
|
-
|
72
|
+
|
226
73
|
# The ids in the source association.
|
227
74
|
#
|
228
75
|
# @since 0.2.0
|
@@ -232,5 +79,5 @@ module Dynamoid #:nodoc:
|
|
232
79
|
|
233
80
|
end
|
234
81
|
end
|
235
|
-
|
82
|
+
|
236
83
|
end
|
@@ -5,39 +5,11 @@ module Dynamoid #:nodoc:
|
|
5
5
|
# object to which the association object is associated.
|
6
6
|
module Associations
|
7
7
|
class BelongsTo
|
8
|
-
include
|
9
|
-
|
10
|
-
|
11
|
-
#
|
12
|
-
# @return [Boolean] true/false
|
13
|
-
#
|
14
|
-
# @since 0.2.0
|
15
|
-
def ==(other)
|
16
|
-
target == other
|
17
|
-
end
|
18
|
-
|
19
|
-
# Delegate methods we don't find directly to the target.
|
20
|
-
#
|
21
|
-
# @since 0.2.0
|
22
|
-
def method_missing(method, *args)
|
23
|
-
if target.respond_to?(method)
|
24
|
-
target.send(method, *args)
|
25
|
-
else
|
26
|
-
super
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
8
|
+
include Association
|
9
|
+
include SingleAssociation
|
10
|
+
|
30
11
|
private
|
31
|
-
|
32
|
-
# Find the target of the belongs_to association.
|
33
|
-
#
|
34
|
-
# @return [Dynamoid::Document] the found target (or nil if nothing)
|
35
|
-
#
|
36
|
-
# @since 0.2.0
|
37
|
-
def target
|
38
|
-
records.first
|
39
|
-
end
|
40
|
-
|
12
|
+
|
41
13
|
# Find the target association, either has_many or has_one. Uses either options[:inverse_of] or the source class name and default parsing to
|
42
14
|
# return the most likely name for the target association.
|
43
15
|
#
|
@@ -4,31 +4,12 @@ module Dynamoid #:nodoc:
|
|
4
4
|
# The has and belongs to many association.
|
5
5
|
module Associations
|
6
6
|
class HasAndBelongsToMany
|
7
|
-
include
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
# @return [Boolean] true/false
|
12
|
-
#
|
13
|
-
# @since 0.2.0
|
14
|
-
def ==(other)
|
15
|
-
records == Array(other)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Delegate methods we don't find directly to the records array.
|
19
|
-
#
|
20
|
-
# @since 0.2.0
|
21
|
-
def method_missing(method, *args)
|
22
|
-
if records.respond_to?(method)
|
23
|
-
records.send(method, *args)
|
24
|
-
else
|
25
|
-
super
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
7
|
+
include Association
|
8
|
+
include ManyAssociation
|
9
|
+
|
29
10
|
private
|
30
|
-
|
31
|
-
# Find the target association, always another :has_and_belongs_to_many association. Uses either options[:inverse_of] or the source class name
|
11
|
+
|
12
|
+
# Find the target association, always another :has_and_belongs_to_many association. Uses either options[:inverse_of] or the source class name
|
32
13
|
# and default parsing to return the most likely name for the target association.
|
33
14
|
#
|
34
15
|
# @since 0.2.0
|
@@ -38,15 +19,15 @@ module Dynamoid #:nodoc:
|
|
38
19
|
return nil if guess.nil? || guess[:type] != :has_and_belongs_to_many
|
39
20
|
key_name
|
40
21
|
end
|
41
|
-
|
22
|
+
|
42
23
|
# Associate a source object to this association.
|
43
24
|
#
|
44
|
-
# @since 0.2.0
|
25
|
+
# @since 0.2.0
|
45
26
|
def associate_target(object)
|
46
27
|
ids = object.send(target_attribute) || Set.new
|
47
28
|
object.update_attribute(target_attribute, ids.merge(Array(source.id)))
|
48
29
|
end
|
49
|
-
|
30
|
+
|
50
31
|
# Disassociate a source object from this association.
|
51
32
|
#
|
52
33
|
# @since 0.2.0
|
@@ -56,5 +37,5 @@ module Dynamoid #:nodoc:
|
|
56
37
|
end
|
57
38
|
end
|
58
39
|
end
|
59
|
-
|
40
|
+
|
60
41
|
end
|
@@ -4,27 +4,8 @@ module Dynamoid #:nodoc:
|
|
4
4
|
# The has_many association.
|
5
5
|
module Associations
|
6
6
|
class HasMany
|
7
|
-
include
|
8
|
-
|
9
|
-
# Is this array equal to the association's records?
|
10
|
-
#
|
11
|
-
# @return [Boolean] true/false
|
12
|
-
#
|
13
|
-
# @since 0.2.0
|
14
|
-
def ==(other)
|
15
|
-
records == Array(other)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Delegate methods we don't find directly to the records array.
|
19
|
-
#
|
20
|
-
# @since 0.2.0
|
21
|
-
def method_missing(method, *args)
|
22
|
-
if records.respond_to?(method)
|
23
|
-
records.send(method, *args)
|
24
|
-
else
|
25
|
-
super
|
26
|
-
end
|
27
|
-
end
|
7
|
+
include Association
|
8
|
+
include ManyAssociation
|
28
9
|
|
29
10
|
private
|
30
11
|
|
@@ -4,64 +4,36 @@ module Dynamoid #:nodoc:
|
|
4
4
|
# The HasOne association.
|
5
5
|
module Associations
|
6
6
|
class HasOne
|
7
|
-
include
|
8
|
-
|
9
|
-
# Is this object equal to the association's target?
|
10
|
-
#
|
11
|
-
# @return [Boolean] true/false
|
12
|
-
#
|
13
|
-
# @since 0.2.0
|
14
|
-
def ==(other)
|
15
|
-
target == other
|
16
|
-
end
|
7
|
+
include Association
|
8
|
+
include SingleAssociation
|
17
9
|
|
18
|
-
# Delegate methods we don't find directly to the target.
|
19
|
-
#
|
20
|
-
# @since 0.2.0
|
21
|
-
def method_missing(method, *args)
|
22
|
-
if target.respond_to?(method)
|
23
|
-
target.send(method, *args)
|
24
|
-
else
|
25
|
-
super
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
10
|
private
|
30
|
-
|
31
|
-
# Find the target of the has_one association.
|
32
|
-
#
|
33
|
-
# @return [Dynamoid::Document] the found target (or nil if nothing)
|
34
|
-
#
|
35
|
-
# @since 0.2.0
|
36
|
-
def target
|
37
|
-
records.first
|
38
|
-
end
|
39
11
|
|
40
|
-
# Find the target association, always a :belongs_to association. Uses either options[:inverse_of] or the source class name
|
12
|
+
# Find the target association, always a :belongs_to association. Uses either options[:inverse_of] or the source class name
|
41
13
|
# and default parsing to return the most likely name for the target association.
|
42
14
|
#
|
43
|
-
# @since 0.2.0
|
15
|
+
# @since 0.2.0
|
44
16
|
def target_association
|
45
17
|
key_name = options[:inverse_of] || source.class.to_s.singularize.underscore.to_sym
|
46
18
|
guess = target_class.associations[key_name]
|
47
19
|
return nil if guess.nil? || guess[:type] != :belongs_to
|
48
20
|
key_name
|
49
21
|
end
|
50
|
-
|
22
|
+
|
51
23
|
# Associate a source object to this association.
|
52
24
|
#
|
53
|
-
# @since 0.2.0
|
25
|
+
# @since 0.2.0
|
54
26
|
def associate_target(object)
|
55
27
|
object.update_attribute(target_attribute, Set[source.id])
|
56
28
|
end
|
57
|
-
|
29
|
+
|
58
30
|
# Disassociate a source object from this association.
|
59
31
|
#
|
60
|
-
# @since 0.2.0
|
32
|
+
# @since 0.2.0
|
61
33
|
def disassociate_target(object)
|
62
34
|
source.update_attribute(source_attribute, nil)
|
63
35
|
end
|
64
36
|
end
|
65
37
|
end
|
66
|
-
|
38
|
+
|
67
39
|
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Dynamoid #:nodoc:
|
3
|
+
|
4
|
+
module Associations
|
5
|
+
module ManyAssociation
|
6
|
+
|
7
|
+
attr_accessor :query
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
@query = {}
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
include Enumerable
|
15
|
+
# Delegate methods to the records the association represents.
|
16
|
+
delegate :first, :last, :empty?, :size, :to => :records
|
17
|
+
|
18
|
+
# The records associated to the source.
|
19
|
+
#
|
20
|
+
# @return the association records; depending on which association this is, either a single instance or an array
|
21
|
+
#
|
22
|
+
# @since 0.2.0
|
23
|
+
def records
|
24
|
+
results = Array(target_class.find(source_ids.to_a))
|
25
|
+
|
26
|
+
if query.empty?
|
27
|
+
results
|
28
|
+
else
|
29
|
+
results_with_query(results)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Alias convenience methods for the associations.
|
34
|
+
alias :all :records
|
35
|
+
alias :count :size
|
36
|
+
alias :nil? :empty?
|
37
|
+
|
38
|
+
# Delegate include? to the records.
|
39
|
+
def include?(object)
|
40
|
+
records.include?(object)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Deletes an object or array of objects from the association. This removes their records from the association field on the source,
|
44
|
+
# and attempts to remove the source from the target association if it is detected to exist.
|
45
|
+
#
|
46
|
+
# @param [Dynamoid::Document] object the object (or array of objects) to remove from the association
|
47
|
+
#
|
48
|
+
# @return [Dynamoid::Document] the deleted object
|
49
|
+
#
|
50
|
+
# @since 0.2.0
|
51
|
+
def delete(object)
|
52
|
+
source.update_attribute(source_attribute, source_ids - Array(object).collect(&:id))
|
53
|
+
Array(object).each {|o| self.send(:disassociate_target, o)} if target_association
|
54
|
+
object
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# Add an object or array of objects to an association. This preserves the current records in the association (if any)
|
59
|
+
# and adds the object to the target association if it is detected to exist.
|
60
|
+
#
|
61
|
+
# @param [Dynamoid::Document] object the object (or array of objects) to add to the association
|
62
|
+
#
|
63
|
+
# @return [Dynamoid::Document] the added object
|
64
|
+
#
|
65
|
+
# @since 0.2.0
|
66
|
+
def <<(object)
|
67
|
+
source.update_attribute(source_attribute, source_ids.merge(Array(object).collect(&:id)))
|
68
|
+
Array(object).each {|o| self.send(:associate_target, o)} if target_association
|
69
|
+
object
|
70
|
+
end
|
71
|
+
|
72
|
+
# Replace an association with object or array of objects. This removes all of the existing associated records and replaces them with
|
73
|
+
# the passed object(s), and associates the target association if it is detected to exist.
|
74
|
+
#
|
75
|
+
# @param [Dynamoid::Document] object the object (or array of objects) to add to the association
|
76
|
+
#
|
77
|
+
# @return [Dynamoid::Document] the added object
|
78
|
+
#
|
79
|
+
# @since 0.2.0
|
80
|
+
def setter(object)
|
81
|
+
records.each {|o| delete(o)}
|
82
|
+
self << (object)
|
83
|
+
object
|
84
|
+
end
|
85
|
+
|
86
|
+
# Create a new instance of the target class and add it directly to the association. If the create fails an exception will be raised.
|
87
|
+
#
|
88
|
+
# @param [Hash] attribute hash for the new object
|
89
|
+
#
|
90
|
+
# @return [Dynamoid::Document] the newly-created object
|
91
|
+
#
|
92
|
+
# @since 0.2.0
|
93
|
+
def create!(attributes = {})
|
94
|
+
self << target_class.create!(attributes)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Create a new instance of the target class and add it directly to the association.
|
98
|
+
#
|
99
|
+
# @param [Hash] attribute hash for the new object
|
100
|
+
#
|
101
|
+
# @return [Dynamoid::Document] the newly-created object
|
102
|
+
#
|
103
|
+
# @since 0.2.0
|
104
|
+
def create(attributes = {})
|
105
|
+
self << target_class.create(attributes)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Create a new instance of the target class and add it directly to the association. If the create fails an exception will be raised.
|
109
|
+
#
|
110
|
+
# @param [Hash] attribute hash for the new object
|
111
|
+
#
|
112
|
+
# @return [Dynamoid::Document] the newly-created object
|
113
|
+
#
|
114
|
+
# @since 0.2.0
|
115
|
+
def each(&block)
|
116
|
+
records.each(&block)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Destroys all members of the association and removes them from the association.
|
120
|
+
#
|
121
|
+
# @since 0.2.0
|
122
|
+
def destroy_all
|
123
|
+
objs = records
|
124
|
+
source.update_attribute(source_attribute, nil)
|
125
|
+
objs.each(&:destroy)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Deletes all members of the association and removes them from the association.
|
129
|
+
#
|
130
|
+
# @since 0.2.0
|
131
|
+
def delete_all
|
132
|
+
objs = records
|
133
|
+
source.update_attribute(source_attribute, nil)
|
134
|
+
objs.each(&:delete)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Naive association filtering.
|
138
|
+
#
|
139
|
+
# @param [Hash] A hash of attributes; each must match every returned object's attribute exactly.
|
140
|
+
#
|
141
|
+
# @return [Dynamoid::Association] the association this method was called on (for chaining purposes)
|
142
|
+
#
|
143
|
+
# @since 0.2.0
|
144
|
+
def where(args)
|
145
|
+
args.each {|k, v| query[k] = v}
|
146
|
+
self
|
147
|
+
end
|
148
|
+
|
149
|
+
# Is this array equal to the association's records?
|
150
|
+
#
|
151
|
+
# @return [Boolean] true/false
|
152
|
+
#
|
153
|
+
# @since 0.2.0
|
154
|
+
def ==(other)
|
155
|
+
records == Array(other)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Delegate methods we don't find directly to the records array.
|
159
|
+
#
|
160
|
+
# @since 0.2.0
|
161
|
+
def method_missing(method, *args)
|
162
|
+
if records.respond_to?(method)
|
163
|
+
records.send(method, *args)
|
164
|
+
else
|
165
|
+
super
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# If a query exists, filter all existing results based on that query.
|
172
|
+
#
|
173
|
+
# @param [Array] results the raw results for the association
|
174
|
+
#
|
175
|
+
# @return [Array] the filtered results for the query
|
176
|
+
#
|
177
|
+
# @since 0.2.0
|
178
|
+
def results_with_query(results)
|
179
|
+
results.find_all do |result|
|
180
|
+
query.all? do |attribute, value|
|
181
|
+
result.send(attribute) == value
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Dynamoid #:nodoc:
|
3
|
+
|
4
|
+
module Associations
|
5
|
+
module SingleAssociation
|
6
|
+
|
7
|
+
|
8
|
+
def setter(object)
|
9
|
+
delete
|
10
|
+
source.update_attribute(source_attribute, Set[object.id])
|
11
|
+
self.send(:associate_target, object) if target_association
|
12
|
+
object
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete
|
16
|
+
source.update_attribute(source_attribute, nil)
|
17
|
+
self.send(:disassociate_target, target) if target && target_association
|
18
|
+
target
|
19
|
+
end
|
20
|
+
|
21
|
+
def create!(attributes = {})
|
22
|
+
setter(target_class.create!(attributes))
|
23
|
+
end
|
24
|
+
|
25
|
+
def create(attributes = {})
|
26
|
+
setter(target_class.create!(attributes))
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
# Is this object equal to the association's target?
|
31
|
+
#
|
32
|
+
# @return [Boolean] true/false
|
33
|
+
#
|
34
|
+
# @since 0.2.0
|
35
|
+
def ==(other)
|
36
|
+
target == other
|
37
|
+
end
|
38
|
+
|
39
|
+
# Delegate methods we don't find directly to the target.
|
40
|
+
#
|
41
|
+
# @since 0.2.0
|
42
|
+
def method_missing(method, *args)
|
43
|
+
if target.respond_to?(method)
|
44
|
+
target.send(method, *args)
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def nil?
|
51
|
+
target.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Find the target of the has_one association.
|
57
|
+
#
|
58
|
+
# @return [Dynamoid::Document] the found target (or nil if nothing)
|
59
|
+
#
|
60
|
+
# @since 0.2.0
|
61
|
+
def target
|
62
|
+
target_class.find(source_ids.first)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/dynamoid/document.rb
CHANGED
@@ -21,11 +21,7 @@ module Dynamoid #:nodoc:
|
|
21
21
|
#
|
22
22
|
# @since 0.2.0
|
23
23
|
def create(attrs = {})
|
24
|
-
|
25
|
-
obj.run_callbacks(:create) do
|
26
|
-
obj.save
|
27
|
-
end
|
28
|
-
obj
|
24
|
+
new(attrs).tap(&:save)
|
29
25
|
end
|
30
26
|
|
31
27
|
# Initialize a new object and immediately save it to the database. Raise an exception if persistence failed.
|
@@ -36,11 +32,7 @@ module Dynamoid #:nodoc:
|
|
36
32
|
#
|
37
33
|
# @since 0.2.0
|
38
34
|
def create!(attrs = {})
|
39
|
-
|
40
|
-
obj.run_callbacks(:create) do
|
41
|
-
obj.save!
|
42
|
-
end
|
43
|
-
obj
|
35
|
+
new(attrs).tap(&:save!)
|
44
36
|
end
|
45
37
|
|
46
38
|
# Initialize a new object.
|
@@ -51,7 +43,7 @@ module Dynamoid #:nodoc:
|
|
51
43
|
#
|
52
44
|
# @since 0.2.0
|
53
45
|
def build(attrs = {})
|
54
|
-
|
46
|
+
new(attrs)
|
55
47
|
end
|
56
48
|
|
57
49
|
# Does this object exist?
|
data/lib/dynamoid/fields.rb
CHANGED
@@ -29,16 +29,12 @@ module Dynamoid #:nodoc:
|
|
29
29
|
def field(name, type = :string, options = {})
|
30
30
|
named = name.to_s
|
31
31
|
self.attributes[name] = {:type => type}.merge(options)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
define_method("#{named}=")
|
36
|
-
|
37
|
-
|
38
|
-
define_method("#{named}?") do
|
39
|
-
!read_attribute(named).nil?
|
40
|
-
end
|
41
|
-
define_attribute_methods(self.attributes.keys)
|
32
|
+
|
33
|
+
define_method(named) { read_attribute(named) }
|
34
|
+
define_method("#{named}?") { !read_attribute(named).nil? }
|
35
|
+
define_method("#{named}=") {|value| write_attribute(named, value) }
|
36
|
+
|
37
|
+
define_attribute_method(name)
|
42
38
|
end
|
43
39
|
end
|
44
40
|
|
@@ -53,7 +49,7 @@ module Dynamoid #:nodoc:
|
|
53
49
|
#
|
54
50
|
# @since 0.2.0
|
55
51
|
def write_attribute(name, value)
|
56
|
-
|
52
|
+
attribute_will_change!(name) unless self.read_attribute(name) == value
|
57
53
|
attributes[name.to_sym] = value
|
58
54
|
end
|
59
55
|
alias :[]= :write_attribute
|
@@ -107,4 +103,4 @@ module Dynamoid #:nodoc:
|
|
107
103
|
|
108
104
|
end
|
109
105
|
|
110
|
-
end
|
106
|
+
end
|
data/lib/dynamoid/persistence.rb
CHANGED
@@ -100,17 +100,13 @@ module Dynamoid
|
|
100
100
|
# @since 0.2.0
|
101
101
|
def save(options = {})
|
102
102
|
@previously_changed = changes
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
persist
|
107
|
-
end
|
108
|
-
end
|
103
|
+
|
104
|
+
if new_record?
|
105
|
+
run_callbacks(:create) { persist }
|
109
106
|
else
|
110
|
-
|
111
|
-
persist
|
112
|
-
end
|
107
|
+
persist
|
113
108
|
end
|
109
|
+
|
114
110
|
self
|
115
111
|
end
|
116
112
|
|
@@ -177,10 +173,12 @@ module Dynamoid
|
|
177
173
|
#
|
178
174
|
# @since 0.2.0
|
179
175
|
def persist
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
176
|
+
run_callbacks(:save) do
|
177
|
+
self.id = SecureRandom.uuid if self.id.nil? || self.id.blank?
|
178
|
+
Dynamoid::Adapter.write(self.class.table_name, self.dump)
|
179
|
+
save_indexes
|
180
|
+
!(@new_record = false)
|
181
|
+
end
|
184
182
|
end
|
185
183
|
|
186
184
|
end
|
@@ -19,14 +19,13 @@ describe "Dynamoid::Associations::BelongsTo" do
|
|
19
19
|
|
20
20
|
it 'delegates equality to its source record' do
|
21
21
|
@magazine = @subscription.magazine.create
|
22
|
-
|
22
|
+
|
23
23
|
@subscription.magazine.should == @magazine
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'associates has_many automatically' do
|
27
27
|
@magazine = @subscription.magazine.create
|
28
28
|
|
29
|
-
@magazine.subscriptions.size.should == 1
|
30
29
|
@magazine.subscriptions.should include @subscription
|
31
30
|
|
32
31
|
@magazine = Magazine.create
|
@@ -63,7 +62,6 @@ describe "Dynamoid::Associations::BelongsTo" do
|
|
63
62
|
it 'associates has_one automatically' do
|
64
63
|
@magazine = @sponsor.magazine.create
|
65
64
|
|
66
|
-
@magazine.sponsor.size.should == 1
|
67
65
|
@magazine.sponsor.should == @sponsor
|
68
66
|
|
69
67
|
@user = @subscription.customer.create
|
@@ -37,7 +37,6 @@ describe "Dynamoid::Associations::HasOne" do
|
|
37
37
|
it 'associates belongs_to automatically' do
|
38
38
|
@sponsor = @magazine.sponsor.create
|
39
39
|
@sponsor.magazine.should == @magazine
|
40
|
-
@magazine.sponsor.size.should == 1
|
41
40
|
@magazine.sponsor.should == @sponsor
|
42
41
|
|
43
42
|
@subscription = @user.monthly.create
|
@@ -8,6 +8,14 @@ describe "Dynamoid::Document" do
|
|
8
8
|
@address.new_record.should be_true
|
9
9
|
@address.attributes.should == {:id=>nil, :created_at=>nil, :updated_at=>nil, :city=>nil, :options=>nil}
|
10
10
|
end
|
11
|
+
|
12
|
+
it 'responds to will_change! methods for all fields' do
|
13
|
+
@address = Address.new
|
14
|
+
@address.should respond_to(:id_will_change!)
|
15
|
+
@address.should respond_to(:options_will_change!)
|
16
|
+
@address.should respond_to(:created_at_will_change!)
|
17
|
+
@address.should respond_to(:updated_at_will_change!)
|
18
|
+
end
|
11
19
|
|
12
20
|
it 'initializes a new document with attributes' do
|
13
21
|
@address = Address.new(:city => 'Chicago')
|
@@ -1,12 +1,14 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
3
|
describe "Dynamoid::Persistence" do
|
4
|
-
|
4
|
+
|
5
|
+
let(:document_class) { Class.new.send :include, Dynamoid::Document }
|
6
|
+
|
5
7
|
before do
|
6
8
|
Random.stubs(:rand).with(Dynamoid::Config.partition_size).returns(0)
|
7
9
|
@address = Address.new
|
8
10
|
end
|
9
|
-
|
11
|
+
|
10
12
|
context 'without AWS keys' do
|
11
13
|
unless ENV['ACCESS_KEY'] && ENV['SECRET_KEY']
|
12
14
|
before do
|
@@ -102,6 +104,30 @@ describe "Dynamoid::Persistence" do
|
|
102
104
|
User.undump(@hash)[:name].should == 'Josh'
|
103
105
|
User.undump(@hash)[:created_at].to_f == @time.to_f
|
104
106
|
end
|
107
|
+
|
108
|
+
it 'runs the before_create callback only once' do
|
109
|
+
document_class.before_create { doing_before_create }
|
110
|
+
|
111
|
+
document_class.any_instance.expects(:doing_before_create)
|
112
|
+
|
113
|
+
document_class.create
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'runs after save callbacks when doing #create' do
|
117
|
+
document_class.after_create { doing_after_create }
|
118
|
+
|
119
|
+
document_class.any_instance.expects(:doing_after_create)
|
120
|
+
|
121
|
+
document_class.create
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'runs after save callbacks when doing #save' do
|
125
|
+
document_class.after_create { doing_after_create }
|
126
|
+
|
127
|
+
document_class.any_instance.expects(:doing_after_create)
|
128
|
+
|
129
|
+
document_class.new.save
|
130
|
+
end
|
105
131
|
|
106
132
|
it 'tracks previous changes on save or update' do
|
107
133
|
@address.city = 'Chicago'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -282,6 +282,8 @@ files:
|
|
282
282
|
- lib/dynamoid/associations/has_and_belongs_to_many.rb
|
283
283
|
- lib/dynamoid/associations/has_many.rb
|
284
284
|
- lib/dynamoid/associations/has_one.rb
|
285
|
+
- lib/dynamoid/associations/many_association.rb
|
286
|
+
- lib/dynamoid/associations/single_association.rb
|
285
287
|
- lib/dynamoid/components.rb
|
286
288
|
- lib/dynamoid/config.rb
|
287
289
|
- lib/dynamoid/config/options.rb
|
@@ -337,7 +339,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
337
339
|
version: '0'
|
338
340
|
segments:
|
339
341
|
- 0
|
340
|
-
hash:
|
342
|
+
hash: 3564635141636457738
|
341
343
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
342
344
|
none: false
|
343
345
|
requirements:
|