dynamoid 3.5.0 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -4
  3. data/README.md +24 -18
  4. data/lib/dynamoid.rb +1 -0
  5. data/lib/dynamoid/adapter.rb +7 -4
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +14 -11
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/batch_get_item.rb +1 -0
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +8 -7
  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 +1 -0
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/limit.rb +1 -0
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/middleware/start_key.rb +1 -0
  13. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +1 -0
  14. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +1 -0
  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 +1 -0
  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 +1 -0
  27. data/lib/dynamoid/config.rb +3 -2
  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 +1 -0
  32. data/lib/dynamoid/criteria/chain.rb +353 -33
  33. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +1 -0
  34. data/lib/dynamoid/criteria/key_fields_detector.rb +10 -1
  35. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +1 -0
  36. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +1 -0
  37. data/lib/dynamoid/dirty.rb +71 -16
  38. data/lib/dynamoid/document.rb +123 -42
  39. data/lib/dynamoid/dumping.rb +9 -0
  40. data/lib/dynamoid/dynamodb_time_zone.rb +1 -0
  41. data/lib/dynamoid/fields.rb +189 -16
  42. data/lib/dynamoid/finders.rb +65 -28
  43. data/lib/dynamoid/identity_map.rb +6 -0
  44. data/lib/dynamoid/indexes.rb +74 -15
  45. data/lib/dynamoid/log/formatter.rb +26 -0
  46. data/lib/dynamoid/middleware/identity_map.rb +1 -0
  47. data/lib/dynamoid/persistence.rb +452 -106
  48. data/lib/dynamoid/persistence/import.rb +1 -0
  49. data/lib/dynamoid/persistence/save.rb +1 -0
  50. data/lib/dynamoid/persistence/update_fields.rb +1 -0
  51. data/lib/dynamoid/persistence/upsert.rb +1 -0
  52. data/lib/dynamoid/primary_key_type_mapping.rb +1 -0
  53. data/lib/dynamoid/railtie.rb +1 -0
  54. data/lib/dynamoid/tasks/database.rb +1 -0
  55. data/lib/dynamoid/type_casting.rb +12 -0
  56. data/lib/dynamoid/undumping.rb +8 -0
  57. data/lib/dynamoid/validations.rb +2 -0
  58. data/lib/dynamoid/version.rb +1 -1
  59. metadata +7 -19
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module Dumping
5
6
  def self.dump_attributes(attributes, attributes_options)
6
7
  {}.tap do |h|
@@ -35,6 +36,7 @@ module Dynamoid
35
36
  when :serialized then SerializedDumper
36
37
  when :raw then RawDumper
37
38
  when :boolean then BooleanDumper
39
+ when :binary then BinaryDumper
38
40
  when Class then CustomTypeDumper
39
41
  end
40
42
 
@@ -287,6 +289,13 @@ module Dynamoid
287
289
  end
288
290
  end
289
291
 
292
+ # string -> string
293
+ class BinaryDumper < Base
294
+ def process(value)
295
+ Base64.strict_encode64(value)
296
+ end
297
+ end
298
+
290
299
  # any object -> string
291
300
  class CustomTypeDumper < Base
292
301
  def process(value)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dynamoid
4
+ # @private
4
5
  module DynamodbTimeZone
5
6
  def self.in_time_zone(value)
6
7
  case Dynamoid::Config.dynamodb_timezone
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Dynamoid #:nodoc:
3
+ module Dynamoid
4
4
  # All fields on a Dynamoid::Document must be explicitly defined -- if you have fields in the database that are not
5
5
  # specified with field, then they will be ignored.
6
6
  module Fields
7
7
  extend ActiveSupport::Concern
8
8
 
9
+ # @private
9
10
  # Types allowed in indexes:
10
11
  PERMITTED_KEY_TYPES = %i[
11
12
  number
@@ -33,18 +34,101 @@ module Dynamoid #:nodoc:
33
34
  module ClassMethods
34
35
  # Specify a field for a document.
35
36
  #
36
- # Its type determines how it is coerced when read in and out of the datastore.
37
- # You can specify :integer, :number, :set, :array, :datetime, :date and :serialized,
37
+ # class User
38
+ # include Dynamoid::Document
39
+ #
40
+ # field :last_name
41
+ # field :age, :integer
42
+ # field :last_sign_in, :datetime
43
+ # end
44
+ #
45
+ # Its type determines how it is coerced when read in and out of the
46
+ # datastore. You can specify +string+, +integer+, +number+, +set+, +array+,
47
+ # +map+, +datetime+, +date+, +serialized+, +raw+, +boolean+ and +binary+
38
48
  # or specify a class that defines a serialization strategy.
39
49
  #
50
+ # By default field type is +string+.
51
+ #
52
+ # Set can store elements of the same type only (it's a limitation of
53
+ # DynamoDB itself). If a set should store elements only some particular
54
+ # type +of+ option should be specified:
55
+ #
56
+ # field :hobbies, :set, of: :string
57
+ #
58
+ # Only +string+, +integer+, +number+, +date+, +datetime+ and +serialized+
59
+ # element types are supported.
60
+ #
61
+ # Element type can have own options - they should be specified in the
62
+ # form of +Hash+:
63
+ #
64
+ # field :hobbies, :set, of: { serialized: { serializer: JSON } }
65
+ #
66
+ # Array can contain element of different types but if supports the same
67
+ # +of+ option to convert all the provided elements to the declared type.
68
+ #
69
+ # field :rates, :array, of: :number
70
+ #
71
+ # By default +date+ and +datetime+ fields are stored as integer values.
72
+ # The format can be changed to string with option +store_as_string+:
73
+ #
74
+ # field :published_on, :datetime, store_as_string: true
75
+ #
76
+ # Boolean field by default is stored as a string +t+ or +f+. But DynamoDB
77
+ # supports boolean type natively. In order to switch to the native
78
+ # boolean type an option +store_as_native_boolean+ should be specified:
79
+ #
80
+ # field :active, :boolean, store_as_native_boolean: true
81
+ #
82
+ # If you specify the +serialized+ type a value will be serialized to
83
+ # string in Yaml format by default. Custom way to serialize value to
84
+ # string can be specified with +serializer+ option. Custom serializer
85
+ # should have +dump+ and +load+ methods.
86
+ #
40
87
  # If you specify a class for field type, Dynamoid will serialize using
41
- # `dynamoid_dump` or `dump` methods, and load using `dynamoid_load` or `load` methods.
88
+ # +dynamoid_dump+ method and load using +dynamoid_load+ method.
89
+ #
90
+ # Default field type is +string+.
91
+ #
92
+ # A field can have a default value. It's assigned at initializing a model
93
+ # if no value is specified:
94
+ #
95
+ # field :age, :integer, default: 1
96
+ #
97
+ # If a defautl value should be recalculated every time it can be
98
+ # specified as a callable object (it should implement a +call+ method
99
+ # e.g. +Proc+ object):
100
+ #
101
+ # field :date_of_birth, :date, default: -> { Date.today }
102
+ #
103
+ # For every field Dynamoid creates several methods:
104
+ #
105
+ # * getter
106
+ # * setter
107
+ # * predicate +<name>?+ to check whether a value set
108
+ # * +<name>_before_type_cast?+ to get an original field value before it was type casted
109
+ #
110
+ # It works in the following way:
111
+ #
112
+ # class User
113
+ # include Dynamoid::Document
114
+ #
115
+ # field :age, :integer
116
+ # end
117
+ #
118
+ # user = User.new
119
+ # user.age # => nil
120
+ # user.age? # => false
121
+ #
122
+ # user.age = 20
123
+ # user.age? # => true
42
124
  #
43
- # Default field type is :string.
125
+ # user.age = '21'
126
+ # user.age # => 21 - integer
127
+ # user.age_before_type_cast # => '21' - string
44
128
  #
45
- # @param [Symbol] name the name of the field
46
- # @param [Symbol] type the type of the field (refer to method description for details)
47
- # @param [Hash] options any additional options for the field
129
+ # @param name [Symbol] name of the field
130
+ # @param type [Symbol] type of the field (optional)
131
+ # @param options [Hash] any additional options for the field type (optional)
48
132
  #
49
133
  # @since 0.2.0
50
134
  def field(name, type = :string, options = {})
@@ -79,11 +163,69 @@ module Dynamoid #:nodoc:
79
163
  end
80
164
  end
81
165
 
166
+ # Declare a table range key.
167
+ #
168
+ # class User
169
+ # include Dynamoid::Document
170
+ #
171
+ # range :last_name
172
+ # end
173
+ #
174
+ # By default a range key is a string. In order to use any other type it
175
+ # should be specified as a second argument:
176
+ #
177
+ # range :age, :integer
178
+ #
179
+ # Type options can be specified as well:
180
+ #
181
+ # range :date_of_birth, :date, store_as_string: true
182
+ #
183
+ # @param name [Symbol] a range key attribute name
184
+ # @param type [Symbol] a range key type (optional)
185
+ # @param options [Symbol] type options (optional)
82
186
  def range(name, type = :string, options = {})
83
187
  field(name, type, options)
84
188
  self.range_key = name
85
189
  end
86
190
 
191
+ # Set table level properties.
192
+ #
193
+ # There are some sensible defaults:
194
+ #
195
+ # * table name is based on a model class e.g. +users+ for +User+ class
196
+ # * hash key name - +id+ by default
197
+ # * hash key type - +string+ by default
198
+ # * generating timestamp fields +created_at+ and +updated_at+
199
+ # * billing mode and read/write capacity units
200
+ #
201
+ # The +table+ method can be used to override the defaults:
202
+ #
203
+ # class User
204
+ # include Dynamoid::Document
205
+ #
206
+ # table name: :customers, key: :uuid
207
+ # end
208
+ #
209
+ # The hash key field is declared by default and a type is a string. If
210
+ # another type is needed the field should be declared explicitly:
211
+ #
212
+ # class User
213
+ # include Dynamoid::Document
214
+ #
215
+ # field :id, :integer
216
+ # end
217
+ #
218
+ # @param options [Hash] options to override default table settings
219
+ # @option options [Symbol] :name name of a table
220
+ # @option options [Symbol] :key name of a hash key attribute
221
+ # @option options [Symbol] :inheritance_field name of an attribute used for STI
222
+ # @option options [Symbol] :capacity_mode table billing mode - either +provisioned+ or +on_demand+
223
+ # @option options [Integer] :write_capacity table write capacity units
224
+ # @option options [Integer] :read_capacity table read capacity units
225
+ # @option options [true|false] :timestamps whether generate +created_at+ and +updated_at+ fields or not
226
+ # @option options [Hash] :expires set up a table TTL and should have following structure +{ field: <attriubute name>, after: <seconds> }+
227
+ #
228
+ # @since 0.4.0
87
229
  def table(options)
88
230
  # a default 'id' column is created when Dynamoid::Document is included
89
231
  unless attributes.key? hash_key
@@ -104,6 +246,12 @@ module Dynamoid #:nodoc:
104
246
  end
105
247
  end
106
248
 
249
+ # Remove a field declaration
250
+ #
251
+ # Removes a field from the list of fields and removes all te generated
252
+ # for a field methods.
253
+ #
254
+ # @param field [Symbol] a field name
107
255
  def remove_field(field)
108
256
  field = field.to_sym
109
257
  attributes.delete(field) || raise('No such field')
@@ -120,6 +268,7 @@ module Dynamoid #:nodoc:
120
268
  end
121
269
  end
122
270
 
271
+ # @private
123
272
  def timestamps_enabled?
124
273
  options[:timestamps] || (options[:timestamps].nil? && Dynamoid::Config.timestamps)
125
274
  end
@@ -135,7 +284,7 @@ module Dynamoid #:nodoc:
135
284
  end
136
285
 
137
286
  def warn_about_method_overriding(method_name, field_name)
138
- if self.instance_methods.include?(method_name.to_sym)
287
+ if instance_methods.include?(method_name.to_sym)
139
288
  Dynamoid.logger.warn("Method #{method_name} generated for the field #{field_name} overrides already existing method")
140
289
  end
141
290
  end
@@ -145,10 +294,16 @@ module Dynamoid #:nodoc:
145
294
  attr_accessor :attributes
146
295
  alias raw_attributes attributes
147
296
 
148
- # Write an attribute on the object. Also marks the previous value as dirty.
297
+ # Write an attribute on the object.
298
+ #
299
+ # user.age = 20
300
+ # user.write_attribute(:age, 21)
301
+ # user.age # => 21
149
302
  #
150
- # @param [Symbol] name the name of the field
151
- # @param [Object] value the value to assign to that field
303
+ # Also marks the previous value as dirty.
304
+ #
305
+ # @param name [Symbol] the name of the field
306
+ # @param value [Object] the value to assign to that field
152
307
  #
153
308
  # @since 0.2.0
154
309
  def write_attribute(name, value)
@@ -169,22 +324,40 @@ module Dynamoid #:nodoc:
169
324
 
170
325
  # Read an attribute from an object.
171
326
  #
172
- # @param [Symbol] name the name of the field
327
+ # user.age = 20
328
+ # user.read_attribute(:age) # => 20
173
329
  #
330
+ # @param name [Symbol] the name of the field
331
+ # @return attribute value
174
332
  # @since 0.2.0
175
333
  def read_attribute(name)
176
334
  attributes[name.to_sym]
177
335
  end
178
336
  alias [] read_attribute
179
337
 
180
- # Returns a hash of attributes before typecasting
338
+ # Return attributes values before type casting.
339
+ #
340
+ # user = User.new
341
+ # user.age = '21'
342
+ # user.age # => 21
343
+ #
344
+ # user.attributes_before_type_cast # => { age: '21' }
345
+ #
346
+ # @return [Hash] original attribute values
181
347
  def attributes_before_type_cast
182
348
  @attributes_before_type_cast
183
349
  end
184
350
 
185
- # Returns the value of the attribute identified by name before typecasting
351
+ # Return the value of the attribute identified by name before type casting.
352
+ #
353
+ # user = User.new
354
+ # user.age = '21'
355
+ # user.age # => 21
356
+ #
357
+ # user.read_attribute_before_type_cast(:age) # => '21'
186
358
  #
187
- # @param [Symbol] attribute name
359
+ # @param name [Symbol] attribute name
360
+ # @return original attribute value
188
361
  def read_attribute_before_type_cast(name)
189
362
  return nil unless name.respond_to?(:to_sym)
190
363
 
@@ -6,6 +6,7 @@ module Dynamoid
6
6
  module Finders
7
7
  extend ActiveSupport::Concern
8
8
 
9
+ # @private
9
10
  RANGE_MAP = {
10
11
  'gt' => :range_greater_than,
11
12
  'lt' => :range_less_than,
@@ -19,9 +20,25 @@ module Dynamoid
19
20
  module ClassMethods
20
21
  # Find one or many objects, specified by one id or an array of ids.
21
22
  #
22
- # @param [Array/String] *id an array of ids or one single id
23
- # @param [Hash] options
23
+ # By default it raises +RecordNotFound+ exception if at least one model
24
+ # isn't found. This behavior can be changed with +raise_error+ option. If
25
+ # specified +raise_error: false+ option then +find+ will not raise the
26
+ # exception.
24
27
  #
28
+ # When a document schema includes range key it always should be specified
29
+ # in +find+ method call. In case it's missing +MissingRangeKey+ exception
30
+ # will be raised.
31
+ #
32
+ # Please note that +find+ doesn't preserve order of models in result when
33
+ # passes multiple ids.
34
+ #
35
+ # Supported following options:
36
+ # * +consistent_read+
37
+ # * +range_key+
38
+ # * +raise_error+
39
+ #
40
+ # @param ids [String|Array] hash key or an array of hash keys
41
+ # @param options [Hash]
25
42
  # @return [Dynamoid::Document] one object or an array of objects, depending on whether the input was an array or not
26
43
  #
27
44
  # @example Find by partition key
@@ -45,36 +62,45 @@ module Dynamoid
45
62
  # @since 0.2.0
46
63
  def find(*ids, **options)
47
64
  if ids.size == 1 && !ids[0].is_a?(Array)
48
- _find_by_id(ids[0], options.merge(raise_error: true))
65
+ _find_by_id(ids[0], options.reverse_merge(raise_error: true))
49
66
  else
50
- _find_all(ids.flatten(1), options.merge(raise_error: true))
67
+ _find_all(ids.flatten(1), options.reverse_merge(raise_error: true))
51
68
  end
52
69
  end
53
70
 
54
- # Return objects found by the given array of ids, either hash keys, or hash/range key combinations using BatchGetItem.
71
+ # Find several models at once.
72
+ #
73
+ # Returns objects found by the given array of ids, either hash keys, or
74
+ # hash/range key combinations using +BatchGetItem+.
75
+ #
55
76
  # Returns empty array if no results found.
56
77
  #
57
- # Uses backoff specified by `Dynamoid::Config.backoff` config option
78
+ # Uses backoff specified by +Dynamoid::Config.backoff+ config option.
58
79
  #
59
- # @param [Array<ID>] ids
60
- # @param [Hash] options: Passed to the underlying query.
80
+ # @param ids [Array] array of primary keys
81
+ # @param options [Hash]
82
+ # @option options [true|false] :consistent_read
83
+ # @option options [true|false] :raise_error
61
84
  #
62
85
  # @example
63
- # find all the user with hash key
86
+ # # Find all the user with hash key
64
87
  # User.find_all(['1', '2', '3'])
65
88
  #
66
- # find all the tweets using hash key and range key with consistent read
67
- # Tweet.find_all([['1', 'red'], ['1', 'green']], :consistent_read => true)
89
+ # # Find all the tweets using hash key and range key with consistent read
90
+ # Tweet.find_all([['1', 'red'], ['1', 'green']], consistent_read: true)
68
91
  def find_all(ids, options = {})
69
92
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_all is deprecated! Call .find instead of')
70
93
 
71
94
  _find_all(ids, options)
72
95
  end
73
96
 
74
- # Find one object directly by id.
75
- #
76
- # @param [String] id the id of the object to find
97
+ # Find one object directly by primary key.
77
98
  #
99
+ # @param id [String] the id of the object to find
100
+ # @param options [Hash]
101
+ # @option options [true|false] :consistent_read
102
+ # @option options [true|false] :raise_error
103
+ # @option options [Scalar value] :range_key
78
104
  # @return [Dynamoid::Document] the found object, or nil if nothing was found
79
105
  #
80
106
  # @example Find by partition key
@@ -90,7 +116,10 @@ module Dynamoid
90
116
  _find_by_id(id, options)
91
117
  end
92
118
 
119
+ # @private
93
120
  def _find_all(ids, options = {})
121
+ raise Errors::MissingRangeKey if range_key && ids.any? { |pk, sk| sk.nil? }
122
+
94
123
  if range_key
95
124
  ids = ids.map do |pk, sk|
96
125
  sk_casted = TypeCasting.cast_field(sk, attributes[range_key])
@@ -131,7 +160,10 @@ module Dynamoid
131
160
  end
132
161
  end
133
162
 
163
+ # @private
134
164
  def _find_by_id(id, options = {})
165
+ raise Errors::MissingRangeKey if range_key && options[:range_key].nil?
166
+
135
167
  if range_key
136
168
  key = options[:range_key]
137
169
  key_casted = TypeCasting.cast_field(key, attributes[range_key])
@@ -149,10 +181,10 @@ module Dynamoid
149
181
  end
150
182
  end
151
183
 
152
- # Find one object directly by hash and range keys
184
+ # Find one object directly by hash and range keys.
153
185
  #
154
- # @param [String] hash_key of the object to find
155
- # @param [String/Number] range_key of the object to find
186
+ # @param hash_key [Scalar value] hash key of the object to find
187
+ # @param range_key [Scalar value] range key of the object to find
156
188
  #
157
189
  def find_by_composite_key(hash_key, range_key, options = {})
158
190
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_by_composite_key is deprecated! Call .find instead of')
@@ -169,6 +201,7 @@ module Dynamoid
169
201
  # range :level, :integer
170
202
  # table :key => :chamber_type
171
203
  # end
204
+ #
172
205
  # ChamberType.find_all_by_composite_key('DustVault', range_greater_than: 1)
173
206
  #
174
207
  # @param [String] hash_key of the objects to find
@@ -193,20 +226,22 @@ module Dynamoid
193
226
  # @example
194
227
  # class User
195
228
  # include Dynamoid::Document
196
- # field :email, :string
197
- # field :age, :integer
198
- # field :gender, :string
199
- # field :rank :number
229
+ #
200
230
  # table :key => :email
201
- # global_secondary_index :hash_key => :age, :range_key => :rank
231
+ # global_secondary_index hash_key: :age, range_key: :rank
232
+ #
233
+ # field :email, :string
234
+ # field :age, :integer
235
+ # field :gender, :string
236
+ # field :rank :number
202
237
  # end
238
+ #
203
239
  # # NOTE: the first param and the second param are both hashes,
204
240
  # # so curly braces must be used on first hash param if sending both params
205
- # User.find_all_by_secondary_index({:age => 5}, :range => {"rank.lte" => 10})
241
+ # User.find_all_by_secondary_index({ age: 5 }, range: { "rank.lte": 10 })
206
242
  #
207
- # @param [Hash] eg: {:age => 5}
208
- # @param [Hash] eg: {"rank.lte" => 10}
209
- # @param [Hash] options - query filter, projected keys, scan_index_forward etc
243
+ # @param hash [Hash] conditions for the hash key e.g. +{ age: 5 }+
244
+ # @param options [Hash] conditions on range key e.g. +{ "rank.lte": 10 }, query filter, projected keys, scan_index_forward etc.
210
245
  # @return [Array] an array of all matching items
211
246
  def find_all_by_secondary_index(hash, options = {})
212
247
  ActiveSupport::Deprecation.warn('[Dynamoid] .find_all_by_secondary_index is deprecated! Call .where instead of')
@@ -245,7 +280,8 @@ module Dynamoid
245
280
  end
246
281
  end
247
282
 
248
- # Find using exciting method_missing finders attributes. Uses criteria chains under the hood to accomplish this neatness.
283
+ # Find using exciting method_missing finders attributes. Uses criteria
284
+ # chains under the hood to accomplish this neatness.
249
285
  #
250
286
  # @example find a user by a first name
251
287
  # User.find_by_first_name('Josh')
@@ -253,8 +289,9 @@ module Dynamoid
253
289
  # @example find all users by first and last name
254
290
  # User.find_all_by_first_name_and_last_name('Josh', 'Symonds')
255
291
  #
256
- # @return [Dynamoid::Document/Array] the found object, or an array of found objects if all was somewhere in the method
292
+ # @return [Dynamoid::Document|Array] the found object, or an array of found objects if all was somewhere in the method
257
293
  #
294
+ # @private
258
295
  # @since 0.2.0
259
296
  def method_missing(method, *args)
260
297
  if method =~ /find/