ocean-dynamo 0.3.13 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ff62cac1b81cd01f68f4d8be25004ae94c0afa14
4
- data.tar.gz: f5fa8a4e8d8c7b86421a2ad977fd279136a08402
3
+ metadata.gz: 65a6bf63b42ff8ffecd0ea41b312b5657e62d5c9
4
+ data.tar.gz: 3055b4f29b1006c4fe80de69d97e31802653eb27
5
5
  SHA512:
6
- metadata.gz: d2296afae10ea96ca8eecdb5822878da60fbd2c20b293a868694f6167bfc95ddb9519153a8b1fb08bacc7ec2506a25c307aefc4cf01398e4486d17e01f568c9e
7
- data.tar.gz: 02ec483b0b395f07e5213bc572cf57e30ad30cd420d0b9bf39fafbcb04198f391bb8b73603f22c1e6b98dc728d18e7491e1d84c72e0ecd0232ccabfe62fbb798
6
+ metadata.gz: 1d365ff5096522676044fa6edbb3fd8e4c48de842636d820192e8674ce046de2f3af871d0b50236a837363daa24349819485003f09d32f06ab9d52d5c457745a
7
+ data.tar.gz: 997a24d91aa6a42f47dc38262200158120781ba74a9bd2355df304291d3796c3076d9dcd0710ce1cde96594abc8ea35cc617025f3034664b898e8d40ad0501a2
data/README.rdoc CHANGED
@@ -34,7 +34,7 @@ which means it will scale without limits.
34
34
 
35
35
  The following example shows the basic syntax for declaring a DynamoDB-based schema.
36
36
 
37
- class AsyncJob < OceanDynamo::Base
37
+ class AsyncJob < OceanDynamo::Table
38
38
 
39
39
  dynamo_schema(:uuid) do
40
40
  attribute :credentials, :string
@@ -97,7 +97,7 @@ value will return the empty string, <tt>""</tt>.
97
97
 
98
98
  The following example shows how to set up a +belongs_to+ relation:
99
99
 
100
- class Parent < OceanDynamo::Base
100
+ class Parent < OceanDynamo::Table
101
101
  dynamo_schema do
102
102
  attribute :this
103
103
  attribute :that
@@ -107,7 +107,7 @@ The following example shows how to set up a +belongs_to+ relation:
107
107
  end
108
108
 
109
109
 
110
- class Child < OceanDynamo::Base
110
+ class Child < OceanDynamo::Table
111
111
  dynamo_schema(:uuid) do
112
112
  attribute :foo
113
113
  attribute :bar
data/lib/ocean-dynamo.rb CHANGED
@@ -4,19 +4,48 @@ require "aws-sdk"
4
4
  require "active_model"
5
5
  require "active_support"
6
6
 
7
- require "ocean-dynamo/base"
8
7
  require "ocean-dynamo/exceptions"
9
8
  require "ocean-dynamo/class_variables"
9
+
10
+ require "ocean-dynamo/basal"
10
11
  require "ocean-dynamo/tables"
11
12
  require "ocean-dynamo/schema"
12
13
  require "ocean-dynamo/attributes"
13
- require "ocean-dynamo/callbacks"
14
14
  require "ocean-dynamo/persistence"
15
15
  require "ocean-dynamo/queries"
16
-
16
+ require "ocean-dynamo/associations/associations"
17
17
  require "ocean-dynamo/associations/belongs_to"
18
+ require "ocean-dynamo/associations/has_many"
18
19
 
19
20
 
20
21
  module OceanDynamo
22
+ class Table
23
+
24
+ include ActiveModel::Model
25
+ include ActiveModel::Validations::Callbacks
26
+
27
+ define_model_callbacks :initialize, only: :after
28
+ define_model_callbacks :save
29
+ define_model_callbacks :create
30
+ define_model_callbacks :update
31
+ define_model_callbacks :destroy
32
+ define_model_callbacks :commit, only: :after
33
+ define_model_callbacks :touch
34
+
35
+
36
+ include Basal
37
+
38
+ extend Tables
39
+ extend Schema
40
+
41
+ include Attributes
42
+ include Persistence
43
+ extend Queries
44
+
45
+ include Associations
46
+ include BelongsTo
47
+ include HasMany
48
+
21
49
 
50
+ end
22
51
  end
@@ -0,0 +1,58 @@
1
+ module OceanDynamo
2
+ module Associations
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+
9
+ # ---------------------------------------------------------
10
+ #
11
+ # Class methods
12
+ #
13
+ # ---------------------------------------------------------
14
+
15
+ module ClassMethods
16
+
17
+ #
18
+ #
19
+ #
20
+ def relates_to(klass)
21
+ relations[klass]
22
+ end
23
+
24
+ #
25
+ #
26
+ #
27
+ def register_relation(klass, value)
28
+ relations[klass] = value
29
+ end
30
+
31
+ #
32
+ #
33
+ #
34
+ def clear_relations
35
+ self.relations = Hash.new
36
+ end
37
+
38
+
39
+ def dynamo_schema(*)
40
+ clear_relations
41
+ super
42
+ end
43
+
44
+ end
45
+
46
+
47
+ # ---------------------------------------------------------
48
+ #
49
+ # Instance variables and methods
50
+ #
51
+ # ---------------------------------------------------------
52
+
53
+ # def initialize(*)
54
+ # self.class.clear_relations
55
+ # end
56
+
57
+ end
58
+ end
@@ -1,95 +1,123 @@
1
1
  module OceanDynamo
2
- class Base
2
+ module BelongsTo
3
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
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
8
 
9
- assert_only_one_belongs_to!
9
+ # ---------------------------------------------------------
10
+ #
11
+ # Class methods
12
+ #
13
+ # ---------------------------------------------------------
10
14
 
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
15
+ module ClassMethods
16
+
17
+ #
18
+ #
19
+ #
20
+ def belongs_to(target) # :master, "master", Master
21
+ target_attr = target.to_s.underscore # "master"
22
+ target_attr_id = "#{target_attr}_id" # "master_id"
23
+ target_class = target_attr.camelize.constantize # Master
13
24
 
14
- attribute table_hash_key, :string # Define :master_id
15
- define_attribute_accessors(table_hash_key) # Define master_id, master_id=, master_id?
25
+ assert_only_one_belongs_to!
16
26
 
17
- # Make sure there always is a parent
18
- validates(table_hash_key, presence: true) # Can't save without a parent_id
27
+ self.table_range_key = table_hash_key # The RANGE KEY is variable
28
+ self.table_hash_key = target_attr_id.to_sym # The HASH KEY is the parent UUID
19
29
 
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?
30
+ attribute table_hash_key, :string # Define :master_id
31
+ define_attribute_accessors(table_hash_key) # Define master_id, master_id=, master_id?
23
32
 
33
+ # Make sure there always is a parent
34
+ validates(table_hash_key, presence: true) # Can't save without a parent_id
24
35
 
36
+ # Define the range attribute (our unique UUID)
37
+ attribute(table_range_key, :string, default: "") # Define :uuid
38
+ define_attribute_accessors(table_range_key) # define uuid, uuid=, uuid?
25
39
 
26
- # Define the parent id attribute
27
- attribute target_attr_id, :reference, default: nil, target_class: target_class,
28
- association: :belongs_to
29
40
 
30
- self.class_eval "def #{target_attr}
31
- read_and_maybe_load_pointer('#{target_attr_id}')
32
- end"
33
41
 
34
- self.class_eval "def #{target_attr}=(value)
35
- write_attribute('#{target_attr_id}', value)
36
- @#{target_attr} = value
37
- end"
42
+ # Define the parent id attribute
43
+ attribute target_attr_id, :reference, default: nil, target_class: target_class,
44
+ association: :belongs_to
45
+ register_relation(target_class, :belongs_to)
38
46
 
39
- self.class_eval "def #{target_attr_id}
40
- get_pointer_id(@#{target_attr})
41
- end"
42
47
 
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
48
+ # Define accessors for instances
49
+ self.class_eval "def #{target_attr}
50
+ read_and_maybe_load_pointer('#{target_attr_id}')
51
+ end"
49
52
 
53
+ self.class_eval "def #{target_attr}=(value)
54
+ write_attribute('#{target_attr_id}', value)
55
+ @#{target_attr} = value
56
+ end"
50
57
 
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
58
+ self.class_eval "def #{target_attr_id}
59
+ get_pointer_id(@#{target_attr})
60
+ end"
57
61
 
62
+ self.class_eval "def #{target_attr_id}=(value)
63
+ write_attribute('#{target_attr_id}', value)
64
+ @#{target_attr} = value
65
+ end"
66
+ # TODO: "?" methods
67
+ end
58
68
 
59
69
 
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
70
+ #
71
+ # Returns true if the class has a belongs_to association.
72
+ #
73
+ def has_belongs_to?
74
+ fields[table_hash_key]['association'] == :belongs_to
75
+ end
66
76
 
67
77
 
68
78
 
69
- protected
79
+ #
80
+ # Returns the class of the belongs_to association, or false if none.
81
+ #
82
+ def belongs_to_class
83
+ has_belongs_to? && fields[table_hash_key]['target_class']
84
+ end
70
85
 
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}"
86
+
87
+
88
+ protected
89
+
90
+ #
91
+ # belongs_to can be specified only once in each model, since we use the range key to
92
+ # store its UUID and the hash key to store the UUID of the parent, as in
93
+ # ["parent_uuid", "child_uuid"]. This allows the parent to find all its children
94
+ # extremely efficiently by using only the primary index. It also allows the child
95
+ # to find its parent using only its own hash key. Presto: scalability without any
96
+ # secondary indices in the has_many/belongs_to association.
97
+ #
98
+ # Caveat: the parent must have a simple primary key, not a composite one. It *is*
99
+ # possible to use a composite key, but then the children must use scans to find
100
+ # their parents. We could conditionalise this, of course, so that the lookup
101
+ # strategy is transparent to the user.
102
+ #
103
+ def assert_only_one_belongs_to! # :nodoc:
104
+ if has_belongs_to?
105
+ raise OceanDynamo::AssociationMustBeUnique,
106
+ "#{self} already belongs_to #{belongs_to_class}"
107
+ end
108
+ false
88
109
  end
89
- false
90
110
  end
91
111
 
92
112
 
113
+ # ---------------------------------------------------------
114
+ #
115
+ # Instance variables and methods
116
+ #
117
+ # ---------------------------------------------------------
118
+
119
+ protected
120
+
93
121
  #
94
122
  # This is run by #initialize and by #assign_attributes to set the
95
123
  # association variables (@master, for instance) and its associated attribute
@@ -15,7 +15,7 @@ module OceanDynamo
15
15
  #
16
16
  # For example, given
17
17
  #
18
- # class Blog < OceanDynamo::Base
18
+ # class Blog < OceanDynamo::Table
19
19
  # has_many :posts
20
20
  # end
21
21
  #
@@ -66,7 +66,7 @@ module OceanDynamo
66
66
  #
67
67
  # *First:* Specify a subset of fields to be selected from the result set.
68
68
  #
69
- # class Person < OceanDynamo::Base
69
+ # class Person < OceanDynamo::Table
70
70
  # has_many :pets
71
71
  # end
72
72
  #
@@ -121,10 +121,10 @@ module OceanDynamo
121
121
  end
122
122
 
123
123
  # Finds an object in the collection responding to the +id+. Uses the same
124
- # rules as <tt>OceanDynamo::Base.find</tt>. Returns <tt>OceanDynamo::RecordNotFound</tt>
124
+ # rules as <tt>OceanDynamo::Table.find</tt>. Returns <tt>OceanDynamo::RecordNotFound</tt>
125
125
  # error if the object can not be found.
126
126
  #
127
- # class Person < OceanDynamo::Base
127
+ # class Person < OceanDynamo::Table
128
128
  # has_many :pets
129
129
  # end
130
130
  #
@@ -154,7 +154,7 @@ module OceanDynamo
154
154
  # If the collection is empty, the first form returns +nil+, and the second
155
155
  # form returns an empty array.
156
156
  #
157
- # class Person < OceanDynamo::Base
157
+ # class Person < OceanDynamo::Table
158
158
  # has_many :pets
159
159
  # end
160
160
  #
@@ -184,7 +184,7 @@ module OceanDynamo
184
184
  # If the collection is empty, the first form returns +nil+, and the second
185
185
  # form returns an empty array.
186
186
  #
187
- # class Person < OceanDynamo::Base
187
+ # class Person < OceanDynamo::Table
188
188
  # has_many :pets
189
189
  # end
190
190
  #
@@ -215,7 +215,7 @@ module OceanDynamo
215
215
  # You can pass an array of attributes hashes, this will return an array
216
216
  # with the new objects.
217
217
  #
218
- # class Person < OceanDynamo::Base
218
+ # class Person < OceanDynamo::Table
219
219
  # has_many :pets
220
220
  # end
221
221
  #
@@ -243,7 +243,7 @@ module OceanDynamo
243
243
  # attributes, linked to this object and that has already been saved (if it
244
244
  # passes the validations).
245
245
  #
246
- # class Person < OceanDynamo::Base
246
+ # class Person < OceanDynamo::Table
247
247
  # has_many :pets
248
248
  # end
249
249
  #
@@ -271,7 +271,7 @@ module OceanDynamo
271
271
 
272
272
  # Like +create+, except that if the record is invalid, raises an exception.
273
273
  #
274
- # class Person < OceanDynamo::Base
274
+ # class Person < OceanDynamo::Table
275
275
  # has_many :pets
276
276
  # end
277
277
  #
@@ -290,7 +290,7 @@ module OceanDynamo
290
290
  # inserts each record, +push+ and +concat+ behave identically. Returns +self+
291
291
  # so method calls may be chained.
292
292
  #
293
- # class Person < OceanDynamo::Base
293
+ # class Person < OceanDynamo::Table
294
294
  # pets :has_many
295
295
  # end
296
296
  #
@@ -316,7 +316,7 @@ module OceanDynamo
316
316
  # Replaces this collection with +other_array+. This will perform a diff
317
317
  # and delete/add only records that have changed.
318
318
  #
319
- # class Person < OceanDynamo::Base
319
+ # class Person < OceanDynamo::Table
320
320
  # has_many :pets
321
321
  # end
322
322
  #
@@ -348,7 +348,7 @@ module OceanDynamo
348
348
  # sets the foreign keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>,
349
349
  # the default strategy is +delete_all+.
350
350
  #
351
- # class Person < OceanDynamo::Base
351
+ # class Person < OceanDynamo::Table
352
352
  # has_many :pets # dependent: :nullify option by default
353
353
  # end
354
354
  #
@@ -381,7 +381,7 @@ module OceanDynamo
381
381
  # are removed by calling their +destroy+ method. See +destroy+ for more
382
382
  # information.
383
383
  #
384
- # class Person < OceanDynamo::Base
384
+ # class Person < OceanDynamo::Table
385
385
  # has_many :pets, dependent: :destroy
386
386
  # end
387
387
  #
@@ -406,7 +406,7 @@ module OceanDynamo
406
406
  # If it is set to <tt>:delete_all</tt>, all the objects are deleted
407
407
  # *without* calling their +destroy+ method.
408
408
  #
409
- # class Person < OceanDynamo::Base
409
+ # class Person < OceanDynamo::Table
410
410
  # has_many :pets, dependent: :delete_all
411
411
  # end
412
412
  #
@@ -435,7 +435,7 @@ module OceanDynamo
435
435
  # This will _always_ remove the records ignoring the +:dependent+
436
436
  # option.
437
437
  #
438
- # class Person < OceanDynamo::Base
438
+ # class Person < OceanDynamo::Table
439
439
  # has_many :pets
440
440
  # end
441
441
  #
@@ -467,7 +467,7 @@ module OceanDynamo
467
467
  # keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
468
468
  # strategy is +delete_all+.
469
469
  #
470
- # class Person < OceanDynamo::Base
470
+ # class Person < OceanDynamo::Table
471
471
  # has_many :pets # dependent: :nullify option by default
472
472
  # end
473
473
  #
@@ -495,7 +495,7 @@ module OceanDynamo
495
495
  # If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
496
496
  # their +destroy+ method. See +destroy+ for more information.
497
497
  #
498
- # class Person < OceanDynamo::Base
498
+ # class Person < OceanDynamo::Table
499
499
  # has_many :pets, dependent: :destroy
500
500
  # end
501
501
  #
@@ -523,7 +523,7 @@ module OceanDynamo
523
523
  # If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
524
524
  # *without* calling their +destroy+ method.
525
525
  #
526
- # class Person < OceanDynamo::Base
526
+ # class Person < OceanDynamo::Table
527
527
  # has_many :pets, dependent: :delete_all
528
528
  # end
529
529
  #
@@ -551,7 +551,7 @@ module OceanDynamo
551
551
  # You can pass +Fixnum+ or +String+ values, it finds the records
552
552
  # responding to the +id+ and executes delete on them.
553
553
  #
554
- # class Person < OceanDynamo::Base
554
+ # class Person < OceanDynamo::Table
555
555
  # has_many :pets
556
556
  # end
557
557
  #
@@ -579,7 +579,7 @@ module OceanDynamo
579
579
  # This method will _always_ remove record from the database ignoring
580
580
  # the +:dependent+ option. Returns an array with the removed records.
581
581
  #
582
- # class Person < OceanDynamo::Base
582
+ # class Person < OceanDynamo::Table
583
583
  # has_many :pets
584
584
  # end
585
585
  #
@@ -649,7 +649,7 @@ module OceanDynamo
649
649
 
650
650
  # Specifies whether the records should be unique or not.
651
651
  #
652
- # class Person < OceanDynamo::Base
652
+ # class Person < OceanDynamo::Table
653
653
  # has_many :pets
654
654
  # end
655
655
  #
@@ -668,7 +668,7 @@ module OceanDynamo
668
668
 
669
669
  # Count all records using SQL.
670
670
  #
671
- # class Person < OceanDynamo::Base
671
+ # class Person < OceanDynamo::Table
672
672
  # has_many :pets
673
673
  # end
674
674
  #
@@ -690,7 +690,7 @@ module OceanDynamo
690
690
  # equivalent. If not and you are going to need the records anyway
691
691
  # +length+ will take one less query. Otherwise +size+ is more efficient.
692
692
  #
693
- # class Person < OceanDynamo::Base
693
+ # class Person < OceanDynamo::Table
694
694
  # has_many :pets
695
695
  # end
696
696
  #
@@ -716,7 +716,7 @@ module OceanDynamo
716
716
  # equivalent. If not and you are going to need the records anyway this
717
717
  # method will take one less query. Otherwise +size+ is more efficient.
718
718
  #
719
- # class Person < OceanDynamo::Base
719
+ # class Person < OceanDynamo::Table
720
720
  # has_many :pets
721
721
  # end
722
722
  #
@@ -742,7 +742,7 @@ module OceanDynamo
742
742
  # not already been loaded and you are going to fetch the records anyway it
743
743
  # is better to check <tt>collection.length.zero?</tt>.
744
744
  #
745
- # class Person < OceanDynamo::Base
745
+ # class Person < OceanDynamo::Table
746
746
  # has_many :pets
747
747
  # end
748
748
  #
@@ -759,7 +759,7 @@ module OceanDynamo
759
759
 
760
760
  # Returns +true+ if the collection is not empty.
761
761
  #
762
- # class Person < OceanDynamo::Base
762
+ # class Person < OceanDynamo::Table
763
763
  # has_many :pets
764
764
  # end
765
765
  #
@@ -793,7 +793,7 @@ module OceanDynamo
793
793
  # Returns true if the collection has more than one record.
794
794
  # Equivalent to <tt>collection.size > 1</tt>.
795
795
  #
796
- # class Person < OceanDynamo::Base
796
+ # class Person < OceanDynamo::Table
797
797
  # has_many :pets
798
798
  # end
799
799
  #
@@ -830,7 +830,7 @@ module OceanDynamo
830
830
 
831
831
  # Returns +true+ if the given object is present in the collection.
832
832
  #
833
- # class Person < OceanDynamo::Base
833
+ # class Person < OceanDynamo::Table
834
834
  # has_many :pets
835
835
  # end
836
836
  #
@@ -869,7 +869,7 @@ module OceanDynamo
869
869
  # to the corresponding element in the other array, otherwise returns
870
870
  # +false+.
871
871
  #
872
- # class Person < OceanDynamo::Base
872
+ # class Person < OceanDynamo::Table
873
873
  # has_many :pets
874
874
  # end
875
875
  #
@@ -895,7 +895,7 @@ module OceanDynamo
895
895
  # Returns a new array of objects from the collection. If the collection
896
896
  # hasn't been loaded, it fetches the records from the database.
897
897
  #
898
- # class Person < OceanDynamo::Base
898
+ # class Person < OceanDynamo::Table
899
899
  # has_many :pets
900
900
  # end
901
901
  #
@@ -934,7 +934,7 @@ module OceanDynamo
934
934
  # to the association's primary key. Returns +self+, so several appends may be
935
935
  # chained together.
936
936
  #
937
- # class Person < OceanDynamo::Base
937
+ # class Person < OceanDynamo::Table
938
938
  # has_many :pets
939
939
  # end
940
940
  #
@@ -971,7 +971,7 @@ module OceanDynamo
971
971
  # Reloads the collection from the database. Returns +self+.
972
972
  # Equivalent to <tt>collection(true)</tt>.
973
973
  #
974
- # class Person < OceanDynamo::Base
974
+ # class Person < OceanDynamo::Table
975
975
  # has_many :pets
976
976
  # end
977
977
  #
@@ -0,0 +1,69 @@
1
+ module OceanDynamo
2
+ module HasMany
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+
9
+ # ---------------------------------------------------------
10
+ #
11
+ # Class methods
12
+ #
13
+ # ---------------------------------------------------------
14
+
15
+ module ClassMethods
16
+
17
+ #
18
+ #
19
+ #
20
+ def has_many(children) # :children
21
+ children_attr = children.to_s.underscore # "children"
22
+ child_class = children_attr.singularize.camelize.constantize # Child
23
+ register_relation(child_class, :has_many)
24
+ # Define accessors for instances
25
+ self.class_eval "def #{children_attr}; read_children(#{child_class}); end"
26
+ self.class_eval "def #{children_attr}=(value); write_children(#{child_class}, value); end"
27
+ # TODO: "?" method
28
+ end
29
+
30
+ end
31
+
32
+
33
+ # ---------------------------------------------------------
34
+ #
35
+ # Instance variables and methods
36
+ #
37
+ # ---------------------------------------------------------
38
+
39
+ #
40
+ #
41
+ #
42
+ def read_children(child_class)
43
+ if new_record?
44
+ nil
45
+ else
46
+ result = Array.new
47
+ _late_connect?
48
+ child_items = child_class.dynamo_items
49
+ child_items.query(hash_value: id, range_gte: "0") do |item_data|
50
+ result << child_class.new._setup_from_dynamo(item_data)
51
+ end
52
+ result
53
+ end
54
+ end
55
+
56
+ #
57
+ #
58
+ #
59
+ def write_children(child_class, value)
60
+ return nil if value.blank?
61
+ raise AssociationTypeMismatch, "not an array or nil" if !value.is_a?(Array)
62
+ raise AssociationTypeMismatch, "an array element is not a #{child_class}" unless value.all? { |m| m.is_a?(child_class) }
63
+ # We now know that value is an array containing only members of the child_class
64
+ value.each(&:save!)
65
+ value
66
+ end
67
+
68
+ end
69
+ end
@@ -1,5 +1,26 @@
1
1
  module OceanDynamo
2
- class Base
2
+ module Attributes
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+
9
+ # ---------------------------------------------------------
10
+ #
11
+ # Class methods
12
+ #
13
+ # ---------------------------------------------------------
14
+
15
+ module ClassMethods
16
+ end
17
+
18
+
19
+ # ---------------------------------------------------------
20
+ #
21
+ # Instance variables and methods
22
+ #
23
+ # ---------------------------------------------------------
3
24
 
4
25
  #
5
26
  # The hash of attributes and their values. Keys are strings.
@@ -0,0 +1,32 @@
1
+ module Basal
2
+
3
+ def ==(comparison_object)
4
+ super ||
5
+ comparison_object.instance_of?(self.class) &&
6
+ id.present? &&
7
+ comparison_object.id == id
8
+ end
9
+ alias :eql? :==
10
+
11
+
12
+ # Clone and freeze the attributes hash such that associations are still
13
+ # accessible, even on destroyed records, but cloned models will not be
14
+ # frozen.
15
+ def freeze
16
+ @attributes = @attributes.clone.freeze
17
+ self
18
+ end
19
+
20
+ # Returns +true+ if the attributes hash has been frozen.
21
+ def frozen?
22
+ @attributes.frozen?
23
+ end
24
+
25
+ # Allows sort on objects
26
+ def <=>(other_object)
27
+ if other_object.is_a?(self.class)
28
+ self.to_key <=> other_object.to_key
29
+ end
30
+ end
31
+
32
+ end
@@ -1,5 +1,5 @@
1
1
  module OceanDynamo
2
- class Base
2
+ class Table
3
3
 
4
4
  class_attribute :dynamo_client, instance_writer: false
5
5
  self.dynamo_client = nil
@@ -49,5 +49,8 @@ module OceanDynamo
49
49
  class_attribute :timestamp_attributes, instance_writer: false
50
50
  self.timestamp_attributes = [:created_at, :updated_at]
51
51
 
52
+ class_attribute :relations, instance_writer: false
53
+ self.relations = nil
54
+
52
55
  end
53
56
  end
@@ -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(:"#{@record.class}.errors.messages.record_invalid")
26
+ super(@record.errors.messages)
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(:"#{@record.class}.errors.messages.record_invalid")
41
+ super(@record.errors.messages)
42
42
  end
43
43
  end
44
44
 
@@ -60,4 +60,7 @@ module OceanDynamo
60
60
  class RangeKeyMustNotBeSpecified < BelongsToError; end
61
61
  class HashKeyMayNotBeNamedId < BelongsToError; end
62
62
 
63
+ class HasManyError < DynamoError; end
64
+
65
+
63
66
  end
@@ -1,5 +1,10 @@
1
1
  module OceanDynamo
2
- class Base
2
+ module Persistence
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
3
8
 
4
9
  # ---------------------------------------------------------
5
10
  #
@@ -7,44 +12,55 @@ module OceanDynamo
7
12
  #
8
13
  # ---------------------------------------------------------
9
14
 
15
+ module ClassMethods
10
16
 
11
- def self.create(attributes = nil, &block)
12
- object = new(attributes)
13
- yield(object) if block_given?
14
- object.save
15
- object
16
- end
17
+ def create(attributes = nil, &block)
18
+ object = new(attributes)
19
+ yield(object) if block_given?
20
+ object.save
21
+ object
22
+ end
17
23
 
18
24
 
19
- def self.create!(attributes = nil, &block)
20
- object = new(attributes)
21
- yield(object) if block_given?
22
- object.save!
23
- object
24
- end
25
+ def create!(attributes = nil, &block)
26
+ object = new(attributes)
27
+ yield(object) if block_given?
28
+ object.save!
29
+ object
30
+ end
25
31
 
26
32
 
27
- def self.delete(hash, range=nil)
28
- item = dynamo_items[hash, range]
29
- return false unless item.exists?
30
- item.delete
31
- true
32
- end
33
+ def delete(hash, range=nil)
34
+ item = dynamo_items[hash, range]
35
+ return false unless item.exists?
36
+ item.delete
37
+ true
38
+ end
33
39
 
34
40
 
35
- def self.delete_all
36
- dynamo_items.each() do |item|
37
- item.delete
41
+ def delete_all
42
+ dynamo_items.each() do |item|
43
+ item.delete
44
+ end
45
+ nil
46
+ end
47
+
48
+
49
+ def destroy_all
50
+ dynamo_items.select() do |item_data|
51
+ new._setup_from_dynamo(item_data).destroy
52
+ end
53
+ nil
38
54
  end
39
- nil
40
- end
41
55
 
42
56
 
43
- def self.destroy_all
44
- dynamo_items.select() do |item_data|
45
- new._setup_from_dynamo(item_data).destroy
57
+ def _late_connect? # :nodoc:
58
+ return false if table_connected
59
+ return false unless table_connect_policy
60
+ establish_db_connection
61
+ true
46
62
  end
47
- nil
63
+
48
64
  end
49
65
 
50
66
 
@@ -176,7 +192,6 @@ module OceanDynamo
176
192
 
177
193
 
178
194
  def reload(**keywords)
179
- raise "HELLISHNESS" if id == range_key
180
195
  new_instance = self.class.find(hash_key, range_key, **keywords)
181
196
  assign_attributes(new_instance.attributes)
182
197
  self
@@ -241,14 +256,6 @@ module OceanDynamo
241
256
 
242
257
  protected
243
258
 
244
- def self._late_connect? # :nodoc:
245
- return false if table_connected
246
- return false unless table_connect_policy
247
- establish_db_connection
248
- true
249
- end
250
-
251
-
252
259
  def _late_connect? # :nodoc:
253
260
  self.class._late_connect?
254
261
  end
@@ -1,10 +1,16 @@
1
1
  module OceanDynamo
2
- class Base
2
+ module Queries
3
3
 
4
- def self.find(hash, range=nil, consistent: false)
4
+ # ---------------------------------------------------------
5
+ #
6
+ # Class methods
7
+ #
8
+ # ---------------------------------------------------------
9
+
10
+ def find(hash, range=nil, consistent: false)
5
11
  return hash.collect {|elem| find elem, range, consistent: consistent } if hash.is_a?(Array)
6
12
  _late_connect?
7
- hash = hash.id if hash.kind_of?(Base) # TODO: We have (innocuous) leakage, fix!
13
+ hash = hash.id if hash.kind_of?(Table) # TODO: We have (innocuous) leakage, fix!
8
14
  item = dynamo_items[hash, range]
9
15
  unless item.exists?
10
16
  raise RecordNotFound, "can't find a #{self} with primary key ['#{hash}', #{range.inspect}]"
@@ -13,14 +19,14 @@ module OceanDynamo
13
19
  end
14
20
 
15
21
 
16
- def self.find_by_key(*args)
22
+ def find_by_key(*args)
17
23
  find(*args)
18
24
  rescue RecordNotFound
19
25
  nil
20
26
  end
21
27
 
22
28
 
23
- def self.find_by_id(*args)
29
+ def find_by_id(*args)
24
30
  find_by_key(*args)
25
31
  end
26
32
 
@@ -28,7 +34,7 @@ module OceanDynamo
28
34
  #
29
35
  # The number of records in the table.
30
36
  #
31
- def self.count(**options)
37
+ def count(**options)
32
38
  _late_connect?
33
39
  dynamo_items.count(options)
34
40
  end
@@ -37,7 +43,7 @@ module OceanDynamo
37
43
  #
38
44
  # Returns all records in the table.
39
45
  #
40
- def self.all(**options)
46
+ def all(**options)
41
47
  _late_connect?
42
48
  result = []
43
49
  dynamo_items.select(options) do |item_data|
@@ -54,7 +60,7 @@ module OceanDynamo
54
60
  # In that case, batch processing methods allow you to work with the records in batches,
55
61
  # thereby greatly reducing memory consumption.
56
62
  #
57
- def self.find_each(limit: nil, batch_size: 1000)
63
+ def find_each(limit: nil, batch_size: 1000)
58
64
  dynamo_items.select(limit: limit, batch_size: 1000) do |item_data|
59
65
  yield new._setup_from_dynamo(item_data)
60
66
  end
@@ -71,7 +77,7 @@ module OceanDynamo
71
77
  # #
72
78
  # # It’s not possible to set the order.
73
79
  # #
74
- # def self.find_in_batches(start: nil, batch_size: 1000)
80
+ # def find_in_batches(start: nil, batch_size: 1000)
75
81
  # []
76
82
  # end
77
83
  end
@@ -1,19 +1,24 @@
1
1
  module OceanDynamo
2
- class Base
2
+ module Schema
3
3
 
4
+ # ---------------------------------------------------------
5
+ #
6
+ # Class methods
7
+ #
8
+ # ---------------------------------------------------------
4
9
 
5
- def self.dynamo_schema(table_hash_key=:id,
6
- table_range_key=nil,
7
- table_name: compute_table_name,
8
- table_name_prefix: nil,
9
- table_name_suffix: nil,
10
- read_capacity_units: 10,
11
- write_capacity_units: 5,
12
- connect: :late,
13
- create: false,
14
- locking: :lock_version,
15
- timestamps: [:created_at, :updated_at],
16
- &block)
10
+ def dynamo_schema(table_hash_key=:id,
11
+ table_range_key=nil,
12
+ table_name: compute_table_name,
13
+ table_name_prefix: nil,
14
+ table_name_suffix: nil,
15
+ read_capacity_units: 10,
16
+ write_capacity_units: 5,
17
+ connect: :late,
18
+ create: false,
19
+ locking: :lock_version,
20
+ timestamps: [:created_at, :updated_at],
21
+ &block)
17
22
  # Set class vars
18
23
  self.dynamo_client = nil
19
24
  self.dynamo_table = nil
@@ -49,7 +54,7 @@ module OceanDynamo
49
54
  end
50
55
 
51
56
 
52
- def self.define_attribute_accessors(name)
57
+ def define_attribute_accessors(name)
53
58
  name = name.to_s
54
59
  self.class_eval "def #{name}; read_attribute('#{name}'); end"
55
60
  self.class_eval "def #{name}=(value); write_attribute('#{name}', value); end"
@@ -57,17 +62,17 @@ module OceanDynamo
57
62
  end
58
63
 
59
64
 
60
- def self.compute_table_name
65
+ def compute_table_name
61
66
  name.pluralize.underscore
62
67
  end
63
68
 
64
69
 
65
- def self.table_full_name
70
+ def table_full_name
66
71
  "#{table_name_prefix}#{table_name}#{table_name_suffix}"
67
72
  end
68
73
 
69
74
 
70
- def self.attribute(name, type=:string, default: nil, **extra)
75
+ def attribute(name, type=:string, default: nil, **extra)
71
76
  raise DangerousAttributeError, "#{name} is defined by OceanDynamo" if self.dangerous_attributes.include?(name.to_s)
72
77
  attr_accessor name
73
78
  fields[name.to_s] = {type: type, default: default}.merge(extra)
@@ -76,7 +81,7 @@ module OceanDynamo
76
81
 
77
82
  protected
78
83
 
79
- def self.dangerous_attributes # :nodoc:
84
+ def dangerous_attributes # :nodoc:
80
85
  self.public_methods(false).collect do |sym|
81
86
  str = sym.to_s
82
87
  if str.end_with?("?", "=")
@@ -1,7 +1,13 @@
1
1
  module OceanDynamo
2
- class Base
2
+ module Tables
3
3
 
4
- def self.establish_db_connection
4
+ # ---------------------------------------------------------
5
+ #
6
+ # Class methods
7
+ #
8
+ # ---------------------------------------------------------
9
+
10
+ def establish_db_connection
5
11
  setup_dynamo
6
12
  if dynamo_table.exists?
7
13
  wait_until_table_is_active
@@ -14,7 +20,7 @@ module OceanDynamo
14
20
  end
15
21
 
16
22
 
17
- def self.setup_dynamo
23
+ def setup_dynamo
18
24
  #self.dynamo_client = AWS::DynamoDB::Client.new(:api_version => '2012-08-10')
19
25
  self.dynamo_client ||= AWS::DynamoDB.new
20
26
  self.dynamo_table = dynamo_client.tables[table_full_name]
@@ -22,7 +28,7 @@ module OceanDynamo
22
28
  end
23
29
 
24
30
 
25
- def self.wait_until_table_is_active
31
+ def wait_until_table_is_active
26
32
  loop do
27
33
  case dynamo_table.status
28
34
  when :active
@@ -42,25 +48,23 @@ module OceanDynamo
42
48
  end
43
49
 
44
50
 
45
- def self.set_dynamo_table_keys
51
+ def set_dynamo_table_keys
46
52
  hash_key_type = fields[table_hash_key][:type]
47
53
  hash_key_type = :string if hash_key_type == :reference
48
54
  dynamo_table.hash_key = [table_hash_key, hash_key_type]
49
55
 
50
56
  if table_range_key
51
57
  range_key_type = fields[table_range_key][:type]
52
- #range_key_type = :string if range_key_type == :reference
53
58
  dynamo_table.range_key = [table_range_key, range_key_type]
54
59
  end
55
60
  end
56
61
 
57
62
 
58
- def self.create_table
63
+ def create_table
59
64
  hash_key_type = fields[table_hash_key][:type]
60
65
  hash_key_type = :string if hash_key_type == :reference
61
66
 
62
67
  range_key_type = table_range_key && fields[table_range_key][:type]
63
- #range_key_type = :string if range_key_type == :reference
64
68
 
65
69
  self.dynamo_table = dynamo_client.tables.create(table_full_name,
66
70
  table_read_capacity_units, table_write_capacity_units,
@@ -73,7 +77,7 @@ module OceanDynamo
73
77
  end
74
78
 
75
79
 
76
- def self.delete_table
80
+ def delete_table
77
81
  return false unless dynamo_table.exists? && dynamo_table.status == :active
78
82
  dynamo_table.delete
79
83
  true
@@ -1,3 +1,3 @@
1
1
  module OceanDynamo
2
- VERSION = "0.3.13"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ocean-dynamo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.13
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Bengtson
@@ -160,11 +160,12 @@ extensions: []
160
160
  extra_rdoc_files: []
161
161
  files:
162
162
  - config/routes.rb
163
+ - lib/ocean-dynamo/associations/associations.rb
163
164
  - lib/ocean-dynamo/associations/belongs_to.rb
164
165
  - lib/ocean-dynamo/associations/collection_proxy.rb
166
+ - lib/ocean-dynamo/associations/has_many.rb
165
167
  - lib/ocean-dynamo/attributes.rb
166
- - lib/ocean-dynamo/base.rb
167
- - lib/ocean-dynamo/callbacks.rb
168
+ - lib/ocean-dynamo/basal.rb
168
169
  - lib/ocean-dynamo/class_variables.rb
169
170
  - lib/ocean-dynamo/engine.rb
170
171
  - lib/ocean-dynamo/exceptions.rb
@@ -1,38 +0,0 @@
1
- module OceanDynamo
2
- class Base
3
-
4
- include ActiveModel::Model
5
- include ActiveModel::Validations::Callbacks
6
-
7
-
8
- def ==(comparison_object)
9
- super ||
10
- comparison_object.instance_of?(self.class) &&
11
- id.present? &&
12
- comparison_object.id == id
13
- end
14
- alias :eql? :==
15
-
16
-
17
- # Clone and freeze the attributes hash such that associations are still
18
- # accessible, even on destroyed records, but cloned models will not be
19
- # frozen.
20
- def freeze
21
- @attributes = @attributes.clone.freeze
22
- self
23
- end
24
-
25
- # Returns +true+ if the attributes hash has been frozen.
26
- def frozen?
27
- @attributes.frozen?
28
- end
29
-
30
- # Allows sort on objects
31
- def <=>(other_object)
32
- if other_object.is_a?(self.class)
33
- self.to_key <=> other_object.to_key
34
- end
35
- end
36
-
37
- end
38
- end
@@ -1,15 +0,0 @@
1
- module OceanDynamo
2
- class Base
3
-
4
- include ActiveModel::Validations::Callbacks
5
-
6
- define_model_callbacks :initialize, only: :after
7
- define_model_callbacks :save
8
- define_model_callbacks :create
9
- define_model_callbacks :update
10
- define_model_callbacks :destroy
11
- define_model_callbacks :commit, only: :after
12
- define_model_callbacks :touch
13
-
14
- end
15
- end