ocean-dynamo 0.3.13 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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