-
Notifications
You must be signed in to change notification settings - Fork 41
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
Conversation
to_param destroyed? new_record? persisted? update assign_attributes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good start, some changes proposed.
lib/aws-record/record/attributes.rb
Outdated
hkey = public_send(self.class.hash_key) | ||
if self.class.range_key | ||
rkey = public_send(self.class.range_key) | ||
"#{hkey}:#{rkey}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document why this is the separator? It's very possible and valid for these key values to contain the ":" character. Depending on what the API contract is, we may need to do more here.
end | ||
|
||
# Updates model attributes and then performs a conditional save | ||
def update(opts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also add an #update!
method, which essentially does the same thing except it raises an exception when the validation fails. Check the implementation of #save!
if !respond_to?("#{field}=") | ||
raise ArgumentError.new "Invalid field: #{field} for model" | ||
end | ||
@data.set_attribute field, new_value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style point in this repo, we explicitly use ()
characters around any non-empty method calls. So:
@data.set_attribute(field, new_value)
|
||
# Updates model attributes and then performs a conditional save | ||
def update(opts) | ||
self.assign_attributes opts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add ()
characters
@@ -151,6 +169,10 @@ def _perform_save(opts) | |||
) | |||
end | |||
end | |||
data = self.instance_variable_get("@data") | |||
data.destroyed = false if data.destroyed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the "if" condition here? It looks like the end condition is always going to be false or false-equivalent.
item.body = SecureRandom.uuid | ||
item.save | ||
|
||
new_key = SecureRandom.uuid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd strongly encourage that we use explicit values in these tests. Why do we need randomness?
@@ -77,6 +77,23 @@ module Record | |||
end | |||
end | |||
|
|||
describe "#new_record" do | |||
it 'marks records fetched from a client call as not being new' do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should go ahead and add the "obvious" test that something created by hand is marked as a new record. Part of the test coverage notion is to avoid surprise regressions later.
@@ -236,6 +260,39 @@ | |||
|
|||
end | |||
|
|||
describe "#update" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also looks like we never actually test-call the #update
method in these tests.
@@ -266,28 +303,60 @@ | |||
klass.configure_client(client: stub_client) | |||
end | |||
|
|||
it 'should perform a hash based attribute assignment without persisting changes' do | |||
it 'assign_attributes should perform a hash based attribute assignment without persisting changes' do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should test this with ActiveModel::Model
present as well.
lib/aws-record/record/attributes.rb
Outdated
hkey = public_send(self.class.hash_key) | ||
if self.class.range_key | ||
rkey = public_send(self.class.range_key) | ||
"#{hkey}:#{rkey}" | ||
"#{URI.encode(hkey)}##{URI.encode(rkey)}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did we come up with the #
character as a separator? We may need to do something more detailed like including the key names. That way, there's something concrete we can use as start/end points of most param values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good on code. For completeness, we need to:
- Get the new method documentation up to the standard of other methods in aws-record. Should have usage examples, parameters explained, and return values tagged. For example, bring the update methods in parity with the save methods.
- Add Cucumber tests, especially for the update method.
- Add CHANGELOG notes explaining what we added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very close!
CHANGELOG.md
Outdated
@@ -1,6 +1,10 @@ | |||
Unreleased Changes | |||
------------------ | |||
|
|||
* Feature - Aws::Record::DirtyTracking - Add the `persisted?`, `new_record?`, and `destroyed` methods to `Aws::Record::DirtyTracking`, 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` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the changelog, let's avoid DirtyTracking
and ItemOperations
. These don't mean anything to an end user, they're basically code organization. Rather, just explain that these methods will now be tacked on to your models.
Also, we may need an "UPGRADING NOTES" bit on ActiveModel, I'll talk with you about that tomorrow.
@@ -91,14 +94,20 @@ def assign_attributes(opts) | |||
end | |||
end | |||
|
|||
# Updates model attributes and then performs a conditional save | |||
# Mass assigns the attributes to the model and then performs a save |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add usage examples to these, since attribute assignment is involved.
# | ||
# @param [Hash] opts | ||
# @raise [Aws::Record::Errors::ValidationError] if any new values | ||
# violate the models validations. | ||
def update!(opts = {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can #update!
be called without any options? This seems to contradict what we did in #update
. It possibly makes the most sense to require the hash to have some attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code is well formed, but some interesting questions on design form. We may at least want to update examples.
# model.assign_attributes(id: 7, name: "Janeß") | ||
# model.id # => 7 | ||
# model.name # => "Jane" | ||
# model.persisted? # => false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, is this true? This should be a true
value if I understand persisted?
properly, and per the ActiveRecord documentation of the same. Is this a mistake?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might be thinking of the #dirty?
method, which would be false after saving, and true again after this operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I get this, we changed the key. A better example would NOT change the key, rather some non-key attributes.
# | ||
# model.update(name: "Janeß") | ||
# model.name # => "Jane" | ||
# model.persisted? # => true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're thinking of the dirty?
method here, which would be false since the attribute changes and save were concurrent. This also would benefit from an example where we are not changing the key.
end | ||
|
||
# Updates model attributes and validates new values | ||
# | ||
# You can use the +:force+ option to perform a simple put/overwrite |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually raises some interesting semantic questions. In ActiveRecord, they generally step around this because the ID field is a semi-hidden field. Where here, #update
could be a put_item
operation depending on the context. I don't think this is inherently wrong, but we either want to:
- Call this out more explicitly in documentation.
- Prevent the changing of key attributes in
#assign_attributes
, ensuring a pure update style.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it initially seems wrong for the keys to be mutable, the current implementation is consistent with what happens if the user manually changes the key value and performs a save.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you. I just think we should be explicit about this, because it has a chance to be a surprise to some users.
Description of changes:
Added:
destroyed?
new_record?
persisted?
update
update!
assign_attributes
Added unit tests to test this new functionality.
Performed integration tests to ensure no regression occurred.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.