dynamoid 3.2.0 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +111 -1
  3. data/README.md +580 -241
  4. data/lib/dynamoid.rb +2 -0
  5. data/lib/dynamoid/adapter.rb +15 -15
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +82 -102
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +108 -0
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +29 -16
  9. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +3 -2
  10. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/backoff.rb +2 -2
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +2 -3
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +2 -2
  13. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +15 -6
  14. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +15 -5
  15. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/table.rb +1 -0
  16. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/until_past_table_status.rb +5 -3
  17. data/lib/dynamoid/application_time_zone.rb +1 -0
  18. data/lib/dynamoid/associations.rb +182 -19
  19. data/lib/dynamoid/associations/association.rb +4 -2
  20. data/lib/dynamoid/associations/belongs_to.rb +2 -1
  21. data/lib/dynamoid/associations/has_and_belongs_to_many.rb +2 -1
  22. data/lib/dynamoid/associations/has_many.rb +2 -1
  23. data/lib/dynamoid/associations/has_one.rb +2 -1
  24. data/lib/dynamoid/associations/many_association.rb +65 -22
  25. data/lib/dynamoid/associations/single_association.rb +28 -1
  26. data/lib/dynamoid/components.rb +8 -3
  27. data/lib/dynamoid/config.rb +16 -3
  28. data/lib/dynamoid/config/backoff_strategies/constant_backoff.rb +1 -0
  29. data/lib/dynamoid/config/backoff_strategies/exponential_backoff.rb +1 -0
  30. data/lib/dynamoid/config/options.rb +1 -0
  31. data/lib/dynamoid/criteria.rb +2 -1
  32. data/lib/dynamoid/criteria/chain.rb +418 -46
  33. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +3 -3
  34. data/lib/dynamoid/criteria/key_fields_detector.rb +109 -32
  35. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +3 -2
  36. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +1 -1
  37. data/lib/dynamoid/dirty.rb +239 -32
  38. data/lib/dynamoid/document.rb +130 -251
  39. data/lib/dynamoid/dumping.rb +9 -0
  40. data/lib/dynamoid/dynamodb_time_zone.rb +1 -0
  41. data/lib/dynamoid/fields.rb +246 -20
  42. data/lib/dynamoid/finders.rb +69 -32
  43. data/lib/dynamoid/identity_map.rb +6 -0
  44. data/lib/dynamoid/indexes.rb +76 -17
  45. data/lib/dynamoid/loadable.rb +31 -0
  46. data/lib/dynamoid/log/formatter.rb +26 -0
  47. data/lib/dynamoid/middleware/identity_map.rb +1 -0
  48. data/lib/dynamoid/persistence.rb +592 -122
  49. data/lib/dynamoid/persistence/import.rb +73 -0
  50. data/lib/dynamoid/persistence/save.rb +64 -0
  51. data/lib/dynamoid/persistence/update_fields.rb +63 -0
  52. data/lib/dynamoid/persistence/upsert.rb +60 -0
  53. data/lib/dynamoid/primary_key_type_mapping.rb +1 -0
  54. data/lib/dynamoid/railtie.rb +1 -0
  55. data/lib/dynamoid/tasks.rb +3 -1
  56. data/lib/dynamoid/tasks/database.rb +1 -0
  57. data/lib/dynamoid/type_casting.rb +12 -2
  58. data/lib/dynamoid/undumping.rb +8 -0
  59. data/lib/dynamoid/validations.rb +2 -0
  60. data/lib/dynamoid/version.rb +1 -1
  61. metadata +49 -71
  62. data/.coveralls.yml +0 -1
  63. data/.document +0 -5
  64. data/.gitignore +0 -74
  65. data/.rspec +0 -2
  66. data/.rubocop.yml +0 -71
  67. data/.rubocop_todo.yml +0 -55
  68. data/.travis.yml +0 -41
  69. data/Appraisals +0 -28
  70. data/Gemfile +0 -8
  71. data/Rakefile +0 -46
  72. data/Vagrantfile +0 -29
  73. data/docker-compose.yml +0 -7
  74. data/dynamoid.gemspec +0 -57
  75. data/gemfiles/rails_4_2.gemfile +0 -11
  76. data/gemfiles/rails_5_0.gemfile +0 -10
  77. data/gemfiles/rails_5_1.gemfile +0 -10
  78. data/gemfiles/rails_5_2.gemfile +0 -10
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dynamoid #:nodoc:
3
+ module Dynamoid
4
4
  # This is the base module for all domain objects that need to be persisted to
5
5
  # the database as documents.
6
6
  module Document
@@ -17,28 +17,19 @@ module Dynamoid #:nodoc:
17
17
  end
18
18
 
19
19
  module ClassMethods
20
- # Set up table options, including naming it whatever you want, setting the id key, and manually overriding read and
21
- # write capacity.
22
- #
23
- # @param [Hash] options options to pass for this table
24
- # @option options [Symbol] :name the name for the table; this still gets namespaced
25
- # @option options [Symbol] :id id column for the table
26
- # @option options [Integer] :read_capacity set the read capacity for the table; does not work on existing tables
27
- # @option options [Integer] :write_capacity set the write capacity for the table; does not work on existing tables
28
- #
29
- # @since 0.4.0
20
+ # @private
30
21
  def table(options = {})
31
22
  self.options = options
32
23
  super if defined? super
33
24
  end
34
25
 
35
26
  def attr_readonly(*read_only_attributes)
36
- ActiveSupport::Deprecation.warn('[Dynamoid] .attr_readonly is deprecated! Call .find instead of')
37
27
  self.read_only_attributes.concat read_only_attributes.map(&:to_s)
38
28
  end
39
29
 
40
- # Returns the read_capacity for this table.
30
+ # Returns the read capacity for this table.
41
31
  #
32
+ # @return [Integer] read capacity units
42
33
  # @since 0.4.0
43
34
  def read_capacity
44
35
  options[:read_capacity] || Dynamoid::Config.read_capacity
@@ -46,91 +37,121 @@ module Dynamoid #:nodoc:
46
37
 
47
38
  # Returns the write_capacity for this table.
48
39
  #
40
+ # @return [Integer] write capacity units
49
41
  # @since 0.4.0
50
42
  def write_capacity
51
43
  options[:write_capacity] || Dynamoid::Config.write_capacity
52
44
  end
53
45
 
46
+ # Returns the billing (capacity) mode for this table.
47
+ #
48
+ # Could be either +provisioned+ or +on_demand+.
49
+ #
50
+ # @return [Symbol]
51
+ def capacity_mode
52
+ options[:capacity_mode] || Dynamoid::Config.capacity_mode
53
+ end
54
+
54
55
  # Returns the field name used to support STI for this table.
56
+ #
57
+ # Default field name is +type+ but it can be overrided in the +table+
58
+ # method call.
59
+ #
60
+ # User.inheritance_field # => :type
55
61
  def inheritance_field
56
62
  options[:inheritance_field] || :type
57
63
  end
58
64
 
59
- # Returns the id field for this class.
65
+ # Returns the hash key field name for this class.
60
66
  #
67
+ # By default +id+ field is used. But it can be overriden in the +table+
68
+ # method call.
69
+ #
70
+ # User.hash_key # => :id
71
+ #
72
+ # @return [Symbol] a hash key name
61
73
  # @since 0.4.0
62
74
  def hash_key
63
75
  options[:key] || :id
64
76
  end
65
77
 
66
- # Returns the number of items for this class.
78
+ # Return the count of items for this class.
79
+ #
80
+ # It returns aproximate value based on DynamoDB statistic. DynamoDB
81
+ # updates it periodicaly so the value can be no accurate.
67
82
  #
83
+ # It's a reletivly cheap operation and doesn't read all the items in a
84
+ # table. It makes just one HTTP request to DynamoDB.
85
+ #
86
+ # @return [Integer] items count in a table
68
87
  # @since 0.6.1
69
88
  def count
70
89
  Dynamoid.adapter.count(table_name)
71
90
  end
72
91
 
73
- # Initialize a new object and immediately save it to the database.
92
+ # Initialize a new object.
74
93
  #
75
- # @param [Hash] attrs Attributes with which to create the object.
94
+ # User.build(name: 'A')
76
95
  #
77
- # @return [Dynamoid::Document] the saved document
96
+ # Initialize an object and pass it into a block to set other attributes.
78
97
  #
79
- # @since 0.2.0
80
- def create(attrs = {})
81
- if attrs.is_a?(Array)
82
- attrs.map { |attr| create(attr) }
83
- else
84
- build(attrs).tap(&:save)
85
- end
86
- end
87
-
88
- # Initialize a new object and immediately save it to the database. Raise an exception if persistence failed.
98
+ # User.build(name: 'A') do |u|
99
+ # u.age = 21
100
+ # end
89
101
  #
90
- # @param [Hash] attrs Attributes with which to create the object.
102
+ # The only difference between +build+ and +new+ methods is that +build+
103
+ # supports STI (Single table inheritance) and looks at the inheritance
104
+ # field. So it can build a model of actual class. For instance:
91
105
  #
92
- # @return [Dynamoid::Document] the saved document
106
+ # class Employee
107
+ # include Dynamoid::Document
93
108
  #
94
- # @since 0.2.0
95
- def create!(attrs = {})
96
- if attrs.is_a?(Array)
97
- attrs.map { |attr| create!(attr) }
98
- else
99
- build(attrs).tap(&:save!)
100
- end
101
- end
102
-
103
- # Initialize a new object.
109
+ # field :type
110
+ # field :name
111
+ # end
104
112
  #
105
- # @param [Hash] attrs Attributes with which to create the object.
113
+ # class Manager < Employee
114
+ # end
106
115
  #
107
- # @return [Dynamoid::Document] the new document
116
+ # Employee.build(name: 'Alice', type: 'Manager') # => #<Manager:0x00007f945756e3f0 ...>
108
117
  #
118
+ # @param attrs [Hash] Attributes with which to create the document
119
+ # @param block [Proc] Block to process a document after initialization
120
+ # @return [Dynamoid::Document] the new document
109
121
  # @since 0.2.0
110
- def build(attrs = {})
111
- choose_right_class(attrs).new(attrs)
122
+ def build(attrs = {}, &block)
123
+ choose_right_class(attrs).new(attrs, &block)
112
124
  end
113
125
 
114
- # Does this object exist?
126
+ # Does this model exist in a table?
115
127
  #
116
- # Supports primary key in format that `find` call understands.
117
- # Multiple keys and single compound primary key should be passed only as Array explicitily.
128
+ # User.exists?('713') # => true
118
129
  #
119
- # Supports conditions in format that `where` call understands.
130
+ # If a range key is declared it should be specified in the following way:
120
131
  #
121
- # @param [Mixed] id_or_conditions the id of the object or a hash with the options to filter from.
132
+ # User.exists?([['713', 'range-key-value']]) # => true
122
133
  #
123
- # @return [Boolean] true/false
134
+ # It's possible to check existence of several models at once:
124
135
  #
125
- # @example With id
136
+ # User.exists?(['713', '714', '715'])
126
137
  #
127
- # Post.exist?(713)
128
- # Post.exist?([713, 210])
138
+ # Or in case when a range key is declared:
129
139
  #
130
- # @example With attributes conditions
140
+ # User.exists?(
141
+ # [
142
+ # ['713', 'range-key-value-1'],
143
+ # ['714', 'range-key-value-2'],
144
+ # ['715', 'range-key-value-3']
145
+ # ]
146
+ # )
131
147
  #
132
- # Post.exist?(version: 1, 'created_at.gt': Time.now - 1.day)
148
+ # It's also possible to specify models not with primary key but with
149
+ # conditions on the attributes (in the +where+ method style):
133
150
  #
151
+ # User.exists?(age: 20, 'created_at.gt': Time.now - 1.day)
152
+ #
153
+ # @param id_or_conditions [String|Array[String]|Array[Array]|Hash] the primary id of the model, a list of primary ids or a hash with the options to filter from.
154
+ # @return [true|false]
134
155
  # @since 0.2.0
135
156
  def exists?(id_or_conditions = {})
136
157
  case id_or_conditions
@@ -145,165 +166,12 @@ module Dynamoid #:nodoc:
145
166
  end
146
167
  end
147
168
 
148
- # Update document with provided values.
149
- # Instantiates document and saves changes. Runs validations and callbacks.
150
- #
151
- # @param [Scalar value] partition key
152
- # @param [Scalar value] sort key, optional
153
- # @param [Hash] attributes
154
- #
155
- # @return [Dynamoid::Doument] updated document
156
- #
157
- # @example Update document
158
- # Post.update(101, read: true)
159
- def update(hash_key, range_key_value = nil, attrs)
160
- model = find(hash_key, range_key: range_key_value, consistent_read: true)
161
- model.update_attributes(attrs)
162
- model
163
- end
164
-
165
- # Update document.
166
- # Uses efficient low-level `UpdateItem` API call.
167
- # Changes attibutes and loads new document version with one API call.
168
- # Doesn't run validations and callbacks. Can make conditional update.
169
- # If a document doesn't exist or specified conditions failed - returns `nil`
170
- #
171
- # @param [Scalar value] partition key
172
- # @param [Scalar value] sort key (optional)
173
- # @param [Hash] attributes
174
- # @param [Hash] conditions
175
- #
176
- # @return [Dynamoid::Document/nil] updated document
177
- #
178
- # @example Update document
179
- # Post.update_fields(101, read: true)
180
- #
181
- # @example Update document with condition
182
- # Post.update_fields(101, { read: true }, if: { version: 1 })
183
- def update_fields(hash_key_value, range_key_value = nil, attrs = {}, conditions = {})
184
- optional_params = [range_key_value, attrs, conditions].compact
185
- if optional_params.first.is_a?(Hash)
186
- range_key_value = nil
187
- attrs, conditions = optional_params[0..1]
188
- else
189
- range_key_value = optional_params.first
190
- attrs, conditions = optional_params[1..2]
191
- end
192
-
193
- options = if range_key
194
- value_casted = TypeCasting.cast_field(range_key_value, attributes[range_key])
195
- value_dumped = Dumping.dump_field(value_casted, attributes[range_key])
196
- { range_key: value_dumped }
197
- else
198
- {}
199
- end
200
-
201
- (conditions[:if_exists] ||= {})[hash_key] = hash_key_value
202
- options[:conditions] = conditions
203
-
204
- attrs = attrs.symbolize_keys
205
- if Dynamoid::Config.timestamps
206
- attrs[:updated_at] ||= DateTime.now.in_time_zone(Time.zone)
207
- end
208
-
209
- begin
210
- new_attrs = Dynamoid.adapter.update_item(table_name, hash_key_value, options) do |t|
211
- attrs.each do |k, v|
212
- value_casted = TypeCasting.cast_field(v, attributes[k])
213
- value_dumped = Dumping.dump_field(value_casted, attributes[k])
214
- t.set(k => value_dumped)
215
- end
216
- end
217
- attrs_undumped = Undumping.undump_attributes(new_attrs, attributes)
218
- new(attrs_undumped)
219
- rescue Dynamoid::Errors::ConditionalCheckFailedException
220
- end
221
- end
222
-
223
- # Update existing document or create new one.
224
- # Similar to `.update_fields`. The only diffirence is creating new document.
225
- #
226
- # Uses efficient low-level `UpdateItem` API call.
227
- # Changes attibutes and loads new document version with one API call.
228
- # Doesn't run validations and callbacks. Can make conditional update.
229
- # If specified conditions failed - returns `nil`
230
- #
231
- # @param [Scalar value] partition key
232
- # @param [Scalar value] sort key (optional)
233
- # @param [Hash] attributes
234
- # @param [Hash] conditions
235
- #
236
- # @return [Dynamoid::Document/nil] updated document
237
- #
238
- # @example Update document
239
- # Post.update(101, read: true)
240
- #
241
- # @example Update document
242
- # Post.upsert(101, read: true)
243
- def upsert(hash_key_value, range_key_value = nil, attrs = {}, conditions = {})
244
- optional_params = [range_key_value, attrs, conditions].compact
245
- if optional_params.first.is_a?(Hash)
246
- range_key_value = nil
247
- attrs, conditions = optional_params[0..1]
248
- else
249
- range_key_value = optional_params.first
250
- attrs, conditions = optional_params[1..2]
251
- end
252
-
253
- options = if range_key
254
- value_casted = TypeCasting.cast_field(range_key_value, attributes[range_key])
255
- value_dumped = Dumping.dump_field(value_casted, attributes[range_key])
256
- { range_key: value_dumped }
257
- else
258
- {}
259
- end
260
-
261
- options[:conditions] = conditions
262
-
263
- attrs = attrs.symbolize_keys
264
- if Dynamoid::Config.timestamps
265
- attrs[:updated_at] ||= DateTime.now.in_time_zone(Time.zone)
266
- end
267
-
268
- begin
269
- new_attrs = Dynamoid.adapter.update_item(table_name, hash_key_value, options) do |t|
270
- attrs.each do |k, v|
271
- value_casted = TypeCasting.cast_field(v, attributes[k])
272
- value_dumped = Dumping.dump_field(value_casted, attributes[k])
273
-
274
- t.set(k => value_dumped)
275
- end
276
- end
277
-
278
- attrs_undumped = Undumping.undump_attributes(new_attrs, attributes)
279
- new(attrs_undumped)
280
- rescue Dynamoid::Errors::ConditionalCheckFailedException
281
- end
282
- end
283
-
284
- def inc(hash_key_value, range_key_value = nil, counters)
285
- options = if range_key
286
- value_casted = TypeCasting.cast_field(range_key_value, attributes[range_key])
287
- value_dumped = Dumping.dump_field(value_casted, attributes[range_key])
288
- { range_key: value_dumped }
289
- else
290
- {}
291
- end
292
-
293
- Dynamoid.adapter.update_item(table_name, hash_key_value, options) do |t|
294
- counters.each do |k, v|
295
- value_casted = TypeCasting.cast_field(v, attributes[k])
296
- value_dumped = Dumping.dump_field(value_casted, attributes[k])
297
-
298
- t.add(k => value_dumped)
299
- end
300
- end
301
- end
302
-
169
+ # @private
303
170
  def deep_subclasses
304
171
  subclasses + subclasses.map(&:deep_subclasses).flatten
305
172
  end
306
173
 
174
+ # @private
307
175
  def choose_right_class(attrs)
308
176
  attrs[inheritance_field] ? attrs[inheritance_field].constantize : self
309
177
  end
@@ -311,41 +179,50 @@ module Dynamoid #:nodoc:
311
179
 
312
180
  # Initialize a new object.
313
181
  #
314
- # @param [Hash] attrs Attributes with which to create the object.
182
+ # User.new(name: 'A')
183
+ #
184
+ # Initialize an object and pass it into a block to set other attributes.
185
+ #
186
+ # User.new(name: 'A') do |u|
187
+ # u.age = 21
188
+ # end
315
189
  #
190
+ # @param attrs [Hash] Attributes with which to create the document
191
+ # @param block [Proc] Block to process a document after initialization
316
192
  # @return [Dynamoid::Document] the new document
317
193
  #
318
194
  # @since 0.2.0
319
- def initialize(attrs = {})
195
+ def initialize(attrs = {}, &block)
320
196
  run_callbacks :initialize do
321
197
  @new_record = true
322
198
  @attributes ||= {}
323
199
  @associations ||= {}
324
200
  @attributes_before_type_cast ||= {}
325
201
 
326
- attrs_with_defaults = {}
327
- self.class.attributes.each do |attribute, options|
328
- attrs_with_defaults[attribute] = if attrs.key?(attribute)
329
- attrs[attribute]
330
- elsif options.key?(:default)
331
- evaluate_default_value(options[:default])
332
- end
202
+ attrs_with_defaults = self.class.attributes.each_with_object({}) do |(attribute, options), res|
203
+ if attrs.key?(attribute)
204
+ res[attribute] = attrs[attribute]
205
+ elsif options.key?(:default)
206
+ res[attribute] = evaluate_default_value(options[:default])
207
+ end
333
208
  end
334
209
 
335
210
  attrs_virtual = attrs.slice(*(attrs.keys - self.class.attributes.keys))
336
211
 
337
212
  load(attrs_with_defaults.merge(attrs_virtual))
338
- end
339
- end
340
213
 
341
- def load(attrs)
342
- attrs.each do |key, value|
343
- send("#{key}=", value) if respond_to?("#{key}=")
214
+ if block
215
+ block.call(self)
216
+ end
344
217
  end
345
218
  end
346
219
 
347
- # An object is equal to another object if their ids are equal.
220
+ # Check equality of two models.
221
+ #
222
+ # A model is equal to another model only if their primary keys (hash key
223
+ # and optionaly range key) are equal.
348
224
  #
225
+ # @return [true|false]
349
226
  # @since 0.2.0
350
227
  def ==(other)
351
228
  if self.class.identity_map_on?
@@ -357,54 +234,54 @@ module Dynamoid #:nodoc:
357
234
  end
358
235
  end
359
236
 
237
+ # Check equality of two models.
238
+ #
239
+ # Works exactly like +==+ does.
240
+ #
241
+ # @return [true|false]
360
242
  def eql?(other)
361
243
  self == other
362
244
  end
363
245
 
364
- def hash
365
- hash_key.hash ^ range_value.hash
366
- end
367
-
368
- # Reload an object from the database -- if you suspect the object has changed in the datastore and you need those
369
- # changes to be reflected immediately, you would call this method. This is a consistent read.
246
+ # Generate an Integer hash value for this model.
370
247
  #
371
- # @return [Dynamoid::Document] the document this method was called on
248
+ # Hash value is based on primary key. So models can be used safely as a
249
+ # +Hash+ keys.
372
250
  #
373
- # @since 0.2.0
374
- def reload
375
- options = { consistent_read: true }
376
-
377
- if self.class.range_key
378
- options[:range_key] = range_value
379
- end
380
-
381
- self.attributes = self.class.find(hash_key, options).attributes
382
- @associations.values.each(&:reset)
383
- self
251
+ # @return [Integer]
252
+ def hash
253
+ hash_key.hash ^ range_value.hash
384
254
  end
385
255
 
386
- # Return an object's hash key, regardless of what it might be called to the object.
256
+ # Return a model's hash key value.
387
257
  #
388
258
  # @since 0.4.0
389
259
  def hash_key
390
- send(self.class.hash_key)
260
+ self[self.class.hash_key.to_sym]
391
261
  end
392
262
 
393
- # Assign an object's hash key, regardless of what it might be called to the object.
263
+ # Assign a model's hash key value, regardless of what it might be called to
264
+ # the object.
394
265
  #
395
266
  # @since 0.4.0
396
267
  def hash_key=(value)
397
- send("#{self.class.hash_key}=", value)
268
+ self[self.class.hash_key.to_sym] = value
398
269
  end
399
270
 
271
+ # Return a model's range key value.
272
+ #
273
+ # Returns +nil+ if a range key isn't declared for a model.
400
274
  def range_value
401
- if range_key = self.class.range_key
402
- send(range_key)
275
+ if self.class.range_key
276
+ self[self.class.range_key.to_sym]
403
277
  end
404
278
  end
405
279
 
280
+ # Assign a model's range key value.
406
281
  def range_value=(value)
407
- send("#{self.class.range_key}=", value)
282
+ if self.class.range_key
283
+ self[self.class.range_key.to_sym] = value
284
+ end
408
285
  end
409
286
 
410
287
  private
@@ -416,7 +293,7 @@ module Dynamoid #:nodoc:
416
293
  # Evaluates the default value given, this is used by undump
417
294
  # when determining the value of the default given for a field options.
418
295
  #
419
- # @param [Object] :value the attribute's default value
296
+ # @param val [Object] the attribute's default value
420
297
  def evaluate_default_value(val)
421
298
  if val.respond_to?(:call)
422
299
  val.call
@@ -428,3 +305,5 @@ module Dynamoid #:nodoc:
428
305
  end
429
306
  end
430
307
  end
308
+
309
+ ActiveSupport.run_load_hooks(:dynamoid, Dynamoid::Document)