Module: Dynamoid::Persistence::ClassMethods
- Defined in:
- lib/dynamoid/persistence.rb
Instance Method Summary collapse
-
#create(attrs = {}, &block) ⇒ Dynamoid::Document
Create a model.
-
#create!(attrs = {}, &block) ⇒ Dynamoid::Document
Create a model.
-
#create_table(options = {}) ⇒ true|false
Create a table.
-
#delete_table ⇒ Model class
Deletes the table for the model.
- #from_database(attrs = {}) ⇒ Object
-
#import(array_of_attributes) ⇒ Array
Create several models at once.
-
#inc(hash_key_value, range_key_value = nil, counters) ⇒ Model class
Increase a numeric field by specified value.
- #table_name ⇒ Object
-
#update(hash_key, range_key_value = nil, attrs) ⇒ Dynamoid::Document
Update document with provided attributes.
-
#update!(hash_key, range_key_value = nil, attrs) ⇒ Dynamoid::Document
Update document with provided attributes.
-
#update_fields(hash_key_value, range_key_value = nil, attrs = {}, conditions = {}) ⇒ Dynamoid::Document|nil
Update document.
-
#upsert(hash_key_value, range_key_value = nil, attrs = {}, conditions = {}) ⇒ Dynamoid::Document|nil
Update an existing document or create a new one.
Instance Method Details
#create(attrs = {}, &block) ⇒ Dynamoid::Document
Create a model.
Initializes a new model and immediately saves it to DynamoDB.
User.create(first_name: 'Mark', last_name: 'Tyler')
Accepts both Hash and Array of Hashes and can create several models.
User.create([{ first_name: 'Alice' }, { first_name: 'Bob' }])
Creates a model and pass it into a block to set other attributes.
User.create(first_name: 'Mark') do |u|
u.age = 21
end
Validates model and runs callbacks.
189 190 191 192 193 194 195 |
# File 'lib/dynamoid/persistence.rb', line 189 def create(attrs = {}, &block) if attrs.is_a?(Array) attrs.map { |attr| create(attr, &block) } else build(attrs, &block).tap(&:save) end end |
#create!(attrs = {}, &block) ⇒ Dynamoid::Document
Create a model.
Initializes a new object and immediately saves it to the Dynamoid. Raises an exception Dynamoid::Errors::DocumentNotValid
if validation failed. Accepts both Hash and Array of Hashes and can create several models.
208 209 210 211 212 213 214 |
# File 'lib/dynamoid/persistence.rb', line 208 def create!(attrs = {}, &block) if attrs.is_a?(Array) attrs.map { |attr| create!(attr, &block) } else build(attrs, &block).tap(&:save!) end end |
#create_table(options = {}) ⇒ true|false
Create a table.
Uses a configuration specified in a model class (with the table
method) e.g. table name, schema (hash and range keys), global and local secondary indexes, billing mode and write/read capacity.
For instance here
class User
include Dynamoid::Document
table key: :uuid
range :last_name
field :first_name
field :last_name
end
User.create_table
create_table
method call will create a table dynamoid_users
with hash key uuid
and range key name
, DynamoDB default billing mode and Dynamoid default read/write capacity units (100/20).
All the configuration can be overridden with options
argument.
User.create_table(table_name: 'users', read_capacity: 200, write_capacity: 40)
Dynamoid creates a table synchronously by default. DynamoDB table creation is an asynchronous operation and a client should wait until a table status changes to ACTIVE
and a table becomes available. That’s why Dynamoid is polling a table status and returns results only when a table becomes available.
Polling is configured with Dynamoid::Config.sync_retry_max_times
and Dynamoid::Config.sync_retry_wait_seconds
configuration options. If table creation takes more time than configured waiting time then Dynamoid stops polling and returns true
.
In order to return back asynchronous behaviour and not to wait until a table is created the sync: false option should be specified.
User.create_table(sync: false)
Subsequent method calls for the same table will be ignored.
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 |
# File 'lib/dynamoid/persistence.rb', line 95 def create_table( = {}) range_key_hash = if range_key { range_key => PrimaryKeyTypeMapping.dynamodb_type(attributes[range_key][:type], attributes[range_key]) } end = { id: hash_key, table_name: table_name, billing_mode: capacity_mode, write_capacity: write_capacity, read_capacity: read_capacity, range_key: range_key_hash, hash_key_type: PrimaryKeyTypeMapping.dynamodb_type(attributes[hash_key][:type], attributes[hash_key]), local_secondary_indexes: local_secondary_indexes.values, global_secondary_indexes: global_secondary_indexes.values }.merge() created_successfuly = Dynamoid.adapter.create_table([:table_name], [:id], ) if created_successfuly && self.[:expires] attribute = self.[:expires][:field] Dynamoid.adapter.update_time_to_live([:table_name], attribute) end self end |
#delete_table ⇒ Model class
Deletes the table for the model.
Dynamoid deletes a table asynchronously and doesn’t wait until a table is deleted completely.
Subsequent method calls for the same table will be ignored.
129 130 131 132 |
# File 'lib/dynamoid/persistence.rb', line 129 def delete_table Dynamoid.adapter.delete_table(table_name) self end |
#from_database(attrs = {}) ⇒ Object
135 136 137 138 139 |
# File 'lib/dynamoid/persistence.rb', line 135 def from_database(attrs = {}) klass = choose_right_class(attrs) attrs_undumped = Undumping.undump_attributes(attrs, klass.attributes) klass.new(attrs_undumped).tap { |r| r.new_record = false } end |
#import(array_of_attributes) ⇒ Array
Create several models at once.
users = User.import([{ name: 'a' }, { name: 'b' }])
import
is a relatively low-level method and bypasses some mechanisms like callbacks and validation.
It sets timestamp fields created_at
and updated_at
if they are blank. It sets a hash key field as well if it’s blank. It expects that the hash key field is string
and sets a random UUID value if the field value is blank. All the field values are type casted to the declared types.
It works efficiently and uses the ‘BatchWriteItem` operation. In order to cope with throttling it uses a backoff strategy if it’s specified with ‘Dynamoid::Config.backoff` configuration option.
Because of the nature of DynamoDB and its limits only 25 models can be saved at once. So multiple HTTP requests can be sent to DynamoDB.
163 164 165 |
# File 'lib/dynamoid/persistence.rb', line 163 def import(array_of_attributes) Import.call(self, array_of_attributes) end |
#inc(hash_key_value, range_key_value = nil, counters) ⇒ Model class
Increase a numeric field by specified value.
User.inc('1', age: 2)
Can update several fields at once.
User.inc('1', age: 2, version: 1)
If range key is declared for a model it should be passed as well:
User.inc('1', 'Tylor', age: 2)
It’s an atomic operation it does not interfere with other write requests.
Uses efficient low-level UpdateItem
operation and does only one HTTP request.
Doesn’t run validations and callbacks. Doesn’t update created_at
and updated_at
as well.
When ‘:touch` option is passed the timestamp columns are updating. If attribute names are passed, they are updated along with updated_at attribute:
User.inc('1', age: 2, touch: true)
User.inc('1', age: 2, touch: :viewed_at)
User.inc('1', age: 2, touch: [:viewed_at, :accessed_at])
409 410 411 412 |
# File 'lib/dynamoid/persistence.rb', line 409 def inc(hash_key_value, range_key_value = nil, counters) Inc.call(self, hash_key_value, range_key_value, counters) self end |
#table_name ⇒ Object
28 29 30 31 32 |
# File 'lib/dynamoid/persistence.rb', line 28 def table_name table_base_name = [:name] || base_class.name.split('::').last.downcase.pluralize @table_name ||= [Dynamoid::Config.namespace.to_s, table_base_name].reject(&:empty?).join('_') end |
#update(hash_key, range_key_value = nil, attrs) ⇒ Dynamoid::Document
Update document with provided attributes.
Instantiates document and saves changes. Runs validations and callbacks. Don’t save changes if validation fails.
User.update('1', age: 26)
If range key is declared for a model it should be passed as well:
User.update('1', 'Tylor', age: 26)
231 232 233 234 235 |
# File 'lib/dynamoid/persistence.rb', line 231 def update(hash_key, range_key_value = nil, attrs) model = find(hash_key, range_key: range_key_value, consistent_read: true) model.update_attributes(attrs) model end |
#update!(hash_key, range_key_value = nil, attrs) ⇒ Dynamoid::Document
Update document with provided attributes.
Instantiates document and saves changes. Runs validations and callbacks.
User.update!('1', age: 26)
If range key is declared for a model it should be passed as well:
User.update('1', 'Tylor', age: 26)
Raises Dynamoid::Errors::DocumentNotValid
exception if validation fails.
254 255 256 257 258 |
# File 'lib/dynamoid/persistence.rb', line 254 def update!(hash_key, range_key_value = nil, attrs) model = find(hash_key, range_key: range_key_value, consistent_read: true) model.update_attributes!(attrs) model end |
#update_fields(hash_key_value, range_key_value = nil, attrs = {}, conditions = {}) ⇒ Dynamoid::Document|nil
Update document.
Doesn’t run validations and callbacks.
User.update_fields('1', age: 26)
If range key is declared for a model it should be passed as well:
User.update_fields('1', 'Tylor', age: 26)
Can make a conditional update so a document will be updated only if it meets the specified conditions. Conditions can be specified as a Hash
with :if
key:
User.update_fields('1', { age: 26 }, { if: { version: 1 } })
Here User
model has an integer version
field and the document will be updated only if the version
attribute currently has value 1.
If a document with specified hash and range keys doesn’t exist or conditions were specified and failed the method call returns nil
.
To check if some attribute (or attributes) isn’t stored in a DynamoDB item (e.g. it wasn’t set explicitly) there is another condition - unless_exists
:
user = User.create(name: 'Tylor')
User.update_fields(user.id, { age: 18 }, { unless_exists: [:age] })
update_fields
uses the UpdateItem
operation so it saves changes and loads an updated document back with one HTTP request.
Raises a Dynamoid::Errors::UnknownAttribute
exception if any of the attributes is not on the model
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/dynamoid/persistence.rb', line 300 def update_fields(hash_key_value, range_key_value = nil, attrs = {}, conditions = {}) optional_params = [range_key_value, attrs, conditions].compact if optional_params.first.is_a?(Hash) range_key_value = nil attrs, conditions = optional_params[0..1] else range_key_value = optional_params.first attrs, conditions = optional_params[1..2] end UpdateFields.call(self, partition_key: hash_key_value, sort_key: range_key_value, attributes: attrs, conditions: conditions) end |
#upsert(hash_key_value, range_key_value = nil, attrs = {}, conditions = {}) ⇒ Dynamoid::Document|nil
Update an existing document or create a new one.
If a document with specified hash and range keys doesn’t exist it creates a new document with specified attributes. Doesn’t run validations and callbacks.
User.upsert('1', age: 26)
If range key is declared for a model it should be passed as well:
User.upsert('1', 'Tylor', age: 26)
Can make a conditional update so a document will be updated only if it meets the specified conditions. Conditions can be specified as a Hash
with :if
key:
User.upsert('1', { age: 26 }, { if: { version: 1 } })
Here User
model has an integer version
field and the document will be updated only if the version
attribute currently has value 1.
To check if some attribute (or attributes) isn’t stored in a DynamoDB item (e.g. it wasn’t set explicitly) there is another condition - unless_exists
:
user = User.create(name: 'Tylor')
User.upsert(user.id, { age: 18 }, { unless_exists: [:age] })
If conditions were specified and failed the method call returns nil
.
upsert
uses the UpdateItem
operation so it saves changes and loads an updated document back with one HTTP request.
Raises a Dynamoid::Errors::UnknownAttribute
exception if any of the attributes is not on the model
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# File 'lib/dynamoid/persistence.rb', line 358 def upsert(hash_key_value, range_key_value = nil, attrs = {}, conditions = {}) optional_params = [range_key_value, attrs, conditions].compact if optional_params.first.is_a?(Hash) range_key_value = nil attrs, conditions = optional_params[0..1] else range_key_value = optional_params.first attrs, conditions = optional_params[1..2] end Upsert.call(self, partition_key: hash_key_value, sort_key: range_key_value, attributes: attrs, conditions: conditions) end |