ocean-dynamo 0.4.0 → 0.4.1
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 +20 -10
- data/lib/ocean-dynamo/associations/associations.rb +11 -0
- data/lib/ocean-dynamo/associations/belongs_to.rb +14 -5
- data/lib/ocean-dynamo/associations/has_many.rb +64 -12
- data/lib/ocean-dynamo/attributes.rb +46 -24
- data/lib/ocean-dynamo/basal.rb +39 -25
- data/lib/ocean-dynamo/persistence.rb +12 -0
- data/lib/ocean-dynamo/schema.rb +2 -42
- data/lib/ocean-dynamo/tables.rb +106 -56
- data/lib/ocean-dynamo/version.rb +1 -1
- data/lib/ocean-dynamo.rb +7 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: d067b77a882722c6a949672647c910338812ccf7
         | 
| 4 | 
            +
              data.tar.gz: 48f95744f784e8cef31a65fe496c9d69737521f8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 8e46bbfcce5105eef9737075b4042c049472ecb06bb7b938503d920a176083ffbeb0c281a4eba79698d34de7e3bec5c8483a4f8a7ca4f6dd9ee1df880fea24f3
         | 
| 7 | 
            +
              data.tar.gz: c519f71fea7afc08ebfe2496c13cec6f3292ada38e51dc15893396005ae70ec29694d0dcfd9702a92a36b99f6c4e614f2b14a7a90b1d639b60ee874bc0f26a3c
         | 
    
        data/README.rdoc
    CHANGED
    
    | @@ -91,11 +91,11 @@ value will return the empty string, <tt>""</tt>. | |
| 91 91 | 
             
               ...
         | 
| 92 92 | 
             
             end
         | 
| 93 93 |  | 
| 94 | 
            -
            === +belongs_to+
         | 
| 94 | 
            +
            === +has_many+ and +belongs_to+
         | 
| 95 95 |  | 
| 96 96 | 
             
            ==== Example
         | 
| 97 97 |  | 
| 98 | 
            -
            The following example shows how to set up a +belongs_to+ relation:
         | 
| 98 | 
            +
            The following example shows how to set up a +has_many+ / +belongs_to+ relation:
         | 
| 99 99 |  | 
| 100 100 | 
             
             class Parent < OceanDynamo::Table
         | 
| 101 101 | 
             
               dynamo_schema do
         | 
| @@ -103,7 +103,7 @@ The following example shows how to set up a +belongs_to+ relation: | |
| 103 103 | 
             
                 attribute :that
         | 
| 104 104 | 
             
                 attribute :the_other
         | 
| 105 105 | 
             
               end
         | 
| 106 | 
            -
                | 
| 106 | 
            +
               has_many :children
         | 
| 107 107 | 
             
             end
         | 
| 108 108 |  | 
| 109 109 |  | 
| @@ -124,6 +124,9 @@ Restrictions for +belongs_to+ tables: | |
| 124 124 | 
             
            * +belongs_to+ can be specified only once in each class.
         | 
| 125 125 | 
             
            * +belongs_to+ must be placed after the +dynamo_schema+ attribute block.
         | 
| 126 126 |  | 
| 127 | 
            +
            Restrictions for +has_many+ tables:
         | 
| 128 | 
            +
            * The +has_many+ table may not have a range key.
         | 
| 129 | 
            +
             | 
| 127 130 | 
             
            These restrictions allow OceanDynamo to implement the +has_many+ / +belongs_to+ 
         | 
| 128 131 | 
             
            relation in a very efficient and massively scalable way. 
         | 
| 129 132 |  | 
| @@ -157,18 +160,25 @@ controllers. OceanDynamo implements much of the infrastructure of ActiveRecord; | |
| 157 160 | 
             
            for instance, +read_attribute+, +write_attribute+, and much of the control logic and
         | 
| 158 161 | 
             
            parameters.
         | 
| 159 162 |  | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 163 | 
            +
            * has_many now works, in a minimalistic fashion. To implement the rest of the interface,
         | 
| 164 | 
            +
              we need association proxies.
         | 
| 165 | 
            +
             | 
| 166 | 
            +
            * Instances of classes with one or more has_many relations now save all relations after
         | 
| 167 | 
            +
              the instance has been saved. To save only dirty association collections, we need
         | 
| 168 | 
            +
              association proxies. Collections which are nil are not saved. Collections which are []
         | 
| 169 | 
            +
              are saved, which means they clear all children. This difference is crucial, until we
         | 
| 170 | 
            +
              get association proxies.
         | 
| 171 | 
            +
             | 
| 172 | 
            +
            * #reload clears all relations.
         | 
| 162 173 |  | 
| 163 174 | 
             
            === Future milestones
         | 
| 164 175 |  | 
| 165 | 
            -
            *  | 
| 166 | 
            -
               | 
| 176 | 
            +
            * Association proxies, to implement the same kind of interace as ActiveRecord, e.g.: 
         | 
| 177 | 
            +
              <code>blog_entry.comments.build(body: "Cool!").save!</code>
         | 
| 167 178 |  | 
| 168 | 
            -
            *  | 
| 179 | 
            +
            * The +has_and_belongs_to_many+ assocation. 
         | 
| 169 180 |  | 
| 170 | 
            -
            *  | 
| 171 | 
            -
              interface as ActiveRecord, e.g.: <code>blog_entry.comments.build(body: "Cool!").save!</code>
         | 
| 181 | 
            +
            * A generator to install the <tt>config/aws.yml</tt> file.
         | 
| 172 182 |  | 
| 173 183 |  | 
| 174 184 | 
             
            === Current use
         | 
| @@ -116,17 +116,26 @@ module OceanDynamo | |
| 116 116 | 
             
                #
         | 
| 117 117 | 
             
                # ---------------------------------------------------------
         | 
| 118 118 |  | 
| 119 | 
            +
                def initialize(attrs={})
         | 
| 120 | 
            +
                  super
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
             | 
| 119 124 | 
             
                protected
         | 
| 120 125 |  | 
| 121 126 | 
             
                #
         | 
| 122 127 | 
             
                # This is run by #initialize and by #assign_attributes to set the
         | 
| 123 128 | 
             
                # association variables (@master, for instance) and its associated attribute
         | 
| 124 | 
            -
                # (such as master_id) to the value given.
         | 
| 129 | 
            +
                # (such as master_id) to the value given. This is a hack, do away with
         | 
| 130 | 
            +
                # the double storage when association proxies are introduced.
         | 
| 125 131 | 
             
                #
         | 
| 126 | 
            -
                def  | 
| 127 | 
            -
                   | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 132 | 
            +
                def set_belongs_to_association(attrs)  # :nodoc:
         | 
| 133 | 
            +
                  parent_class = self.class.belongs_to_class
         | 
| 134 | 
            +
                  return unless parent_class
         | 
| 135 | 
            +
                  parent_class = parent_class.to_s.underscore.to_sym
         | 
| 136 | 
            +
                  if attrs && attrs.include?(parent_class)
         | 
| 137 | 
            +
                    instance_variable_set("@#{parent_class}", attrs[parent_class])
         | 
| 138 | 
            +
                    write_attribute("#{parent_class}_id", attrs[parent_class])
         | 
| 130 139 | 
             
                  end
         | 
| 131 140 | 
             
                end
         | 
| 132 141 |  | 
| @@ -14,17 +14,25 @@ module OceanDynamo | |
| 14 14 |  | 
| 15 15 | 
             
                module ClassMethods
         | 
| 16 16 |  | 
| 17 | 
            +
             | 
| 17 18 | 
             
                  #
         | 
| 18 | 
            -
                  #
         | 
| 19 | 
            +
                  # Defines a has_many relation to a belongs_to class.
         | 
| 19 20 | 
             
                  #
         | 
| 20 21 | 
             
                  def has_many(children)                                         # :children
         | 
| 21 22 | 
             
                    children_attr = children.to_s.underscore                     # "children"
         | 
| 22 23 | 
             
                    child_class = children_attr.singularize.camelize.constantize # Child
         | 
| 23 24 | 
             
                    register_relation(child_class, :has_many)
         | 
| 24 25 | 
             
                    # Define accessors for instances
         | 
| 25 | 
            -
                    self.class_eval "def #{children_attr} | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 26 | 
            +
                    self.class_eval "def #{children_attr} 
         | 
| 27 | 
            +
                                       @#{children_attr} ||= read_children(#{child_class})
         | 
| 28 | 
            +
                                     end"
         | 
| 29 | 
            +
                    self.class_eval "def #{children_attr}=(value)
         | 
| 30 | 
            +
                                       @#{children_attr} = value
         | 
| 31 | 
            +
                                     end"
         | 
| 32 | 
            +
                    self.class_eval "def #{children_attr}? 
         | 
| 33 | 
            +
                                       @#{children_attr} ||= read_children(#{child_class})
         | 
| 34 | 
            +
                                       @#{children_attr}.present?
         | 
| 35 | 
            +
                                     end"
         | 
| 28 36 | 
             
                  end
         | 
| 29 37 |  | 
| 30 38 | 
             
                end
         | 
| @@ -36,8 +44,9 @@ module OceanDynamo | |
| 36 44 | 
             
                #
         | 
| 37 45 | 
             
                # ---------------------------------------------------------
         | 
| 38 46 |  | 
| 47 | 
            +
             | 
| 39 48 | 
             
                #
         | 
| 40 | 
            -
                #
         | 
| 49 | 
            +
                # Reads all children of a has_many relation.
         | 
| 41 50 | 
             
                #
         | 
| 42 51 | 
             
                def read_children(child_class)
         | 
| 43 52 | 
             
                  if new_record? 
         | 
| @@ -53,16 +62,59 @@ module OceanDynamo | |
| 53 62 | 
             
                  end
         | 
| 54 63 | 
             
                end
         | 
| 55 64 |  | 
| 65 | 
            +
             | 
| 66 | 
            +
                #
         | 
| 67 | 
            +
                # Write all children in the arg, which should be nil or an array.
         | 
| 68 | 
            +
                #
         | 
| 69 | 
            +
                def write_children(child_class, arg)
         | 
| 70 | 
            +
                  return nil if arg.blank?
         | 
| 71 | 
            +
                  raise AssociationTypeMismatch, "not an array or nil" if !arg.is_a?(Array)
         | 
| 72 | 
            +
                  raise AssociationTypeMismatch, "an array element is not a #{child_class}" unless arg.all? { |m| m.is_a?(child_class) }
         | 
| 73 | 
            +
                  # We now know that arg is an array containing only members of the child_class
         | 
| 74 | 
            +
                  arg.each(&:save!)
         | 
| 75 | 
            +
                  arg
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
             | 
| 79 | 
            +
                #
         | 
| 80 | 
            +
                # Sets all has_many relations to nil.
         | 
| 56 81 | 
             
                #
         | 
| 82 | 
            +
                def reload(*)
         | 
| 83 | 
            +
                  result = super
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  self.class.relations_of_type(:has_many).each do |klass|
         | 
| 86 | 
            +
                    attr_name = klass.to_s.pluralize.underscore
         | 
| 87 | 
            +
                    instance_variable_set("@#{attr_name}", nil)
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  result
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
             | 
| 94 | 
            +
                protected
         | 
| 95 | 
            +
             | 
| 57 96 | 
             
                #
         | 
| 97 | 
            +
                # This version also writes back any relations. TODO: only
         | 
| 98 | 
            +
                # dirty relations should be persisted. Introduce a dirty flag.
         | 
| 99 | 
            +
                # Easiest done via an association proxy object.
         | 
| 58 100 | 
             
                #
         | 
| 59 | 
            -
                def  | 
| 60 | 
            -
                   | 
| 61 | 
            -
             | 
| 62 | 
            -
                   | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 101 | 
            +
                def dynamo_persist(*)  # :nodoc:
         | 
| 102 | 
            +
                  result = super
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  self.class.relations_of_type(:has_many).each do |klass|
         | 
| 105 | 
            +
                    attr_name = klass.to_s.pluralize.underscore
         | 
| 106 | 
            +
                    # First create or update the children in the new set
         | 
| 107 | 
            +
                    new_children = instance_variable_get("@#{attr_name}")
         | 
| 108 | 
            +
                    next unless new_children
         | 
| 109 | 
            +
                    write_children klass, new_children
         | 
| 110 | 
            +
                    # Destroy all children not in the new set (this is not yet scalable)
         | 
| 111 | 
            +
                    read_children(klass).each do |c|
         | 
| 112 | 
            +
                      next if new_children.include?(c)
         | 
| 113 | 
            +
                      c.delete
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  result
         | 
| 66 118 | 
             
                end
         | 
| 67 119 |  | 
| 68 120 | 
             
              end
         | 
| @@ -13,6 +13,31 @@ module OceanDynamo | |
| 13 13 | 
             
                # ---------------------------------------------------------
         | 
| 14 14 |  | 
| 15 15 | 
             
                module ClassMethods
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def dynamo_schema(table_hash_key=:id, 
         | 
| 18 | 
            +
                                    table_range_key=nil,
         | 
| 19 | 
            +
                                    locking: :lock_version,
         | 
| 20 | 
            +
                                    timestamps: [:created_at, :updated_at],
         | 
| 21 | 
            +
                                    **keywords,
         | 
| 22 | 
            +
                                    &block)
         | 
| 23 | 
            +
                    self.lock_attribute = locking
         | 
| 24 | 
            +
                    self.timestamp_attributes = timestamps
         | 
| 25 | 
            +
                    # Init
         | 
| 26 | 
            +
                    self.fields = HashWithIndifferentAccess.new
         | 
| 27 | 
            +
                    attribute(table_hash_key, :string, default: "")
         | 
| 28 | 
            +
                    if table_range_key
         | 
| 29 | 
            +
                      attribute(table_range_key, :string, default: "")
         | 
| 30 | 
            +
                      self.validates(table_range_key, presence: true)
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                    timestamp_attributes.each { |name| attribute name, :datetime } if timestamp_attributes
         | 
| 33 | 
            +
                    attribute(lock_attribute, :integer, default: 0) if locking
         | 
| 34 | 
            +
                    block.call
         | 
| 35 | 
            +
                    # Define attribute accessors
         | 
| 36 | 
            +
                    fields.each { |name, md| define_attribute_accessors(name) }
         | 
| 37 | 
            +
                    # Return table name
         | 
| 38 | 
            +
                    super
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 16 41 | 
             
                end
         | 
| 17 42 |  | 
| 18 43 |  | 
| @@ -32,6 +57,19 @@ module OceanDynamo | |
| 32 57 | 
             
                attr_reader :dynamo_item    # :nodoc:
         | 
| 33 58 |  | 
| 34 59 |  | 
| 60 | 
            +
                def initialize(attrs={})
         | 
| 61 | 
            +
                  @attributes = Hash.new
         | 
| 62 | 
            +
                  fields.each do |name, md| 
         | 
| 63 | 
            +
                    write_attribute(name, evaluate_default(md[:default], md[:type]))
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                  raise UnknownPrimaryKey unless table_hash_key
         | 
| 66 | 
            +
                  set_belongs_to_association(attrs)
         | 
| 67 | 
            +
                  # Barf on unknown attributes here?
         | 
| 68 | 
            +
                  attrs && attrs.delete_if { |k, v| !fields.has_key?(k) }
         | 
| 69 | 
            +
                  super(attrs)
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
             | 
| 35 73 | 
             
                #
         | 
| 36 74 | 
             
                # Returns the value of the hash key attribute
         | 
| 37 75 | 
             
                #
         | 
| @@ -49,23 +87,6 @@ module OceanDynamo | |
| 49 87 | 
             
                end
         | 
| 50 88 |  | 
| 51 89 |  | 
| 52 | 
            -
                def initialize(attrs={})
         | 
| 53 | 
            -
                  run_callbacks :initialize do
         | 
| 54 | 
            -
                    @attributes = Hash.new
         | 
| 55 | 
            -
                    fields.each do |name, md| 
         | 
| 56 | 
            -
                      write_attribute(name, evaluate_default(md[:default], md[:type]))
         | 
| 57 | 
            -
                    end
         | 
| 58 | 
            -
                    @dynamo_item = nil
         | 
| 59 | 
            -
                    @destroyed = false
         | 
| 60 | 
            -
                    @new_record = true
         | 
| 61 | 
            -
                    raise UnknownPrimaryKey unless table_hash_key
         | 
| 62 | 
            -
                  end
         | 
| 63 | 
            -
                  assign_associations(attrs)
         | 
| 64 | 
            -
                  attrs && attrs.delete_if { |k, v| !fields.has_key?(k) }
         | 
| 65 | 
            -
                  super(attrs)
         | 
| 66 | 
            -
                end
         | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 90 | 
             
                def [](attribute)
         | 
| 70 91 | 
             
                  read_attribute attribute
         | 
| 71 92 | 
             
                end
         | 
| @@ -77,7 +98,7 @@ module OceanDynamo | |
| 77 98 |  | 
| 78 99 |  | 
| 79 100 | 
             
                def id
         | 
| 80 | 
            -
             | 
| 101 | 
            +
                  hash_key
         | 
| 81 102 | 
             
                end
         | 
| 82 103 |  | 
| 83 104 |  | 
| @@ -85,8 +106,9 @@ module OceanDynamo | |
| 85 106 | 
             
                  write_attribute(table_hash_key, value)
         | 
| 86 107 | 
             
                end
         | 
| 87 108 |  | 
| 109 | 
            +
             | 
| 88 110 | 
             
                def id?
         | 
| 89 | 
            -
                   | 
| 111 | 
            +
                  hash_key.present?
         | 
| 90 112 | 
             
                end
         | 
| 91 113 |  | 
| 92 114 |  | 
| @@ -101,7 +123,7 @@ module OceanDynamo | |
| 101 123 | 
             
                  if fields.has_key?(attr_name)
         | 
| 102 124 | 
             
                    @attributes[attr_name]
         | 
| 103 125 | 
             
                  else
         | 
| 104 | 
            -
                    raise ActiveModel::MissingAttributeError, "can't read unknown attribute  | 
| 126 | 
            +
                    raise ActiveModel::MissingAttributeError, "can't read unknown attribute '#{attr_ name}"
         | 
| 105 127 | 
             
                  end
         | 
| 106 128 | 
             
                end
         | 
| 107 129 |  | 
| @@ -112,7 +134,7 @@ module OceanDynamo | |
| 112 134 | 
             
                  if fields.has_key?(attr_name)
         | 
| 113 135 | 
             
                    @attributes[attr_name] = type_cast_attribute_for_write(attr_name, value)
         | 
| 114 136 | 
             
                  else
         | 
| 115 | 
            -
                    raise ActiveModel::MissingAttributeError, "can't write unknown attribute  | 
| 137 | 
            +
                    raise ActiveModel::MissingAttributeError, "can't write unknown attribute '#{attr_name}'"
         | 
| 116 138 | 
             
                  end
         | 
| 117 139 | 
             
                end
         | 
| 118 140 |  | 
| @@ -121,14 +143,14 @@ module OceanDynamo | |
| 121 143 | 
             
                  return nil unless persisted?
         | 
| 122 144 | 
             
                  key = respond_to?(:id) && id
         | 
| 123 145 | 
             
                  return nil unless key
         | 
| 124 | 
            -
                  table_range_key ? [key,  | 
| 146 | 
            +
                  table_range_key ? [key, range_key] : [key]
         | 
| 125 147 | 
             
                end
         | 
| 126 148 |  | 
| 127 149 |  | 
| 128 150 | 
             
                def assign_attributes(values, without_protection: false)
         | 
| 129 151 | 
             
                  return if values.blank?
         | 
| 130 152 | 
             
                  values = values.stringify_keys
         | 
| 131 | 
            -
                   | 
| 153 | 
            +
                  set_belongs_to_association(values)
         | 
| 132 154 | 
             
                  # if values.respond_to?(:permitted?)
         | 
| 133 155 | 
             
                  #   unless values.permitted?
         | 
| 134 156 | 
             
                  #     raise ActiveModel::ForbiddenAttributesError
         | 
| @@ -180,7 +202,7 @@ module OceanDynamo | |
| 180 202 | 
             
                  if respond_to?("#{k}=")
         | 
| 181 203 | 
             
                    raise
         | 
| 182 204 | 
             
                  else
         | 
| 183 | 
            -
                    raise UnknownAttributeError, "unknown attribute:  | 
| 205 | 
            +
                    raise UnknownAttributeError, "unknown attribute: '#{k}'"
         | 
| 184 206 | 
             
                  end
         | 
| 185 207 | 
             
                end
         | 
| 186 208 |  | 
    
        data/lib/ocean-dynamo/basal.rb
    CHANGED
    
    | @@ -1,32 +1,46 @@ | |
| 1 | 
            -
            module  | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
                 | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 1 | 
            +
            module OceanDynamo
         | 
| 2 | 
            +
              module Basal
         | 
| 3 | 
            +
                  
         | 
| 4 | 
            +
                #
         | 
| 5 | 
            +
                # The equality comparator. 
         | 
| 6 | 
            +
                #
         | 
| 7 | 
            +
                def ==(comparison_object)
         | 
| 8 | 
            +
                  super ||
         | 
| 9 | 
            +
                    comparison_object.instance_of?(self.class) &&
         | 
| 10 | 
            +
                    hash_key.present? &&
         | 
| 11 | 
            +
                    comparison_object.hash_key == hash_key &&
         | 
| 12 | 
            +
                    (range_key == comparison_object.range_key)
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
                alias :eql? :==
         | 
| 10 15 |  | 
| 11 16 |  | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                # Allows sort on instances
         | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                def <=>(other_object)
         | 
| 21 | 
            +
                  if other_object.is_a?(self.class)
         | 
| 22 | 
            +
                    self.to_key <=> other_object.to_key
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 19 25 |  | 
| 20 | 
            -
              # Returns +true+ if the attributes hash has been frozen.
         | 
| 21 | 
            -
              def frozen?
         | 
| 22 | 
            -
                @attributes.frozen?
         | 
| 23 | 
            -
              end
         | 
| 24 26 |  | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
                 | 
| 28 | 
            -
             | 
| 27 | 
            +
                #
         | 
| 28 | 
            +
                # Clone and freeze the attributes hash such that associations are still
         | 
| 29 | 
            +
                # accessible, even on destroyed records, but cloned models will not be
         | 
| 30 | 
            +
                # frozen.
         | 
| 31 | 
            +
                #
         | 
| 32 | 
            +
                def freeze
         | 
| 33 | 
            +
                  @attributes = @attributes.clone.freeze
         | 
| 34 | 
            +
                  self
         | 
| 29 35 | 
             
                end
         | 
| 30 | 
            -
              end
         | 
| 31 36 |  | 
| 37 | 
            +
             | 
| 38 | 
            +
                #
         | 
| 39 | 
            +
                # Returns +true+ if the attributes hash has been frozen.
         | 
| 40 | 
            +
                #
         | 
| 41 | 
            +
                def frozen?
         | 
| 42 | 
            +
                  @attributes.frozen?
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              end
         | 
| 32 46 | 
             
            end
         | 
| @@ -70,6 +70,18 @@ module OceanDynamo | |
| 70 70 | 
             
                #
         | 
| 71 71 | 
             
                # ---------------------------------------------------------
         | 
| 72 72 |  | 
| 73 | 
            +
                def initialize(attrs={})
         | 
| 74 | 
            +
                  @destroyed = false
         | 
| 75 | 
            +
                  @new_record = true
         | 
| 76 | 
            +
                  super
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
             | 
| 80 | 
            +
                def dynamo_schema(*)
         | 
| 81 | 
            +
                  super
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
             | 
| 73 85 | 
             
                def destroyed?
         | 
| 74 86 | 
             
                  @destroyed
         | 
| 75 87 | 
             
                end
         | 
    
        data/lib/ocean-dynamo/schema.rb
    CHANGED
    
    | @@ -7,48 +7,8 @@ module OceanDynamo | |
| 7 7 | 
             
                #
         | 
| 8 8 | 
             
                # ---------------------------------------------------------
         | 
| 9 9 |  | 
| 10 | 
            -
                def dynamo_schema( | 
| 11 | 
            -
             | 
| 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)
         | 
| 22 | 
            -
                  # Set class vars
         | 
| 23 | 
            -
                  self.dynamo_client = nil
         | 
| 24 | 
            -
                  self.dynamo_table = nil
         | 
| 25 | 
            -
                  self.dynamo_items = nil
         | 
| 26 | 
            -
                  self.table_connected = false
         | 
| 27 | 
            -
                  self.table_connect_policy = connect
         | 
| 28 | 
            -
                  self.table_create_policy = create
         | 
| 29 | 
            -
                  self.table_hash_key = table_hash_key
         | 
| 30 | 
            -
                  self.table_range_key = table_range_key
         | 
| 31 | 
            -
                  self.table_name = table_name
         | 
| 32 | 
            -
                  self.table_name_prefix = table_name_prefix
         | 
| 33 | 
            -
                  self.table_name_suffix = table_name_suffix
         | 
| 34 | 
            -
                  self.table_read_capacity_units = read_capacity_units
         | 
| 35 | 
            -
                  self.table_write_capacity_units = write_capacity_units
         | 
| 36 | 
            -
                  self.lock_attribute = locking
         | 
| 37 | 
            -
                  self.timestamp_attributes = timestamps
         | 
| 38 | 
            -
                  # Init
         | 
| 39 | 
            -
                  self.fields = HashWithIndifferentAccess.new
         | 
| 40 | 
            -
                  attribute(table_hash_key, :string, default: "")
         | 
| 41 | 
            -
                  if table_range_key
         | 
| 42 | 
            -
                    attribute(table_range_key, :string, default: "")
         | 
| 43 | 
            -
                    self.validates(table_range_key, presence: true)
         | 
| 44 | 
            -
                  end
         | 
| 45 | 
            -
                  timestamp_attributes.each { |name| attribute name, :datetime } if timestamp_attributes
         | 
| 46 | 
            -
                  attribute(lock_attribute, :integer, default: 0) if locking
         | 
| 47 | 
            -
                  block.call
         | 
| 48 | 
            -
                  # Define attribute accessors
         | 
| 49 | 
            -
                  fields.each { |name, md| define_attribute_accessors(name) }
         | 
| 50 | 
            -
                  # Connect to AWS
         | 
| 51 | 
            -
                  establish_db_connection if connect == true
         | 
| 10 | 
            +
                def dynamo_schema(*)
         | 
| 11 | 
            +
                  super
         | 
| 52 12 | 
             
                  # Finally return the full table name
         | 
| 53 13 | 
             
                  table_full_name
         | 
| 54 14 | 
             
                end
         | 
    
        data/lib/ocean-dynamo/tables.rb
    CHANGED
    
    | @@ -1,86 +1,136 @@ | |
| 1 1 | 
             
            module OceanDynamo
         | 
| 2 2 | 
             
              module Tables
         | 
| 3 3 |  | 
| 4 | 
            +
                def self.included(base)
         | 
| 5 | 
            +
                  base.extend(ClassMethods)
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
              
         | 
| 8 | 
            +
             | 
| 4 9 | 
             
                # ---------------------------------------------------------
         | 
| 5 10 | 
             
                #
         | 
| 6 11 | 
             
                #  Class methods
         | 
| 7 12 | 
             
                #
         | 
| 8 13 | 
             
                # ---------------------------------------------------------
         | 
| 9 14 |  | 
| 10 | 
            -
                 | 
| 11 | 
            -
             | 
| 12 | 
            -
                   | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 15 | 
            +
                module ClassMethods
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  def dynamo_schema(table_hash_key=:id, 
         | 
| 18 | 
            +
                                    table_range_key=nil,
         | 
| 19 | 
            +
                                    table_name: compute_table_name,
         | 
| 20 | 
            +
                                    table_name_prefix: nil,
         | 
| 21 | 
            +
                                    table_name_suffix: nil,
         | 
| 22 | 
            +
                                    read_capacity_units: 10,
         | 
| 23 | 
            +
                                    write_capacity_units: 5,
         | 
| 24 | 
            +
                                    connect: :late,
         | 
| 25 | 
            +
                                    create: false,
         | 
| 26 | 
            +
                                    **keywords,
         | 
| 27 | 
            +
                                    &block)
         | 
| 28 | 
            +
                    self.dynamo_client = nil
         | 
| 29 | 
            +
                    self.dynamo_table = nil
         | 
| 30 | 
            +
                    self.dynamo_items = nil
         | 
| 31 | 
            +
                    self.table_connected = false
         | 
| 32 | 
            +
                    self.table_connect_policy = connect
         | 
| 33 | 
            +
                    self.table_create_policy = create
         | 
| 34 | 
            +
                    self.table_hash_key = table_hash_key
         | 
| 35 | 
            +
                    self.table_range_key = table_range_key
         | 
| 36 | 
            +
                    self.table_name = table_name
         | 
| 37 | 
            +
                    self.table_name_prefix = table_name_prefix
         | 
| 38 | 
            +
                    self.table_name_suffix = table_name_suffix
         | 
| 39 | 
            +
                    self.table_read_capacity_units = read_capacity_units
         | 
| 40 | 
            +
                    self.table_write_capacity_units = write_capacity_units
         | 
| 41 | 
            +
                    # Connect if asked to
         | 
| 42 | 
            +
                    establish_db_connection if connect == true
         | 
| 18 43 | 
             
                  end
         | 
| 19 | 
            -
                  set_dynamo_table_keys
         | 
| 20 | 
            -
                end
         | 
| 21 44 |  | 
| 22 45 |  | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 46 | 
            +
                  def establish_db_connection
         | 
| 47 | 
            +
                    setup_dynamo  
         | 
| 48 | 
            +
                    if dynamo_table.exists?
         | 
| 49 | 
            +
                      wait_until_table_is_active
         | 
| 50 | 
            +
                      self.table_connected = true
         | 
| 51 | 
            +
                    else
         | 
| 52 | 
            +
                      raise(TableNotFound, table_full_name) unless table_create_policy
         | 
| 53 | 
            +
                      create_table
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                    set_dynamo_table_keys
         | 
| 56 | 
            +
                  end
         | 
| 29 57 |  | 
| 30 58 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
                     | 
| 34 | 
            -
                     | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
                     | 
| 41 | 
            -
                       | 
| 42 | 
            -
                       | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
                       | 
| 59 | 
            +
                  def setup_dynamo
         | 
| 60 | 
            +
                    #self.dynamo_client = AWS::DynamoDB::Client.new(:api_version => '2012-08-10') 
         | 
| 61 | 
            +
                    self.dynamo_client ||= AWS::DynamoDB.new
         | 
| 62 | 
            +
                    self.dynamo_table = dynamo_client.tables[table_full_name]
         | 
| 63 | 
            +
                    self.dynamo_items = dynamo_table.items
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
             | 
| 67 | 
            +
                  def wait_until_table_is_active
         | 
| 68 | 
            +
                    loop do
         | 
| 69 | 
            +
                      case dynamo_table.status
         | 
| 70 | 
            +
                      when :active
         | 
| 71 | 
            +
                        set_dynamo_table_keys
         | 
| 72 | 
            +
                        return
         | 
| 73 | 
            +
                      when :updating, :creating
         | 
| 74 | 
            +
                        sleep 1
         | 
| 75 | 
            +
                        next
         | 
| 76 | 
            +
                      when :deleting
         | 
| 77 | 
            +
                        sleep 1 while dynamo_table.exists?
         | 
| 78 | 
            +
                        create_table
         | 
| 79 | 
            +
                        return
         | 
| 80 | 
            +
                      else
         | 
| 81 | 
            +
                        raise UnknownTableStatus.new("Unknown DynamoDB table status '#{dynamo_table.status}'")
         | 
| 82 | 
            +
                      end
         | 
| 46 83 | 
             
                    end
         | 
| 47 84 | 
             
                  end
         | 
| 48 | 
            -
                end
         | 
| 49 85 |  | 
| 50 86 |  | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 87 | 
            +
                  def set_dynamo_table_keys
         | 
| 88 | 
            +
                    hash_key_type = fields[table_hash_key][:type]
         | 
| 89 | 
            +
                    hash_key_type = :string if hash_key_type == :reference
         | 
| 90 | 
            +
                    dynamo_table.hash_key = [table_hash_key, hash_key_type]
         | 
| 55 91 |  | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 92 | 
            +
                    if table_range_key
         | 
| 93 | 
            +
                      range_key_type = fields[table_range_key][:type]
         | 
| 94 | 
            +
                      dynamo_table.range_key = [table_range_key, range_key_type]
         | 
| 95 | 
            +
                    end
         | 
| 59 96 | 
             
                  end
         | 
| 60 | 
            -
                end
         | 
| 61 97 |  | 
| 62 98 |  | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 99 | 
            +
                  def create_table
         | 
| 100 | 
            +
                    hash_key_type = fields[table_hash_key][:type]
         | 
| 101 | 
            +
                    hash_key_type = :string if hash_key_type == :reference
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    range_key_type = table_range_key && fields[table_range_key][:type]
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    self.dynamo_table = dynamo_client.tables.create(table_full_name, 
         | 
| 106 | 
            +
                      table_read_capacity_units, table_write_capacity_units,
         | 
| 107 | 
            +
                      hash_key: { table_hash_key => hash_key_type},
         | 
| 108 | 
            +
                      range_key: table_range_key && { table_range_key => range_key_type }
         | 
| 109 | 
            +
                    )
         | 
| 110 | 
            +
                    sleep 1 until dynamo_table.status == :active
         | 
| 111 | 
            +
                    setup_dynamo
         | 
| 112 | 
            +
                    true
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
             | 
| 66 115 |  | 
| 67 | 
            -
                   | 
| 116 | 
            +
                  def delete_table
         | 
| 117 | 
            +
                    return false unless dynamo_table.exists? && dynamo_table.status == :active
         | 
| 118 | 
            +
                    dynamo_table.delete
         | 
| 119 | 
            +
                    true
         | 
| 120 | 
            +
                  end
         | 
| 68 121 |  | 
| 69 | 
            -
                  self.dynamo_table = dynamo_client.tables.create(table_full_name, 
         | 
| 70 | 
            -
                    table_read_capacity_units, table_write_capacity_units,
         | 
| 71 | 
            -
                    hash_key: { table_hash_key => hash_key_type},
         | 
| 72 | 
            -
                    range_key: table_range_key && { table_range_key => range_key_type }
         | 
| 73 | 
            -
                  )
         | 
| 74 | 
            -
                  sleep 1 until dynamo_table.status == :active
         | 
| 75 | 
            -
                  setup_dynamo
         | 
| 76 | 
            -
                  true
         | 
| 77 122 | 
             
                end
         | 
| 123 | 
            +
              
         | 
| 78 124 |  | 
| 125 | 
            +
                # ---------------------------------------------------------
         | 
| 126 | 
            +
                #
         | 
| 127 | 
            +
                #  Instance methods
         | 
| 128 | 
            +
                #
         | 
| 129 | 
            +
                # ---------------------------------------------------------
         | 
| 79 130 |  | 
| 80 | 
            -
                def  | 
| 81 | 
            -
                   | 
| 82 | 
            -
                   | 
| 83 | 
            -
                  true
         | 
| 131 | 
            +
                def initialize(*)
         | 
| 132 | 
            +
                  @dynamo_item = nil
         | 
| 133 | 
            +
                  super
         | 
| 84 134 | 
             
                end
         | 
| 85 135 |  | 
| 86 136 | 
             
              end
         | 
    
        data/lib/ocean-dynamo/version.rb
    CHANGED
    
    
    
        data/lib/ocean-dynamo.rb
    CHANGED
    
    | @@ -35,7 +35,7 @@ module OceanDynamo | |
| 35 35 |  | 
| 36 36 | 
             
                include Basal
         | 
| 37 37 |  | 
| 38 | 
            -
                 | 
| 38 | 
            +
                include Tables
         | 
| 39 39 | 
             
                extend Schema
         | 
| 40 40 |  | 
| 41 41 | 
             
                include Attributes
         | 
| @@ -47,5 +47,11 @@ module OceanDynamo | |
| 47 47 | 
             
                include HasMany
         | 
| 48 48 |  | 
| 49 49 |  | 
| 50 | 
            +
                def initialize(attrs={})
         | 
| 51 | 
            +
                  run_callbacks :initialize do
         | 
| 52 | 
            +
                    super
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 50 56 | 
             
              end
         | 
| 51 57 | 
             
            end
         | 
    
        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.4. | 
| 4 | 
            +
              version: 0.4.1
         | 
| 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-25 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: aws-sdk
         |