synamoid 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,16 +0,0 @@
1
- module Dynamoid
2
- module Middleware
3
- class IdentityMap
4
- def initialize(app)
5
- @app = app
6
- end
7
-
8
- def call(env)
9
- Dynamoid::IdentityMap.clear
10
- @app.call(env)
11
- ensure
12
- Dynamoid::IdentityMap.clear
13
- end
14
- end
15
- end
16
- end
@@ -1,359 +0,0 @@
1
- require 'bigdecimal'
2
- require 'securerandom'
3
- require 'yaml'
4
-
5
- # encoding: utf-8
6
- module Dynamoid
7
-
8
- # Persistence is responsible for dumping objects to and marshalling objects from the datastore. It tries to reserialize
9
- # values to be of the same type as when they were passed in, based on the fields in the class.
10
- module Persistence
11
- extend ActiveSupport::Concern
12
-
13
- attr_accessor :new_record
14
- alias :new_record? :new_record
15
-
16
- module ClassMethods
17
-
18
- def table_name
19
- table_base_name = options[:name] || base_class.name.split('::').last
20
- .downcase.pluralize
21
-
22
- @table_name ||= [Dynamoid::Config.namespace.to_s,table_base_name].reject(&:empty?).join("_")
23
- end
24
-
25
- # Creates a table.
26
- #
27
- # @param [Hash] options options to pass for table creation
28
- # @option options [Symbol] :id the id field for the table
29
- # @option options [Symbol] :table_name the actual name for the table
30
- # @option options [Integer] :read_capacity set the read capacity for the table; does not work on existing tables
31
- # @option options [Integer] :write_capacity set the write capacity for the table; does not work on existing tables
32
- # @option options [Hash] {range_key => :type} a hash of the name of the range key and a symbol of its type
33
- # @option options [Symbol] :hash_key_type the dynamo type of the hash key (:string or :number)
34
- # @since 0.4.0
35
- def create_table(options = {})
36
- if self.range_key
37
- range_key_hash = { range_key => dynamo_type(attributes[range_key][:type]) }
38
- else
39
- range_key_hash = nil
40
- end
41
- options = {
42
- :id => self.hash_key,
43
- :table_name => self.table_name,
44
- :write_capacity => self.write_capacity,
45
- :read_capacity => self.read_capacity,
46
- :range_key => range_key_hash,
47
- :hash_key_type => dynamo_type(attributes[self.hash_key][:type]),
48
- :local_secondary_indexes => self.local_secondary_indexes.values,
49
- :global_secondary_indexes => self.global_secondary_indexes.values
50
- }.merge(options)
51
-
52
- Dynamoid.adapter.create_table(options[:table_name], options[:id], options)
53
- end
54
-
55
- def from_database(attrs = {})
56
- clazz = attrs[:type] ? obj = attrs[:type].constantize : self
57
- clazz.new(attrs).tap { |r| r.new_record = false }
58
- end
59
-
60
- # Undump an object into a hash, converting each type from a string representation of itself into the type specified by the field.
61
- #
62
- # @since 0.2.0
63
- def undump(incoming = nil)
64
- incoming = (incoming || {}).symbolize_keys
65
- Hash.new.tap do |hash|
66
- self.attributes.each do |attribute, options|
67
- hash[attribute] = undump_field(incoming[attribute], options)
68
- end
69
- incoming.each {|attribute, value| hash[attribute] = value unless hash.has_key? attribute }
70
- end
71
- end
72
-
73
- # Undump a string value for a given type.
74
- #
75
- # @since 0.2.0
76
- def undump_field(value, options)
77
- if (field_class = options[:type]).is_a?(Class)
78
- raise 'Dynamoid class-type fields do not support default values' if options[:default]
79
-
80
- if field_class.respond_to?(:dynamoid_load)
81
- field_class.dynamoid_load(value)
82
- end
83
- elsif options[:type] == :serialized
84
- if value.is_a?(String)
85
- options[:serializer] ? options[:serializer].load(value) : YAML.load(value)
86
- else
87
- value
88
- end
89
- else
90
- if value.nil? && (default_value = options[:default])
91
- value = default_value.respond_to?(:call) ? default_value.call : default_value
92
- end
93
-
94
- unless value.nil?
95
- case options[:type]
96
- when :string
97
- value.to_s
98
- when :integer
99
- Integer(value)
100
- when :number
101
- BigDecimal.new(value.to_s)
102
- when :array
103
- value.to_a
104
- when :raw
105
- if value.is_a?(Hash)
106
- undump_hash(value)
107
- else
108
- value
109
- end
110
- when :set
111
- Set.new(value)
112
- when :datetime
113
- if value.is_a?(Date) || value.is_a?(DateTime) || value.is_a?(Time)
114
- value
115
- else
116
- Time.at(value).to_datetime
117
- end
118
- when :boolean
119
- # persisted as 't', but because undump is called during initialize it can come in as true
120
- if value == 't' || value == true
121
- true
122
- elsif value == 'f' || value == false
123
- false
124
- else
125
- raise ArgumentError, "Boolean column neither true nor false"
126
- end
127
- else
128
- raise ArgumentError, "Unknown type #{options[:type]}"
129
- end
130
- end
131
- end
132
- end
133
-
134
- def dynamo_type(type)
135
- if type.is_a?(Class)
136
- type.respond_to?(:dynamoid_field_type) ? type.dynamoid_field_type : :string
137
- else
138
- case type
139
- when :integer, :number, :datetime
140
- :number
141
- when :string, :serialized
142
- :string
143
- else
144
- raise 'unknown type'
145
- end
146
- end
147
- end
148
-
149
- private
150
-
151
- def undump_hash(hash)
152
- {}.tap do |h|
153
- hash.each { |key, value| h[key.to_sym] = undump_hash_value(value) }
154
- end
155
- end
156
-
157
- def undump_hash_value(val)
158
- case val
159
- when BigDecimal
160
- if Dynamoid::Config.convert_big_decimal
161
- val.to_f
162
- else
163
- val
164
- end
165
- when Hash
166
- undump_hash(val)
167
- when Array
168
- val.map { |v| undump_hash_value(v) }
169
- else
170
- val
171
- end
172
- end
173
- end
174
-
175
- # Set updated_at and any passed in field to current DateTime. Useful for things like last_login_at, etc.
176
- #
177
- def touch(name = nil)
178
- now = DateTime.now
179
- self.updated_at = now
180
- attributes[name] = now if name
181
- save
182
- end
183
-
184
- # Is this object persisted in the datastore? Required for some ActiveModel integration stuff.
185
- #
186
- # @since 0.2.0
187
- def persisted?
188
- !new_record?
189
- end
190
-
191
- # Run the callbacks and then persist this object in the datastore.
192
- #
193
- # @since 0.2.0
194
- def save(options = {})
195
- self.class.create_table
196
-
197
- if new_record?
198
- conditions = { :unless_exists => [self.class.hash_key]}
199
- conditions[:unless_exists] << range_key if(range_key)
200
-
201
- run_callbacks(:create) { persist(conditions) }
202
- else
203
- persist
204
- end
205
-
206
- self
207
- end
208
-
209
- #
210
- # update!() will increment the lock_version if the table has the column, but will not check it. Thus, a concurrent save will
211
- # never cause an update! to fail, but an update! may cause a concurrent save to fail.
212
- #
213
- #
214
- def update!(conditions = {}, &block)
215
- run_callbacks(:update) do
216
- options = range_key ? {:range_key => dump_field(self.read_attribute(range_key), self.class.attributes[range_key])} : {}
217
-
218
- begin
219
- new_attrs = Dynamoid.adapter.update_item(self.class.table_name, self.hash_key, options.merge(:conditions => conditions)) do |t|
220
- if(self.class.attributes[:lock_version])
221
- t.add(lock_version: 1)
222
- end
223
-
224
- yield t
225
- end
226
- load(new_attrs)
227
- rescue Dynamoid::Errors::ConditionalCheckFailedException
228
- raise Dynamoid::Errors::StaleObjectError.new(self, 'update')
229
- end
230
- end
231
- end
232
-
233
- def update(conditions = {}, &block)
234
- update!(conditions, &block)
235
- true
236
- rescue Dynamoid::Errors::StaleObjectError
237
- false
238
- end
239
-
240
- # Delete this object, but only after running callbacks for it.
241
- #
242
- # @since 0.2.0
243
- def destroy
244
- run_callbacks(:destroy) do
245
- self.delete
246
- end
247
- self
248
- end
249
-
250
- # Delete this object from the datastore.
251
- #
252
- # @since 0.2.0
253
- def delete
254
- options = range_key ? {:range_key => dump_field(self.read_attribute(range_key), self.class.attributes[range_key])} : {}
255
-
256
- # Add an optimistic locking check if the lock_version column exists
257
- if(self.class.attributes[:lock_version])
258
- conditions = {:if => {}}
259
- conditions[:if][:lock_version] =
260
- if changes[:lock_version].nil?
261
- self.lock_version
262
- else
263
- changes[:lock_version][0]
264
- end
265
- options[:conditions] = conditions
266
- end
267
- Dynamoid.adapter.delete(self.class.table_name, self.hash_key, options)
268
- rescue Dynamoid::Errors::ConditionalCheckFailedException
269
- raise Dynamoid::Errors::StaleObjectError.new(self, 'delete')
270
- end
271
-
272
- # Dump this object's attributes into hash form, fit to be persisted into the datastore.
273
- #
274
- # @since 0.2.0
275
- def dump
276
- Hash.new.tap do |hash|
277
- self.class.attributes.each do |attribute, options|
278
- hash[attribute] = dump_field(self.read_attribute(attribute), options)
279
- end
280
- end
281
- end
282
-
283
- private
284
-
285
- # Determine how to dump this field. Given a value, it'll determine how to turn it into a value that can be
286
- # persisted into the datastore.
287
- #
288
- # @since 0.2.0
289
- def dump_field(value, options)
290
- if (field_class = options[:type]).is_a?(Class)
291
- if value.respond_to?(:dynamoid_dump)
292
- value.dynamoid_dump
293
- elsif field_class.respond_to?(:dynamoid_dump)
294
- field_class.dynamoid_dump(value)
295
- else
296
- raise ArgumentError, "Neither #{field_class} nor #{value} support serialization for Dynamoid."
297
- end
298
- else
299
- case options[:type]
300
- when :string
301
- !value.nil? ? value.to_s : nil
302
- when :integer
303
- !value.nil? ? Integer(value) : nil
304
- when :number
305
- !value.nil? ? value : nil
306
- when :set
307
- !value.nil? ? Set.new(value) : nil
308
- when :array
309
- !value.nil? ? value : nil
310
- when :datetime
311
- !value.nil? ? value.to_time.to_f : nil
312
- when :serialized
313
- options[:serializer] ? options[:serializer].dump(value) : value.to_yaml
314
- when :raw
315
- !value.nil? ? value : nil
316
- when :boolean
317
- !value.nil? ? value.to_s[0] : nil
318
- else
319
- raise ArgumentError, "Unknown type #{options[:type]}"
320
- end
321
- end
322
- end
323
-
324
- # Persist the object into the datastore. Assign it an id first if it doesn't have one.
325
- #
326
- # @since 0.2.0
327
- def persist(conditions = nil)
328
- run_callbacks(:save) do
329
- self.hash_key = SecureRandom.uuid if self.hash_key.nil? || self.hash_key.blank?
330
-
331
- # Add an exists check to prevent overwriting existing records with new ones
332
- if(new_record?)
333
- conditions ||= {}
334
- (conditions[:unless_exists] ||= []) << self.class.hash_key
335
- end
336
-
337
- # Add an optimistic locking check if the lock_version column exists
338
- if(self.class.attributes[:lock_version])
339
- conditions ||= {}
340
- self.lock_version = (lock_version || 0) + 1
341
- #Uses the original lock_version value from ActiveModel::Dirty in case user changed lock_version manually
342
- (conditions[:if] ||= {})[:lock_version] = changes[:lock_version][0] if(changes[:lock_version][0])
343
- end
344
-
345
- begin
346
- Dynamoid.adapter.write(self.class.table_name, self.dump, conditions)
347
- @new_record = false
348
- true
349
- rescue Dynamoid::Errors::ConditionalCheckFailedException => e
350
- if new_record?
351
- raise Dynamoid::Errors::RecordNotUnique.new(e, self)
352
- else
353
- raise Dynamoid::Errors::StaleObjectError.new(self, 'persist')
354
- end
355
- end
356
- end
357
- end
358
- end
359
- end
@@ -1,63 +0,0 @@
1
- # encoding: utf-8
2
- module Dynamoid
3
-
4
- # Provide ActiveModel validations to Dynamoid documents.
5
- module Validations
6
- extend ActiveSupport::Concern
7
-
8
- include ActiveModel::Validations
9
- include ActiveModel::Validations::Callbacks
10
-
11
- # Override save to provide validation support.
12
- #
13
- # @since 0.2.0
14
- def save(options = {})
15
- options.reverse_merge!(:validate => true)
16
- return false if options[:validate] and (not valid?)
17
- super
18
- end
19
-
20
- # Is this object valid?
21
- #
22
- # @since 0.2.0
23
- def valid?(context = nil)
24
- context ||= (new_record? ? :create : :update)
25
- super(context)
26
- end
27
-
28
- # Raise an error unless this object is valid.
29
- #
30
- # @since 0.2.0
31
- def save!
32
- raise Dynamoid::Errors::DocumentNotValid.new(self) unless valid?
33
- save(:validate => false)
34
- end
35
-
36
- module ClassMethods
37
-
38
- # Override validates_presence_of to handle false values as present.
39
- #
40
- # @since 1.1.1
41
- def validates_presence_of(*attr_names)
42
- validates_with PresenceValidator, _merge_attributes(attr_names)
43
- end
44
-
45
- private
46
-
47
- # Validates that the specified attributes are present (false or not blank).
48
- class PresenceValidator < ActiveModel::EachValidator
49
- # Validate the record for the record and value.
50
- def validate_each(record, attr_name, value)
51
- record.errors.add(attr_name, :blank, options) if not_present?(value)
52
- end
53
-
54
- private
55
-
56
- # Check whether a value is not present.
57
- def not_present?(value)
58
- value.blank? && value != false
59
- end
60
- end
61
- end
62
- end
63
- end
@@ -1,3 +0,0 @@
1
- module Dynamoid
2
- VERSION = "1.2.1"
3
- end