dynamoid 0.3.0 → 0.3.1
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 +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:
|