Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Utility Methods to Aws::Record Model Interface #82

Merged
merged 19 commits into from
Jun 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Unreleased Changes
------------------

* Feature - Aws::Record - Add the `persisted?`, `new_record?`, and `destroyed?` methods to `Aws::Record`, which supports use cases where you'd like to see if a record has just been newly initialized, or has been deleted or was a preexisting record retrieved from DynamoDB. Note that these methods are present in `ActiveModel::Model` so you should require that module before `Aws::Record`

* Feature - Aws::Record - Add the `assign_attributes`, `update`, and `update!` methods to `Aws::Record` which supports the use case where the user might want to mass assign or update a records attributes by hash. `update!` also ensures that a `ValidationError` is thrown on an invalid update

* Upgrading - If you already include `ActiveModel::Model` on your models the new `persisted?`, `new_record?` and `destroyed?` methods will not function properly unless you include `ActiveModel::Model` before `Aws::Record`

2.0.2 (2018-06-08)
------------------

Expand Down
31 changes: 31 additions & 0 deletions features/items/items.feature
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,34 @@ Feature: Amazon DynamoDB Items
["count", 5]
]
"""

Scenario: Update an Item from Amazon DynamoDB with aws-record
Given an item exists in the DynamoDB table with item data:
"""
{
"id": "4",
"count": 5,
"content": "Body content."
}
"""
When we call the 'find' class method with parameter data:
"""
{
"id": "4",
"count": 5
}
"""
And we call 'update' on the aws-record item instance with parameter data:
"""
{
"body": "Updated Body Content."
}
"""
Then we should receive an aws-record item with attribute data:
"""
{
"id": "4",
"count": 5,
"body": "Updated Body Content."
}
"""
5 changes: 5 additions & 0 deletions features/items/step_definitions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
@instance.delete!
end

When(/^we call 'update' on the aws\-record item instance with parameter data:$/) do |string|
data = JSON.parse(string, symbolize_names: true)
@instance.update(data)
end

When(/^we set the item attribute "([^"]*)" to be "([^"]*)"$/) do |attr, value|
@instance.send(:"#{attr}=", value)
end
Expand Down
64 changes: 64 additions & 0 deletions lib/aws-record/record/dirty_tracking.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,70 @@ def dirty?
@data.dirty?
end

# Returns +true+ if the model is not new and has not been deleted, +false+ otherwise.
#
# @example
# class Model
# include Aws::Record
# integer_attr :id, hash_key: true
# string_attr :name
# end
#
# model = Model.new
# model.persisted? # => false
# model.save
# model.persisted? # => true
# model.delete!
# model.persisted? # => false
#
# @return [Boolean] +true+ if the model is not new and has not been deleted, +false+
# otherwise.
def persisted?
@data.persisted?
end

# Returns +true+ if the model is newly initialized, +false+ otherwise.
#
# @example
# class Model
# include Aws::Record
# integer_attr :id, hash_key: true
# string_attr :name
# end
#
# model = Model.new
# model.new_record? # => true
# model.save
# model.new_record? # => false
#
# @return [Boolean] +true+ if the model is newly initialized, +false+
# otherwise.
def new_record?
@data.new_record?
end

# Returns +true+ if the model has been destroyed, +false+ otherwise.
#
# @example
# class Model
# include Aws::Record
# integer_attr :id, hash_key: true
# string_attr :name
# end
#
# model = Model.new
# model.destroyed? # => false
# model.save
# model.destroyed? # => false
# model.delete!
# model.destroyed? # => true
#
# @return [Boolean] +true+ if the model has been destroyed, +false+
# otherwise.
def destroyed?
@data.destroyed?
end

# Fetches attributes for this instance of an item from Amazon DynamoDB
# using its primary key and the +find(*)+ class method.
#
Expand Down
1 change: 1 addition & 0 deletions lib/aws-record/record/item_collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def _build_items_from_response(items, model)
data.set_attribute(name, attr.extract(item))
end
data.clean!
data.new_record = false
ret << record
end
ret
Expand Down
17 changes: 17 additions & 0 deletions lib/aws-record/record/item_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ def initialize(model_attributes, opts)
@model_attributes = model_attributes
@track_mutations = opts[:track_mutations]
@track_mutations = true if opts[:track_mutations].nil?
@new_record = true
@destroyed = false

populate_default_values
end

attr_accessor :new_record, :destroyed

def get_attribute(name)
@model_attributes.attribute_for(name).type_cast(@data[name])
Expand All @@ -34,6 +39,18 @@ def set_attribute(name, value)
@data[name] = value
end

def new_record?
@new_record
end

def destroyed?
@destroyed
end

def persisted?
!(new_record? || destroyed?)
end

def raw_value(name)
@data[name]
end
Expand Down
110 changes: 109 additions & 1 deletion lib/aws-record/record/item_operations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,109 @@ def save(opts = {})
end
end


# Deletes the item instance that matches the key values of this item
# instance in Amazon DynamoDB.
#
# @example Usage Example
# class MyModel
# include Aws::Record
# integer_attr :uuid, hash_key: true
# string_attr :name, range_key: true
# integer_attr :age
# float_attr :height
# end
#
# model = MyModel.new(id: 4, name: "John", age: 4, height: 70.5)
# model.age # => 4
# model.height # => 70.5
# model.save
# model.dirty? # => false
#
# model.assign_attributes(age: 5, height: 150.75)
# model.age # => 5
# model.height # => 150.75
# model.dirty? # => true
#
#
# @param [Hash] opts
def assign_attributes(opts)
opts.each do |field, new_value|
field = field.to_sym
setter = "#{field}="
raise ArgumentError.new "Invalid field: #{field} for model" unless respond_to?(setter)
public_send(setter, new_value)
end
end

# Mass assigns the attributes to the model and then performs a save
#
# You can use the +:force+ option to perform a simple put/overwrite
# without conditional validation or update logic.
#
# Note that aws-record allows you to change your model's key values,
# but this will be interpreted as persisting a new item to your DynamoDB
# table
#
# @example Usage Example
# class MyModel
# include Aws::Record
# integer_attr :uuid, hash_key: true
# string_attr :name, range_key: true
# integer_attr :age
# float_attr :height
# end
#
# model = MyModel.new(id: 4, name: "John", age: 4, height: 70.5)
# model.age # => 4
# model.height # => 70.5
# model.save
# model.dirty? # => false
#
# model.update(age: 5, height: 150.75)
# model.age # => 5
# model.height # => 150.75
# model.dirty? # => false
#
#
# @param [Hash] new_param, contains the new parameters for the model
#
# @param [Hash] opts
# @option opts [Boolean] :force if true, will save as a put operation and
# overwrite any existing item on the remote end. Otherwise, and by
# default, will either perform a conditional put or an update call.
# @return false if the record is invalid as defined by an attempt to call
# +valid?+ on this item, if that method exists. Otherwise, returns client
# call return value.
def update(new_params, opts = {})
assign_attributes(new_params)
save(opts)
end

# Updates model attributes and validates new values
#
# You can use the +:force+ option to perform a simple put/overwrite
# without conditional validation or update logic.
#
# Note that aws-record allows you to change your model's key values,
# but this will be interpreted as persisting a new item to your DynamoDB
# table
#
# @param [Hash] new_param, contains the new parameters for the model
#
# @param [Hash] opts
# @option opts [Boolean] :force if true, will save as a put operation and
# overwrite any existing item on the remote end. Otherwise, and by
# default, will either perform a conditional put or an update call.
# @return The update mode if the update is successful
#
# @raise [Aws::Record::Errors::ValidationError] if any new values
# violate the models validations.
def update!(new_params, opts = {})
assign_attributes(new_params)
save!(opts)
end

# Deletes the item instance that matches the key values of this item
# instance in Amazon DynamoDB. Uses the
# {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#delete_item-instance_method Aws::DynamoDB::Client#delete_item}
Expand All @@ -90,7 +193,7 @@ def delete!
table_name: self.class.table_name,
key: key_values
)
true
self.instance_variable_get("@data").destroyed = true
end

private
Expand Down Expand Up @@ -151,6 +254,10 @@ def _perform_save(opts)
)
end
end
data = self.instance_variable_get("@data")
data.destroyed = false
data.new_record = false
true
end

def _build_item_for_save
Expand Down Expand Up @@ -382,6 +489,7 @@ def build_item_from_resp(resp)
data = record.instance_variable_get("@data")
attributes.attributes.each do |name, attr|
data.set_attribute(name, attr.extract(resp.item))
data.new_record = false
end
record
end
Expand Down
Loading