-
Notifications
You must be signed in to change notification settings - Fork 323
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
Use nested path parameters from the parent when using build #318
base: master
Are you sure you want to change the base?
Conversation
f1f03a1
to
dff4af1
Compare
dff4af1
to
f404a9c
Compare
Rebased. |
While the example is probably the intended behavior it does not reflect the current request path. I've updated the example to more closely match the current experience. If one of the potential fixes like remi#318 is found to be acceptable it would make sense to include an update to the READ.me to reflect the changes.
While the example is probably the intended behavior it does not reflect the current request path. I've updated the example to more closely match the current experience. If one of the potential fixes like remi#318 is found to be acceptable it would make sense to include an update to the READ.me to reflect the changes.
4776753
to
f81ea98
Compare
I've rebased this, refactored it slightly, and added a much-needed caching feature. I was surprised it didn't do that already. |
Thank you @chewi. Will review and merge as soon as poss. Haven't looked at the code yet; how are you caching? I ask because I would prefer that we don't have loads of partial solutions to caching floating around. |
By caching, I mean that it sets the associated record using the setter method and by extension the |
I've realised that my understanding was a little off before. |
I just about had things working nicely until I realised it breaks with I have wanted to write my own version of Her for a long while now but I will never have time to do that so I'm trying to make the best of what we have. Can we please discuss some of the questionable design decisions like this one? |
Happy to discuss @chewi - what would be the alternative, and how could we do it? |
Since writing that, I've realised that service = Service.find(1)
customer = service.customers.build
customer.service # This should not refetch the service. I made this work by defining My point above still stands though. I would like to just use |
@chewi I would need to plough through the code to understand completely (sorry - didn't write this bit) but have you come across send_only_modified_attributes in the setup block? https://github.com/remiprev/her#dirty-attributes |
Yes, I do use class Service
has_many :customers
end
class Customer
belongs_to :service
end
service = Service.find(1)
customer = service.customers.build
customer.service.name = "bar"
customer.save # I wouldn't expect this to touch service. AR wouldn't in this case.
customer = Customer.find(1) # { name: "Bob", service: { name: "foo" } }
customer.service.name = "bar"
customer.save # I wouldn't expect this to touch service either. |
OK I see. Are these separate endpoints or nested data? I assume separate endpoints? |
Separate endpoints in my case but I'm trying to consider the bigger picture here. I want to make Her work better for everyone. |
I think I've finally got something workable now. I decided to exclude association data from build requests when the foreign key is present, e.g. if service_id is present then the service parameter will be excluded. It doesn't make much sense to send up association data for existing resources in a build request, even if it is modified. There is further scope to include new associations in a build request but this would need data_key remappings as well as conversions via to_params. I don't need this so someone else can pick that up. I'll give this stuff a bit more testing tomorrow before I push it up. |
f81ea98
to
040384e
Compare
Okay, here it is. This goes quite a but further than the original pull request but the changes are at least loosely related and submitting them separately would have been far more hassle. Obviously there are a lot of changes here so it might be easier to focus on the specs to better understand why they are necessary. |
IIRC this will conflict with the second commit of #298. This side should win but I can fix the conflict myself once you merge one of these. |
Really grateful for all your effort on this @chewi and the changeset looks like it's really useful. Paging @zacharywelch - I don't have a lot of headspace right now. Can you sign off on this? |
I've made some relatively small tweaks to this but I'll give them a bit more testing before I push up again. |
Sorry overlooked this. @chewi let us know when you're ready and I'll give it a review. Thanks! |
040384e
to
42cb5b9
Compare
Small tweaks ballooned into bigger changes. With the build requests working as I intended, some parameters used to build the path started appearing in the query string so I had to adjust Internal _ prefixed parameters found in nested resources were also leaking through so I had to ensure those were stripped out recursively rather than just at the top level. The biggest change is the new "autosave" option. I had previously put hacks into my own gem to work around the fact that Her would nest associated resources in save requests, seemingly at random. This option now gives you control over this behaviour. I called it autosave because it is similar to the option found in ActiveRecord, where The Travis failures are expected because #298 now needs to be merged first. |
Drat, after months of no issues and heavy use in production, my colleague ran into an infinite loop involving |
I’ve had this where an attribute is called ‘attributes’. Might not be related. I fixed up in a middleware.
Ed
… On 10 Apr 2018, at 16:21, James Le Cuirot ***@***.***> wrote:
Drat, after months of no issues and heavy use in production, my colleague ran into an infinite loop involving to_json. It looks very much like the earlier problem with inspect. Investigating...
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
|
It's not that. The new code does a better job of caching associated resources so you can easily end up with loops, hence why I had to fix this for I have since realised that it is my own code including the |
Fixed it by implementing something similar to what ActiveRecord has. I'll push it up tomorrow when we've tried it a bit more. |
Bah, the existing |
I've resolved that by removing the Her getter/setter method in favour of ActiveModel's class attribute. The class attribute does not combine the getter/setter like Her does so I had to add a compatibility fix, though maybe we could change the API to make it consistent. This would be I haven't verified it but while working on this, I noticed that other similar variables like def request_new_object_on_build?
@_her_request_new_object_on_build || (superclass.respond_to?(:request_new_object_on_build?) && superclass.request_new_object_on_build?)
end |
I had barrels of fun rebasing this one following #491 but got there in the end. My concerns above about |
9d528ca
to
8b31447
Compare
Rebased and dealt with the compatibility fix. It's quite nasty but it has to be because of how I had undo the "chop" change from #495 because it broke one of the tests. I believe this was actually a mistake as the method name will not always end with |
Good find @chewi 👏 I went ahead and took care of the |
8b31447
to
c98a62c
Compare
c98a62c
to
31547a0
Compare
Nested attributes may be carrying these too. Also speed up the check by not using a regular expression while we're at it.
This makes it possible to take remedial action such as copying the parameters from the parent. The method signature for build_request_path has been simplified. The path argument was confusing and only used in one place to force the collection path so a :collection option has been implemented instead. This new options hash also implements :remove_used, which is useful for build requests where you want to avoid parameters in the path being duplicated in the query string.
Just like ActiveRecord, this will modify the foreign key value and cache the resource, avoiding a needless lookup later. The use_setter_methods logic is a little convoluted and I tried to improve it but this quickly turned into a can of worms.
When building against a has_one or has_many association, first check for a specified inverse association, then check for a belongs_to matching the resource name, and finally fall back to foo_id as before. The first two approaches allow the resource to be cached.
It's very confusing and needs restructuring for the next commit.
Also cache the parent resource for has_one as this was previously only being done for has_many.
Following my earlier changes, some slightly buggy code in one of my applications triggered this. The problem went away once the code was corrected but it is obviously worth avoiding this to start with.
This new association option is similar to the option of the same name found in ActiveRecord. When true, associated resources are always nested in a parent save. When false, they are never nested. When nil, only new resources are nested. Unlike ActiveRecord, the default is true (rather than nil) in order to preserve existing behaviour. This is also subject to whether send_only_modified_attributes is true or not. When true, only modified resources will be sent, even if autosave is true. Unlike before, it won't do strange things like fetch an existing has_one resource when you assign a new one because of change tracking. 4 of the specs currently fail because they require the fixes in my fix-change-tracking branch.
Arrays added to the hash are always duplicated. This was fixed in version 4.
This makes as_json and to_json behave like they do under ActiveRecord, excluding associations unless they are explicitly included. This is necessary to avoid infinite loops. Unfortunately this caused Her's include_root_in_json code to collide with ActiveModel's include_root_in_json code. I removed Her's getter/setter method in favour of ActiveModel's class attribute but also provided a compatibility fix.
We previously added serialization for ActiveModel::Serialization instances but Her::Model instances can still be left untouched if they do not belong to an association. In order to serialize these safely, we have to move this block of code into embed_params!, which guards against infinite loops. This has the added benefit of not doing unnecessary serialization when send_only_modified_attributes is enabled.
31547a0
to
80d3ae5
Compare
This removes a TODO! 😀