synamoid 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +52 -52
- metadata +5 -41
- data/.document +0 -5
- data/.gitignore +0 -67
- data/.rspec +0 -2
- data/.travis.yml +0 -15
- data/CHANGELOG.md +0 -48
- data/Gemfile +0 -4
- data/Rakefile +0 -64
- data/dynamoid.gemspec +0 -53
- data/lib/dynamoid.rb +0 -53
- data/lib/dynamoid/adapter.rb +0 -190
- data/lib/dynamoid/adapter_plugin/aws_sdk_v2.rb +0 -892
- data/lib/dynamoid/associations.rb +0 -106
- data/lib/dynamoid/associations/association.rb +0 -116
- data/lib/dynamoid/associations/belongs_to.rb +0 -44
- data/lib/dynamoid/associations/has_and_belongs_to_many.rb +0 -40
- data/lib/dynamoid/associations/has_many.rb +0 -39
- data/lib/dynamoid/associations/has_one.rb +0 -39
- data/lib/dynamoid/associations/many_association.rb +0 -193
- data/lib/dynamoid/associations/single_association.rb +0 -69
- data/lib/dynamoid/components.rb +0 -37
- data/lib/dynamoid/config.rb +0 -58
- data/lib/dynamoid/config/options.rb +0 -78
- data/lib/dynamoid/criteria.rb +0 -29
- data/lib/dynamoid/criteria/chain.rb +0 -214
- data/lib/dynamoid/dirty.rb +0 -47
- data/lib/dynamoid/document.rb +0 -201
- data/lib/dynamoid/errors.rb +0 -66
- data/lib/dynamoid/fields.rb +0 -164
- data/lib/dynamoid/finders.rb +0 -199
- data/lib/dynamoid/identity_map.rb +0 -92
- data/lib/dynamoid/indexes.rb +0 -273
- data/lib/dynamoid/middleware/identity_map.rb +0 -16
- data/lib/dynamoid/persistence.rb +0 -359
- data/lib/dynamoid/validations.rb +0 -63
- data/lib/dynamoid/version.rb +0 -3
data/lib/dynamoid/persistence.rb
DELETED
@@ -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
|
data/lib/dynamoid/validations.rb
DELETED
@@ -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
|
data/lib/dynamoid/version.rb
DELETED