ocean-dynamo 0.3.10 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.rdoc +46 -10
- data/lib/ocean-dynamo.rb +4 -3
- data/lib/ocean-dynamo/associations/belongs_to.rb +124 -0
- data/lib/ocean-dynamo/{collection_proxy.rb → associations/collection_proxy.rb} +0 -0
- data/lib/ocean-dynamo/attributes.rb +20 -1
- data/lib/ocean-dynamo/exceptions.rb +8 -2
- data/lib/ocean-dynamo/persistence.rb +12 -8
- data/lib/ocean-dynamo/queries.rb +5 -1
- data/lib/ocean-dynamo/schema.rb +9 -7
- data/lib/ocean-dynamo/tables.rb +16 -5
- data/lib/ocean-dynamo/version.rb +1 -1
- metadata +4 -4
- data/lib/ocean-dynamo/associations.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5fb4f095350a4a1cc43debd8405c550277ec7c1
|
4
|
+
data.tar.gz: e1684c9a9481b00b6ba44657076eeda1930e3628
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8673fa49c6af6b931103b669fc1a597e74b68a864354ab26cd5cddf3d1acc509f0d50c171f8deff2c63227b21aaa1eab2875b8684682e6558886d9cf3abc376
|
7
|
+
data.tar.gz: 6be8bab8ca04c7a5e3e3cf4364627fef8a0cac2894d480dda2a12cb31ac72c1b2773cc88fb1ac600e51d2ba6ff3db4a5f92aecc820d6b4f0ff7a44b0b678ff1d
|
data/README.rdoc
CHANGED
@@ -8,7 +8,7 @@ OceanDynamo requires Ruby 2.0 and Ruby on Rails 4.0.0 or later.
|
|
8
8
|
{<img src="https://badge.fury.io/rb/ocean-dynamo.png" alt="Gem Version" />}[http://badge.fury.io/rb/ocean-dynamo]
|
9
9
|
|
10
10
|
|
11
|
-
|
11
|
+
=== Features
|
12
12
|
|
13
13
|
As one important use case for OceanDynamo is to facilitate the conversion of SQL based
|
14
14
|
ActiveRecord models to DynamoDB based models, it is important that the syntax and semantics
|
@@ -19,7 +19,7 @@ is of course based on ActiveModel.
|
|
19
19
|
The attribute and persistence layer of OceanDynamo is modeled on that of ActiveRecord:
|
20
20
|
there's +save+, +save!+, +create+, +update+, +update!+, +update_attributes+, +find_each+,
|
21
21
|
and all the other methods you're used to. The design goal is always to implement as much
|
22
|
-
|
22
|
+
of the ActiveRecord interface as possible, without compromising scalability. This makes the
|
23
23
|
task of switching from SQL to no-SQL much easier.
|
24
24
|
|
25
25
|
Thanks to its structural similarity to ActiveRecord, OceanDynamo works with FactoryGirl.
|
@@ -28,7 +28,7 @@ To facilitate testing, future versions will keep track of and delete instances a
|
|
28
28
|
OceanDynamo uses primary and secondary indices to retrieve related table items,
|
29
29
|
which means it will scale without limits.
|
30
30
|
|
31
|
-
|
31
|
+
=== Example
|
32
32
|
|
33
33
|
The following example shows the syntax.
|
34
34
|
|
@@ -93,18 +93,54 @@ controllers. Furthermore, OceanDynamo implements much of the infrastructure of A
|
|
93
93
|
for instance, +read_attribute+, +write_attribute+, and much of the control logic and
|
94
94
|
parameters.
|
95
95
|
|
96
|
-
|
97
|
-
expected. Model.find_each and Model.find_in_batches are also available.
|
96
|
+
+Model.all+ and +.count+ work as expected. +Model.find_each+ is also available.
|
98
97
|
|
99
|
-
|
100
|
-
|
98
|
+
+belongs_to+ is now operational. The other side, +has_many+, is much simpler and should
|
99
|
+
follow very soon (probably today).
|
100
|
+
|
101
|
+
Restrictions for +belongs_to+ tables:
|
102
|
+
* * The hash key must be specified and must not be +:id+.
|
103
|
+
* * The range key must not be specified at all.
|
104
|
+
* * belongs_to can be specified only once in each class.
|
105
|
+
|
106
|
+
These restrictions allow OceanDynamo to implement the +has_many+ / +belongs_to+
|
107
|
+
relation in a very efficient and massively scalable way.
|
108
|
+
|
109
|
+
+belongs_to+ claims the range key and uses it to store its own UUID, which normally
|
110
|
+
would be stored in the hash key attribute. Instead, the hash key attribute holds the
|
111
|
+
UUID of the parent. We have thus reversed the roles of these two fields. As a result,
|
112
|
+
all children have their parent UUID as their hash key, and their own UUID in their
|
113
|
+
range key.
|
114
|
+
|
115
|
+
This type of association is even more efficient than its ActiveRecord counterpart as
|
116
|
+
it uses only the primary index of the child model for finds and scans in both directions
|
117
|
+
of the +has_many+ / +belongs_to+ association.
|
118
|
+
|
119
|
+
Furthermore, since DynamoDB has powerful primary index searches involving substrings
|
120
|
+
and matching, the fact that the range key is a string can be used to implement
|
121
|
+
wildcard matching of additional attributes. This gives, amongst other things, the
|
122
|
+
equivalent of an SQL GROUP BY request, again without requiring any secondary indices.
|
123
|
+
|
124
|
+
It's our goal to use a similar technique to implement has_and_belongs_to_many relations,
|
125
|
+
which means that secondary indices won't be necessary for the vast majority of
|
126
|
+
OceanDynamo use cases. This ultimately means reduced operational costs, as well as
|
127
|
+
reduced complexity.
|
128
|
+
|
129
|
+
+.has_belongs_to?+ returns true if the class has a belongs_to association.
|
130
|
+
|
131
|
+
+.belongs_to_class returns the class of the belongs_to association, or false if none.
|
132
|
+
|
133
|
+
+.find+ can now take an array arg.
|
134
|
+
|
135
|
+
+#hash_key+ and +#range_key+ readers added.
|
101
136
|
|
102
137
|
|
103
138
|
=== Future milestones
|
104
139
|
|
105
140
|
After the +has_many+ / +belongs_to+ association, the +has_and_belongs_to_many+ assocation
|
106
|
-
will be implemented. This will require secondary indices
|
107
|
-
|
141
|
+
will be implemented. This will require secondary indices, unlike +has_many+ / +belongs_to+.
|
142
|
+
|
143
|
+
OceanDynamo will use association proxies to achieve the same kind of
|
108
144
|
interface as ActiveRecord, e.g.: <code>blog_entry.comments.build(body: "Cool!").save!</code>
|
109
145
|
|
110
146
|
|
@@ -115,7 +151,7 @@ e.g. to implement critical job queues. It will be used increasingly as features
|
|
115
151
|
added to OceanDynamo and will eventually replace all ActiveRecord tables in Ocean.
|
116
152
|
|
117
153
|
|
118
|
-
|
154
|
+
== Installation
|
119
155
|
|
120
156
|
gem install ocean-dynamo
|
121
157
|
|
data/lib/ocean-dynamo.rb
CHANGED
@@ -9,11 +9,12 @@ require "ocean-dynamo/exceptions"
|
|
9
9
|
require "ocean-dynamo/class_variables"
|
10
10
|
require "ocean-dynamo/tables"
|
11
11
|
require "ocean-dynamo/schema"
|
12
|
-
require "ocean-dynamo/callbacks"
|
13
12
|
require "ocean-dynamo/attributes"
|
14
|
-
require "ocean-dynamo/
|
13
|
+
require "ocean-dynamo/callbacks"
|
15
14
|
require "ocean-dynamo/persistence"
|
16
|
-
require "ocean-dynamo/
|
15
|
+
require "ocean-dynamo/queries"
|
16
|
+
|
17
|
+
require "ocean-dynamo/associations/belongs_to"
|
17
18
|
|
18
19
|
|
19
20
|
module OceanDynamo
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module OceanDynamo
|
2
|
+
class Base
|
3
|
+
|
4
|
+
def self.belongs_to(target) # :master, "master", Master
|
5
|
+
target_attr = target.to_s.underscore # "master"
|
6
|
+
target_attr_id = "#{target_attr}_id" # "master_id"
|
7
|
+
target_class = target_attr.camelize.constantize # Master
|
8
|
+
|
9
|
+
assert_only_one_belongs_to!
|
10
|
+
|
11
|
+
self.table_range_key = table_hash_key # The RANGE KEY is variable
|
12
|
+
self.table_hash_key = target_attr_id.to_sym # The HASH KEY is the parent UUID
|
13
|
+
|
14
|
+
attribute table_hash_key, :string # Define :master_id
|
15
|
+
define_attribute_accessors(table_hash_key) # Define master_id, master_id=, master_id?
|
16
|
+
|
17
|
+
# Make sure there always is a parent
|
18
|
+
validates(table_hash_key, presence: true) # Can't save without a parent_id
|
19
|
+
|
20
|
+
# Define the range attribute (our unique UUID)
|
21
|
+
attribute(table_range_key, :string, default: "") # Define :uuid
|
22
|
+
define_attribute_accessors(table_range_key) # define uuid, uuid=, uuid?
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
# Define the parent id attribute
|
27
|
+
attribute target_attr_id, :reference, default: nil, target_class: target_class,
|
28
|
+
association: :belongs_to
|
29
|
+
|
30
|
+
self.class_eval "def #{target_attr}
|
31
|
+
read_and_maybe_load_pointer('#{target_attr_id}')
|
32
|
+
end"
|
33
|
+
|
34
|
+
self.class_eval "def #{target_attr}=(value)
|
35
|
+
write_attribute('#{target_attr_id}', value)
|
36
|
+
@#{target_attr} = value
|
37
|
+
end"
|
38
|
+
|
39
|
+
self.class_eval "def #{target_attr_id}
|
40
|
+
get_pointer_id(@#{target_attr})
|
41
|
+
end"
|
42
|
+
|
43
|
+
self.class_eval "def #{target_attr_id}=(value)
|
44
|
+
write_attribute('#{target_attr_id}', value)
|
45
|
+
@#{target_attr} = value
|
46
|
+
end"
|
47
|
+
# TODO: "?" methods
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
#
|
52
|
+
# Returns true if the class has a belongs_to association.
|
53
|
+
#
|
54
|
+
def self.has_belongs_to?
|
55
|
+
fields[table_hash_key]['association'] == :belongs_to
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
#
|
61
|
+
# Returns the class of the belongs_to association, or false if none.
|
62
|
+
#
|
63
|
+
def self.belongs_to_class
|
64
|
+
has_belongs_to? && fields[table_hash_key]['target_class']
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
#
|
72
|
+
# belongs_to can be specified only once in each model, since we use the range key to
|
73
|
+
# store its UUID and the hash key to store the UUID of the parent, as in
|
74
|
+
# ["parent_uuid", "child_uuid"]. This allows the parent to find all its children
|
75
|
+
# extremely efficiently by using only the primary index. It also allows the child
|
76
|
+
# to find its parent using only its own hash key. Presto: scalability without any
|
77
|
+
# secondary indices in the has_many/belongs_to association.
|
78
|
+
#
|
79
|
+
# Caveat: the parent must have a simple primary key, not a composite one. It *is*
|
80
|
+
# possible to use a composite key, but then the children must use scans to find
|
81
|
+
# their parents. We could conditionalise this, of course, so that the lookup
|
82
|
+
# strategy is transparent to the user.
|
83
|
+
#
|
84
|
+
def self.assert_only_one_belongs_to! # :nodoc:
|
85
|
+
if has_belongs_to?
|
86
|
+
raise OceanDynamo::AssociationMustBeUnique,
|
87
|
+
"#{self} already belongs_to #{belongs_to_class}"
|
88
|
+
end
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
#
|
94
|
+
# This is run by #initialize and by #assign_attributes to set the
|
95
|
+
# association variables (@master, for instance) and its associated attribute
|
96
|
+
# (such as master_id) to the value given.
|
97
|
+
#
|
98
|
+
def assign_associations(attrs) # :nodoc:
|
99
|
+
if attrs && attrs.include?(:master)
|
100
|
+
@master = attrs[:master]
|
101
|
+
write_attribute('master_id', @master)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def read_and_maybe_load_pointer(name) # :nodoc:
|
107
|
+
ptr = read_attribute(name)
|
108
|
+
return nil if ptr.blank?
|
109
|
+
if persisted? && ptr.is_a?(String)
|
110
|
+
parent = fields[name][:target_class].find(ptr, consistent: true) # TODO: true?
|
111
|
+
write_attribute(name, parent) # Keep the instance we've just read
|
112
|
+
else
|
113
|
+
ptr
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def get_pointer_id(ptr) # :nodoc:
|
119
|
+
return nil if ptr.blank?
|
120
|
+
ptr.is_a?(String) ? ptr : ptr.id
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
File without changes
|
@@ -11,6 +11,23 @@ module OceanDynamo
|
|
11
11
|
attr_reader :dynamo_item # :nodoc:
|
12
12
|
|
13
13
|
|
14
|
+
#
|
15
|
+
# Returns the value of the hash key attribute
|
16
|
+
#
|
17
|
+
def hash_key
|
18
|
+
read_attribute(table_hash_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
#
|
23
|
+
# Returns the value of the range key attribute or false if the
|
24
|
+
# table doesn't have a range_key.
|
25
|
+
#
|
26
|
+
def range_key
|
27
|
+
table_range_key && read_attribute(table_range_key)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
14
31
|
def initialize(attrs={})
|
15
32
|
run_callbacks :initialize do
|
16
33
|
@attributes = Hash.new
|
@@ -22,6 +39,7 @@ module OceanDynamo
|
|
22
39
|
@new_record = true
|
23
40
|
raise UnknownPrimaryKey unless table_hash_key
|
24
41
|
end
|
42
|
+
assign_associations(attrs)
|
25
43
|
attrs && attrs.delete_if { |k, v| !fields.has_key?(k) }
|
26
44
|
super(attrs)
|
27
45
|
end
|
@@ -89,6 +107,7 @@ module OceanDynamo
|
|
89
107
|
def assign_attributes(values, without_protection: false)
|
90
108
|
return if values.blank?
|
91
109
|
values = values.stringify_keys
|
110
|
+
assign_associations(values)
|
92
111
|
# if values.respond_to?(:permitted?)
|
93
112
|
# unless values.permitted?
|
94
113
|
# raise ActiveModel::ForbiddenAttributesError
|
@@ -140,7 +159,7 @@ module OceanDynamo
|
|
140
159
|
if respond_to?("#{k}=")
|
141
160
|
raise
|
142
161
|
else
|
143
|
-
raise UnknownAttributeError, "unknown attribute:
|
162
|
+
raise UnknownAttributeError, "unknown attribute: `#{k}'"
|
144
163
|
end
|
145
164
|
end
|
146
165
|
|
@@ -23,7 +23,7 @@ module OceanDynamo
|
|
23
23
|
def initialize(record) # :nodoc:
|
24
24
|
@record = record
|
25
25
|
errors = @record.errors.full_messages.join(", ")
|
26
|
-
super(
|
26
|
+
super(:"#{@record.class}.errors.messages.record_invalid")
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -38,7 +38,7 @@ module OceanDynamo
|
|
38
38
|
def initialize(record) # :nodoc:
|
39
39
|
@record = record
|
40
40
|
errors = @record.errors.full_messages.join(", ")
|
41
|
-
super(
|
41
|
+
super(:"#{@record.class}.errors.messages.record_invalid")
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -54,4 +54,10 @@ module OceanDynamo
|
|
54
54
|
|
55
55
|
class AssociationTypeMismatch < DynamoError; end
|
56
56
|
|
57
|
+
|
58
|
+
class BelongsToError < DynamoError; end
|
59
|
+
class AssociationMustBeUnique < BelongsToError; end
|
60
|
+
class RangeKeyMustNotBeSpecified < BelongsToError; end
|
61
|
+
class HashKeyMayNotBeNamedId < BelongsToError; end
|
62
|
+
|
57
63
|
end
|
@@ -122,8 +122,13 @@ module OceanDynamo
|
|
122
122
|
run_callbacks :commit do
|
123
123
|
run_callbacks :save do
|
124
124
|
run_callbacks :create do
|
125
|
-
|
126
|
-
|
125
|
+
# Default the correct hash key to a UUID
|
126
|
+
if self.class.has_belongs_to?
|
127
|
+
write_attribute(table_range_key, SecureRandom.uuid) if range_key.blank?
|
128
|
+
else
|
129
|
+
write_attribute(table_hash_key, SecureRandom.uuid) if hash_key.blank?
|
130
|
+
end
|
131
|
+
|
127
132
|
set_timestamps
|
128
133
|
dynamo_persist
|
129
134
|
true
|
@@ -171,8 +176,8 @@ module OceanDynamo
|
|
171
176
|
|
172
177
|
|
173
178
|
def reload(**keywords)
|
174
|
-
|
175
|
-
new_instance = self.class.find(
|
179
|
+
raise "HELLISHNESS" if id == range_key
|
180
|
+
new_instance = self.class.find(hash_key, range_key, **keywords)
|
176
181
|
assign_attributes(new_instance.attributes)
|
177
182
|
self
|
178
183
|
end
|
@@ -295,16 +300,15 @@ module OceanDynamo
|
|
295
300
|
|
296
301
|
def serialize_attribute(attribute, value, metadata=fields[attribute],
|
297
302
|
target_class: metadata[:target_class],
|
298
|
-
type: metadata[:type]
|
299
|
-
no_save: metadata[:no_save]
|
303
|
+
type: metadata[:type]
|
300
304
|
)
|
301
305
|
return nil if value == nil
|
306
|
+
#value = value.id if value.kind_of?(target_class)
|
302
307
|
case type
|
303
308
|
when :reference
|
304
|
-
return nil if no_save
|
305
309
|
raise DynamoError, ":reference must always have a :target_class" unless target_class
|
306
310
|
return value if value.is_a?(String)
|
307
|
-
return value.id if value.
|
311
|
+
return value.id if value.kind_of?(target_class)
|
308
312
|
raise AssociationTypeMismatch, "can't save a #{value.class} in a #{target_class} :reference"
|
309
313
|
when :string
|
310
314
|
return nil if ["", []].include?(value)
|
data/lib/ocean-dynamo/queries.rb
CHANGED
@@ -2,9 +2,13 @@ module OceanDynamo
|
|
2
2
|
class Base
|
3
3
|
|
4
4
|
def self.find(hash, range=nil, consistent: false)
|
5
|
+
return hash.collect {|elem| find elem, range, consistent: consistent } if hash.is_a?(Array)
|
5
6
|
_late_connect?
|
7
|
+
hash = hash.id if hash.kind_of?(Base) # TODO: We have (innocuous) leakage, fix!
|
6
8
|
item = dynamo_items[hash, range]
|
7
|
-
|
9
|
+
unless item.exists?
|
10
|
+
raise RecordNotFound, "can't find a #{self} with primary key ['#{hash}', #{range.inspect}]"
|
11
|
+
end
|
8
12
|
new._setup_from_dynamo(item, consistent: consistent)
|
9
13
|
end
|
10
14
|
|
data/lib/ocean-dynamo/schema.rb
CHANGED
@@ -41,13 +41,7 @@ module OceanDynamo
|
|
41
41
|
attribute(lock_attribute, :integer, default: 0) if locking
|
42
42
|
block.call
|
43
43
|
# Define attribute accessors
|
44
|
-
fields.each
|
45
|
-
name = name.to_s
|
46
|
-
# We define accessors even if the name is 'id' (for which we already have methods)
|
47
|
-
self.class_eval "def #{name}; read_attribute('#{name}'); end"
|
48
|
-
self.class_eval "def #{name}=(value); write_attribute('#{name}', value); end"
|
49
|
-
self.class_eval "def #{name}?; read_attribute('#{name}').present?; end"
|
50
|
-
end
|
44
|
+
fields.each { |name, md| define_attribute_accessors(name) }
|
51
45
|
# Connect to AWS
|
52
46
|
establish_db_connection if connect == true
|
53
47
|
# Finally return the full table name
|
@@ -55,6 +49,14 @@ module OceanDynamo
|
|
55
49
|
end
|
56
50
|
|
57
51
|
|
52
|
+
def self.define_attribute_accessors(name)
|
53
|
+
name = name.to_s
|
54
|
+
self.class_eval "def #{name}; read_attribute('#{name}'); end"
|
55
|
+
self.class_eval "def #{name}=(value); write_attribute('#{name}', value); end"
|
56
|
+
self.class_eval "def #{name}?; read_attribute('#{name}').present?; end"
|
57
|
+
end
|
58
|
+
|
59
|
+
|
58
60
|
def self.compute_table_name
|
59
61
|
name.pluralize.underscore
|
60
62
|
end
|
data/lib/ocean-dynamo/tables.rb
CHANGED
@@ -43,19 +43,30 @@ module OceanDynamo
|
|
43
43
|
|
44
44
|
|
45
45
|
def self.set_dynamo_table_keys
|
46
|
-
|
46
|
+
hash_key_type = fields[table_hash_key][:type]
|
47
|
+
hash_key_type = :string if hash_key_type == :reference
|
48
|
+
dynamo_table.hash_key = [table_hash_key, hash_key_type]
|
49
|
+
|
47
50
|
if table_range_key
|
48
|
-
|
51
|
+
range_key_type = fields[table_range_key][:type]
|
52
|
+
#range_key_type = :string if range_key_type == :reference
|
53
|
+
dynamo_table.range_key = [table_range_key, range_key_type]
|
49
54
|
end
|
50
55
|
end
|
51
56
|
|
52
57
|
|
53
58
|
def self.create_table
|
59
|
+
hash_key_type = fields[table_hash_key][:type]
|
60
|
+
hash_key_type = :string if hash_key_type == :reference
|
61
|
+
|
62
|
+
range_key_type = table_range_key && fields[table_range_key][:type]
|
63
|
+
#range_key_type = :string if range_key_type == :reference
|
64
|
+
|
54
65
|
self.dynamo_table = dynamo_client.tables.create(table_full_name,
|
55
66
|
table_read_capacity_units, table_write_capacity_units,
|
56
|
-
hash_key: { table_hash_key =>
|
57
|
-
range_key: table_range_key && { table_range_key =>
|
58
|
-
|
67
|
+
hash_key: { table_hash_key => hash_key_type},
|
68
|
+
range_key: table_range_key && { table_range_key => range_key_type }
|
69
|
+
)
|
59
70
|
sleep 1 until dynamo_table.status == :active
|
60
71
|
setup_dynamo
|
61
72
|
true
|
data/lib/ocean-dynamo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ocean-dynamo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Bengtson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-09-
|
11
|
+
date: 2013-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -160,12 +160,12 @@ extensions: []
|
|
160
160
|
extra_rdoc_files: []
|
161
161
|
files:
|
162
162
|
- config/routes.rb
|
163
|
-
- lib/ocean-dynamo/associations.rb
|
163
|
+
- lib/ocean-dynamo/associations/belongs_to.rb
|
164
|
+
- lib/ocean-dynamo/associations/collection_proxy.rb
|
164
165
|
- lib/ocean-dynamo/attributes.rb
|
165
166
|
- lib/ocean-dynamo/base.rb
|
166
167
|
- lib/ocean-dynamo/callbacks.rb
|
167
168
|
- lib/ocean-dynamo/class_variables.rb
|
168
|
-
- lib/ocean-dynamo/collection_proxy.rb
|
169
169
|
- lib/ocean-dynamo/engine.rb
|
170
170
|
- lib/ocean-dynamo/exceptions.rb
|
171
171
|
- lib/ocean-dynamo/persistence.rb
|
@@ -1,51 +0,0 @@
|
|
1
|
-
module OceanDynamo
|
2
|
-
class Base
|
3
|
-
|
4
|
-
def self.belongs_to(target) # :api_user, "api_user", ApiUser
|
5
|
-
target_attr = target.to_s.underscore # "api_user"
|
6
|
-
target_attr_id = "#{target_attr}_id" # "api_user_id"
|
7
|
-
target_class = target_attr.camelize.constantize # ApiUser
|
8
|
-
attribute target_attr_id, :reference, default: nil, target_class: target_class
|
9
|
-
attribute target_attr, :reference, default: nil, target_class: target_class, no_save: true
|
10
|
-
|
11
|
-
self.class_eval "def #{target_attr}
|
12
|
-
read_and_maybe_load_pointer('#{target_attr_id}')
|
13
|
-
end"
|
14
|
-
|
15
|
-
self.class_eval "def #{target_attr}=(value)
|
16
|
-
write_attribute('#{target_attr_id}', value)
|
17
|
-
write_attribute('#{target_attr}', value)
|
18
|
-
end"
|
19
|
-
|
20
|
-
self.class_eval "def #{target_attr_id}
|
21
|
-
read_pointer_id('#{target_attr}')
|
22
|
-
end"
|
23
|
-
|
24
|
-
self.class_eval "def #{target_attr_id}=(value)
|
25
|
-
write_attribute('#{target_attr_id}', value)
|
26
|
-
write_attribute('#{target_attr}', value)
|
27
|
-
end"
|
28
|
-
# TODO: "?" methods
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
protected
|
33
|
-
|
34
|
-
def read_and_maybe_load_pointer(name) # :nodoc:
|
35
|
-
ptr = read_attribute(name)
|
36
|
-
return nil if ptr.blank?
|
37
|
-
if persisted? && ptr.is_a?(String)
|
38
|
-
write_attribute(name, fields[name][:target_class].find(ptr)) # Keep the instance we've just read
|
39
|
-
else
|
40
|
-
ptr
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def read_pointer_id(name) # :nodoc:
|
45
|
-
ptr = read_attribute(name)
|
46
|
-
return nil if ptr.blank?
|
47
|
-
ptr.is_a?(String) ? ptr : ptr.id
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
end
|