dynamoid 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "dynamoid"
8
- s.version = "0.3.0"
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.0
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, :query
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 Dynamoid::Associations::Association
9
-
10
- # Is this object equal to the association's target?
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 Dynamoid::Associations::Association
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
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 Dynamoid::Associations::Association
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 Dynamoid::Associations::Association
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
@@ -21,11 +21,7 @@ module Dynamoid #:nodoc:
21
21
  #
22
22
  # @since 0.2.0
23
23
  def create(attrs = {})
24
- obj = self.new(attrs)
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
- obj = self.new(attrs)
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
- self.new(attrs)
46
+ new(attrs)
55
47
  end
56
48
 
57
49
  # Does this object exist?
@@ -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
- define_method(named) do
33
- read_attribute(named)
34
- end
35
- define_method("#{named}=") do |value|
36
- write_attribute(named, value)
37
- end
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
- self.send("#{name}_will_change!".to_sym) unless self.read_attribute(name) == value
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
@@ -100,17 +100,13 @@ module Dynamoid
100
100
  # @since 0.2.0
101
101
  def save(options = {})
102
102
  @previously_changed = changes
103
- if self.new_record?
104
- run_callbacks(:create) do
105
- run_callbacks(:save) do
106
- persist
107
- end
108
- end
103
+
104
+ if new_record?
105
+ run_callbacks(:create) { persist }
109
106
  else
110
- run_callbacks(:save) do
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
- self.id = SecureRandom.uuid if self.id.nil? || self.id.blank?
181
- Dynamoid::Adapter.write(self.class.table_name, self.dump)
182
- save_indexes
183
- @new_record = false
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.0
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: 1952467845042018091
342
+ hash: 3564635141636457738
341
343
  required_rubygems_version: !ruby/object:Gem::Requirement
342
344
  none: false
343
345
  requirements: