Class: OceanDynamo::Base
- Inherits:
-
Object
- Object
- OceanDynamo::Base
- Includes:
- ActiveModel::Model, ActiveModel::Validations::Callbacks
- Defined in:
- lib/ocean-dynamo/base.rb,
lib/ocean-dynamo/schema.rb,
lib/ocean-dynamo/tables.rb,
lib/ocean-dynamo/queries.rb,
lib/ocean-dynamo/callbacks.rb,
lib/ocean-dynamo/attributes.rb,
lib/ocean-dynamo/persistence.rb,
lib/ocean-dynamo/class_variables.rb
Instance Attribute Summary collapse
-
#attributes ⇒ Object
readonly
The hash of attributes and their values.
-
#destroyed ⇒ Object
readonly
:nodoc:.
-
#dynamo_item ⇒ Object
readonly
:nodoc:.
-
#new_record ⇒ Object
readonly
:nodoc:.
Class Method Summary collapse
- .attribute(name, type = :string, **pairs) ⇒ Object
- .compute_table_name ⇒ Object
- .count ⇒ Object
- .create(attributes = nil) {|object| ... } ⇒ Object
- .create!(attributes = nil) {|object| ... } ⇒ Object
- .create_table ⇒ Object
- .delete(hash, range = nil) ⇒ Object
- .delete_table ⇒ Object
- .dynamo_schema(table_hash_key = :uuid, table_range_key = nil, table_name: compute_table_name, table_name_prefix: nil, table_name_suffix: nil, read_capacity_units: 10, write_capacity_units: 5, connect: :late, create: false, locking: :lock_version, timestamps: [:created_at, :updated_at], &block) ⇒ Object
- .establish_db_connection ⇒ Object
- .find(hash, range = nil, consistent: false) ⇒ Object
- .find_by_key(*args) ⇒ Object
- .set_dynamo_table_keys ⇒ Object
- .setup_dynamo ⇒ Object
- .table_full_name ⇒ Object
- .wait_until_table_is_active ⇒ Object
Instance Method Summary collapse
-
#<=>(other_object) ⇒ Object
Allows sort on objects.
- #==(comparison_object) ⇒ Object (also: #eql?)
- #[](attribute) ⇒ Object
- #[]=(attribute, value) ⇒ Object
- #assign_attributes(values) ⇒ Object
- #create(options = {}) ⇒ Object
- #create_or_update(options = {}) ⇒ Object
- #delete ⇒ Object
- #deserialize_attribute(value, metadata, type: ) ⇒ Object
- #destroy ⇒ Object
- #destroy! ⇒ Object
-
#destroyed? ⇒ Boolean
———————————————————.
-
#freeze ⇒ Object
Clone and freeze the attributes hash such that associations are still accessible, even on destroyed records, but cloned models will not be frozen.
-
#frozen? ⇒ Boolean
Returns
true
if the attributes hash has been frozen. - #id ⇒ Object
- #id=(value) ⇒ Object
-
#initialize(attrs = {}) ⇒ Base
constructor
A new instance of Base.
- #new_record? ⇒ Boolean
- #persisted? ⇒ Boolean
- #read_attribute(attr_name) ⇒ Object
- #read_attribute_for_validation(key) ⇒ Object
- #reload(**keywords) ⇒ Object
- #save(options = {}) ⇒ Object
- #save!(options = {}) ⇒ Object
- #serialize_attribute(attribute, value, metadata = , type: ) ⇒ Object
- #serialized_attributes ⇒ Object
- #to_key ⇒ Object
- #touch(name = nil) ⇒ Object
- #type_cast_attribute_for_write(name, value, metadata = , type: ) ⇒ Object
- #update(options = {}) ⇒ Object
- #update_attributes(attrs = {}) ⇒ Object
- #update_attributes!(attrs = {}) ⇒ Object
- #valid?(context = nil) ⇒ Boolean
- #write_attribute(attr_name, value) ⇒ Object
Constructor Details
#initialize(attrs = {}) ⇒ Base
14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/ocean-dynamo/attributes.rb', line 14 def initialize(attrs={}) run_callbacks :initialize do @attributes = Hash.new fields.each do |name, md| write_attribute(name, evaluate_default(md[:default], md[:type])) end @dynamo_item = nil @destroyed = false @new_record = true raise UnknownPrimaryKey unless table_hash_key end attrs && attrs.delete_if { |k, v| !fields.has_key?(k) } super(attrs) end |
Instance Attribute Details
#attributes ⇒ Object (readonly)
The hash of attributes and their values. Keys are strings.
7 8 9 |
# File 'lib/ocean-dynamo/attributes.rb', line 7 def attributes @attributes end |
#destroyed ⇒ Object (readonly)
:nodoc:
9 10 11 |
# File 'lib/ocean-dynamo/attributes.rb', line 9 def destroyed @destroyed end |
#dynamo_item ⇒ Object (readonly)
:nodoc:
11 12 13 |
# File 'lib/ocean-dynamo/attributes.rb', line 11 def dynamo_item @dynamo_item end |
#new_record ⇒ Object (readonly)
:nodoc:
10 11 12 |
# File 'lib/ocean-dynamo/attributes.rb', line 10 def new_record @new_record end |
Class Method Details
.attribute(name, type = :string, **pairs) ⇒ Object
62 63 64 65 66 |
# File 'lib/ocean-dynamo/schema.rb', line 62 def self.attribute(name, type=:string, **pairs) raise DangerousAttributeError, "#{name} is defined by OceanDynamo" if self.dangerous_attributes.include?(name.to_s) attr_accessor name fields[name.to_s] = {type: type, default: pairs[:default]} end |
.compute_table_name ⇒ Object
52 53 54 |
# File 'lib/ocean-dynamo/schema.rb', line 52 def self.compute_table_name name.pluralize.underscore end |
.count ⇒ Object
19 20 21 22 |
# File 'lib/ocean-dynamo/queries.rb', line 19 def self.count _late_connect? dynamo_table.item_count || -1 # The || -1 is for fake_dynamo specs. end |
.create(attributes = nil) {|object| ... } ⇒ Object
11 12 13 14 15 16 |
# File 'lib/ocean-dynamo/persistence.rb', line 11 def self.create(attributes = nil, &block) object = new(attributes) yield(object) if block_given? object.save object end |
.create!(attributes = nil) {|object| ... } ⇒ Object
19 20 21 22 23 24 |
# File 'lib/ocean-dynamo/persistence.rb', line 19 def self.create!(attributes = nil, &block) object = new(attributes) yield(object) if block_given? object.save! object end |
.create_table ⇒ Object
53 54 55 56 57 58 59 60 61 62 |
# File 'lib/ocean-dynamo/tables.rb', line 53 def self.create_table self.dynamo_table = dynamo_client.tables.create(table_full_name, table_read_capacity_units, table_write_capacity_units, hash_key: { table_hash_key => fields[table_hash_key][:type]}, range_key: table_range_key && { table_range_key => fields[table_range_key][:type]} ) sleep 1 until dynamo_table.status == :active setup_dynamo true end |
.delete(hash, range = nil) ⇒ Object
27 28 29 30 31 32 |
# File 'lib/ocean-dynamo/persistence.rb', line 27 def self.delete(hash, range=nil) item = dynamo_items[hash, range] return false unless item.exists? item.delete true end |
.delete_table ⇒ Object
65 66 67 68 69 |
# File 'lib/ocean-dynamo/tables.rb', line 65 def self.delete_table return false unless dynamo_table.exists? && dynamo_table.status == :active dynamo_table.delete true end |
.dynamo_schema(table_hash_key = :uuid, table_range_key = nil, table_name: compute_table_name, table_name_prefix: nil, table_name_suffix: nil, read_capacity_units: 10, write_capacity_units: 5, connect: :late, create: false, locking: :lock_version, timestamps: [:created_at, :updated_at], &block) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/ocean-dynamo/schema.rb', line 5 def self.dynamo_schema(table_hash_key=:uuid, table_range_key=nil, table_name: compute_table_name, table_name_prefix: nil, table_name_suffix: nil, read_capacity_units: 10, write_capacity_units: 5, connect: :late, create: false, locking: :lock_version, timestamps: [:created_at, :updated_at], &block) # Set class vars self.dynamo_client = nil self.dynamo_table = nil self.dynamo_items = nil self.table_connected = false self.table_connect_policy = connect self.table_create_policy = create self.table_hash_key = table_hash_key self.table_range_key = table_range_key self.table_name = table_name self.table_name_prefix = table_name_prefix self.table_name_suffix = table_name_suffix self.table_read_capacity_units = read_capacity_units self.table_write_capacity_units = write_capacity_units self.lock_attribute = locking self. = # Init self.fields = HashWithIndifferentAccess.new attribute table_hash_key, :string, default: '' .each { |name| attribute name, :datetime } if attribute(lock_attribute, :integer, default: 0) if lock_attribute block.call # Define attribute accessors fields.each do |name, md| self.class_eval "def #{name}; read_attribute('#{name.to_s}'); end" self.class_eval "def #{name}=(value); write_attribute('#{name.to_s}', value); end" self.class_eval "def #{name}?; read_attribute('#{name.to_s}').present?; end" end # Connect to AWS establish_db_connection if connect == true # Finally return the full table name table_full_name end |
.establish_db_connection ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 |
# File 'lib/ocean-dynamo/tables.rb', line 4 def self.establish_db_connection setup_dynamo if dynamo_table.exists? wait_until_table_is_active self.table_connected = true else raise(TableNotFound, table_full_name) unless table_create_policy create_table end set_dynamo_table_keys end |
.find(hash, range = nil, consistent: false) ⇒ Object
4 5 6 7 8 9 |
# File 'lib/ocean-dynamo/queries.rb', line 4 def self.find(hash, range=nil, consistent: false) _late_connect? item = dynamo_items[hash, range] raise RecordNotFound unless item.exists? new.send(:dynamo_unpersist, item, consistent) end |
.find_by_key(*args) ⇒ Object
12 13 14 15 16 |
# File 'lib/ocean-dynamo/queries.rb', line 12 def self.find_by_key(*args) find(*args) rescue RecordNotFound nil end |
.set_dynamo_table_keys ⇒ Object
45 46 47 48 49 50 |
# File 'lib/ocean-dynamo/tables.rb', line 45 def self.set_dynamo_table_keys dynamo_table.hash_key = [table_hash_key, fields[table_hash_key][:type]] if table_range_key dynamo_table.range_key = [table_range_key, fields[table_range_key][:type]] end end |
.setup_dynamo ⇒ Object
17 18 19 20 21 22 |
# File 'lib/ocean-dynamo/tables.rb', line 17 def self.setup_dynamo #self.dynamo_client = AWS::DynamoDB::Client.new(:api_version => '2012-08-10') self.dynamo_client ||= AWS::DynamoDB.new self.dynamo_table = dynamo_client.tables[table_full_name] self.dynamo_items = dynamo_table.items end |
.table_full_name ⇒ Object
57 58 59 |
# File 'lib/ocean-dynamo/schema.rb', line 57 def self.table_full_name "#{table_name_prefix}#{table_name}#{table_name_suffix}" end |
.wait_until_table_is_active ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/ocean-dynamo/tables.rb', line 25 def self.wait_until_table_is_active loop do case dynamo_table.status when :active set_dynamo_table_keys return when :updating, :creating sleep 1 next when :deleting sleep 1 while dynamo_table.exists? create_table return else raise UnknownTableStatus.new("Unknown DynamoDB table status '#{dynamo_table.status}'") end end end |
Instance Method Details
#<=>(other_object) ⇒ Object
Allows sort on objects
31 32 33 34 35 |
# File 'lib/ocean-dynamo/base.rb', line 31 def <=>(other_object) if other_object.is_a?(self.class) self.to_key <=> other_object.to_key end end |
#==(comparison_object) ⇒ Object Also known as: eql?
8 9 10 11 12 13 |
# File 'lib/ocean-dynamo/base.rb', line 8 def ==(comparison_object) super || comparison_object.instance_of?(self.class) && id.present? && comparison_object.id == id end |
#[](attribute) ⇒ Object
30 31 32 |
# File 'lib/ocean-dynamo/attributes.rb', line 30 def [](attribute) read_attribute attribute end |
#[]=(attribute, value) ⇒ Object
35 36 37 |
# File 'lib/ocean-dynamo/attributes.rb', line 35 def []=(attribute, value) write_attribute attribute, value end |
#assign_attributes(values) ⇒ Object
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/ocean-dynamo/attributes.rb', line 83 def assign_attributes(values) return if values.blank? values = values.stringify_keys # if values.respond_to?(:permitted?) # unless values.permitted? # raise ActiveModel::ForbiddenAttributesError # end # end values.each { |k, v| _assign_attribute(k, v) } end |
#create(options = {}) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/ocean-dynamo/persistence.rb', line 104 def create(={}) return false if [:validate] != false && !valid?(:create) run_callbacks :commit do run_callbacks :save do run_callbacks :create do k = read_attribute(table_hash_key) write_attribute(table_hash_key, SecureRandom.uuid) if k == "" || k == nil dynamo_persist true end end end end |
#create_or_update(options = {}) ⇒ Object
98 99 100 101 |
# File 'lib/ocean-dynamo/persistence.rb', line 98 def create_or_update(={}) result = new_record? ? create() : update() result != false end |
#delete ⇒ Object
148 149 150 151 152 153 154 |
# File 'lib/ocean-dynamo/persistence.rb', line 148 def delete if persisted? dynamo_delete(lock: lock_attribute) end freeze @destroyed = true end |
#deserialize_attribute(value, metadata, type: ) ⇒ Object
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/ocean-dynamo/attributes.rb', line 159 def deserialize_attribute(value, , type: [:type]) case type when :string return "" if value == nil value.is_a?(Set) ? value.to_a : value when :integer return nil if value == nil value.is_a?(Set) || value.is_a?(Array) ? value.collect(&:to_i) : value.to_i when :float return nil if value == nil value.is_a?(Set) || value.is_a?(Array) ? value.collect(&:to_f) : value.to_f when :boolean case value when "true" true when "false" false else nil end when :datetime return nil if value == nil Time.zone.at(value.to_i) when :serialized return nil if value == nil JSON.parse(value) else raise UnsupportedType.new(type.to_s) end end |
#destroy ⇒ Object
134 135 136 137 138 139 140 |
# File 'lib/ocean-dynamo/persistence.rb', line 134 def destroy run_callbacks :commit do run_callbacks :destroy do delete end end end |
#destroy! ⇒ Object
143 144 145 |
# File 'lib/ocean-dynamo/persistence.rb', line 143 def destroy! destroy || raise(RecordNotDestroyed) end |
#destroyed? ⇒ Boolean
Instance variables and methods
41 42 43 |
# File 'lib/ocean-dynamo/persistence.rb', line 41 def destroyed? @destroyed end |
#freeze ⇒ Object
Clone and freeze the attributes hash such that associations are still accessible, even on destroyed records, but cloned models will not be frozen.
20 21 22 23 |
# File 'lib/ocean-dynamo/base.rb', line 20 def freeze @attributes = @attributes.clone.freeze self end |
#frozen? ⇒ Boolean
Returns true
if the attributes hash has been frozen.
26 27 28 |
# File 'lib/ocean-dynamo/base.rb', line 26 def frozen? @attributes.frozen? end |
#id ⇒ Object
40 41 42 |
# File 'lib/ocean-dynamo/attributes.rb', line 40 def id read_attribute(table_hash_key) end |
#id=(value) ⇒ Object
45 46 47 |
# File 'lib/ocean-dynamo/attributes.rb', line 45 def id=(value) write_attribute(table_hash_key, value) end |
#new_record? ⇒ Boolean
46 47 48 |
# File 'lib/ocean-dynamo/persistence.rb', line 46 def new_record? @new_record end |
#persisted? ⇒ Boolean
51 52 53 |
# File 'lib/ocean-dynamo/persistence.rb', line 51 def persisted? !(new_record? || destroyed?) end |
#read_attribute(attr_name) ⇒ Object
55 56 57 58 59 60 61 |
# File 'lib/ocean-dynamo/attributes.rb', line 55 def read_attribute(attr_name) attr_name = attr_name.to_s if attr_name == 'id' && fields[table_hash_key] != attr_name.to_sym return read_attribute(table_hash_key) end @attributes[attr_name] # Type cast! end |
#read_attribute_for_validation(key) ⇒ Object
50 51 52 |
# File 'lib/ocean-dynamo/attributes.rb', line 50 def read_attribute_for_validation(key) @attributes[key.to_s] end |
#reload(**keywords) ⇒ Object
157 158 159 160 161 162 |
# File 'lib/ocean-dynamo/persistence.rb', line 157 def reload(**keywords) range_key = table_range_key && attributes[table_range_key] new_instance = self.class.find(id, range_key, **keywords) assign_attributes(new_instance.attributes) self end |
#save(options = {}) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/ocean-dynamo/persistence.rb', line 63 def save(={}) if perform_validations() begin create_or_update rescue RecordInvalid false end else false end end |
#save!(options = {}) ⇒ Object
76 77 78 79 80 81 82 83 |
# File 'lib/ocean-dynamo/persistence.rb', line 76 def save!(={}) if perform_validations() [:validate] = false create_or_update() || raise(RecordNotSaved) else raise RecordInvalid.new(self) end end |
#serialize_attribute(attribute, value, metadata = , type: ) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/ocean-dynamo/attributes.rb', line 137 def serialize_attribute(attribute, value, =fields[attribute], type: [:type]) return nil if value == nil case type when :string ["", []].include?(value) ? nil : value when :integer value == [] ? nil : value when :float value == [] ? nil : value when :boolean value ? "true" : "false" when :datetime value.to_i when :serialized value.to_json else raise UnsupportedType.new(type.to_s) end end |
#serialized_attributes ⇒ Object
127 128 129 130 131 132 133 134 |
# File 'lib/ocean-dynamo/attributes.rb', line 127 def serialized_attributes result = {} fields.each do |attribute, | serialized = serialize_attribute(attribute, read_attribute(attribute), ) result[attribute] = serialized unless serialized == nil end result end |
#to_key ⇒ Object
75 76 77 78 79 80 |
# File 'lib/ocean-dynamo/attributes.rb', line 75 def to_key return nil unless persisted? key = respond_to?(:id) && id return nil unless key table_range_key ? [key, read_attribute(table_range_key)] : [key] end |
#touch(name = nil) ⇒ Object
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/ocean-dynamo/persistence.rb', line 165 def touch(name=nil) raise DynamoError, "can not touch on a new record object" unless persisted? _late_connect? run_callbacks :touch do begin dynamo_item.attributes.update(_handle_locking) do |u| (name).each do |k| u.set(k => serialize_attribute(k, read_attribute(k))) end end rescue AWS::DynamoDB::Errors::ConditionalCheckFailedException raise OceanDynamo::StaleObjectError end self end end |
#type_cast_attribute_for_write(name, value, metadata = , type: ) ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/ocean-dynamo/attributes.rb', line 95 def type_cast_attribute_for_write(name, value, =fields[name], type: [:type]) case type when :string return nil if value == nil return value.collect(&:to_s) if value.is_a?(Array) value when :integer return nil if value == nil return value.collect(&:to_i) if value.is_a?(Array) value.to_i when :float return nil if value == nil return value.collect(&:to_f) if value.is_a?(Array) value.to_f when :boolean return nil if value == nil return true if value == true return true if value == "true" false when :datetime return nil if value == nil || !value.kind_of?(Time) value when :serialized return nil if value == nil value else raise UnsupportedType.new(type.to_s) end end |
#update(options = {}) ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/ocean-dynamo/persistence.rb', line 120 def update(={}) return false if [:validate] != false && !valid?(:update) run_callbacks :commit do run_callbacks :save do run_callbacks :update do dynamo_persist(lock: lock_attribute) true end end end end |
#update_attributes(attrs = {}) ⇒ Object
86 87 88 89 |
# File 'lib/ocean-dynamo/persistence.rb', line 86 def update_attributes(attrs={}) assign_attributes(attrs) save end |
#update_attributes!(attrs = {}) ⇒ Object
92 93 94 95 |
# File 'lib/ocean-dynamo/persistence.rb', line 92 def update_attributes!(attrs={}) assign_attributes(attrs) save! end |
#valid?(context = nil) ⇒ Boolean
56 57 58 59 60 |
# File 'lib/ocean-dynamo/persistence.rb', line 56 def valid?(context = nil) context ||= (new_record? ? :create : :update) output = super(context) errors.empty? && output end |
#write_attribute(attr_name, value) ⇒ Object
64 65 66 67 68 69 70 71 72 |
# File 'lib/ocean-dynamo/attributes.rb', line 64 def write_attribute(attr_name, value) attr_name = attr_name.to_s attr_name = table_hash_key.to_s if attr_name == 'id' && fields[table_hash_key] if fields.has_key?(attr_name) @attributes[attr_name] = type_cast_attribute_for_write(attr_name, value) else raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" end end |