-
Notifications
You must be signed in to change notification settings - Fork 109
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
Allow transformers to be included across views #372
Conversation
Signed-off-by: bhooshiek <[email protected]>
Signed-off-by: Nate Baer <[email protected]>
Signed-off-by: Nate Baer <[email protected]>
…of replace them. - Enhance view collection test suite and address comments. Signed-off-by: Nate Baer <[email protected]>
…f there are no view transformers. Signed-off-by: Nate Baer <[email protected]>
lib/blueprinter/view_collection.rb
Outdated
all_transformers = included_transformers.concat(views[:default].view_transformers).uniq | ||
all_transformers.empty? ? Blueprinter.configuration.default_transformers : all_transformers |
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 make a distinction between global default transformers and transformers of the default view.
According to this section of the README, global default transformers should only be used if there are no other transformers. That's fine, and I've moved that logic here.
But I don't believe transformers of the default view should be treated in the same way, which I've changed from how #363 handles it. Instead, transformers of the default view should be merged with the views' other transformers. This is the behavior I would expect, since fields on the default view are merged with the views' other fields.
I've added tests for both of these situations.
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 is an edge case, but in what order should the transformers be applied? I would expect ones from the default
view first, followed by included views, and finally any from the final view. Looks like this might apply the default view ones last. 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.
@njbbaer Currently, the default views transformer is not applied when calling with a specific view. Do we need to change this behavior?
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.
@jhollinger I prefer from last to first in views because that provides easy tracing of transformers for the end implementer. If we taking that route can we also follow the same for default too?
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.
@bhooshiek-narendiran I may not have defined my terms well enough. I was talking about "transformers from the default view", as opposed to "the default transformers configuration setting". I think @njbbaer's approach of ignoring "the default transformers configuration setting" if any transformers are defined in the Blueprint is reasonable. All discussion of "default" below refers to "transformers from the default view".
Allow me to amend my comment. In the PR's current code, when transformers are present, they appear to be applied in this order:
- Transformers from intermediate views (top-down)
- Transformers from the named view
- Transformers from :default view
That ordering strikes me as arbitrary, and I could see it leading to confusion in some edge cases. IMHO a much more predictable ordering would be "descending":
- Transformers from :default view
- Transformers from intermediate views (top-down)
- Transformers from the named view
@bhooshiek-narendiran suggested the reverse of that order, which I also think would be fine:
- Transformers from the named view
- Transformers from intermediate views (bottom-up)
- Transformers from :default view
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.
Thanks @jhollinger, that's an important point. I'm inclined towards the "descending" order, which gives named views the opportunity to have the final say on their rendering, so to speak.
@bhooshiek-narendiran can you elaborate on why you prefer the reverse?
Currently, the default views transformer is not applied when calling with a specific view. Do we need to change this behavior?
Yes, we want to change this behavior so that it's easier to apply transformers more broadly. When a field is added to the default view it is applied to all of the named views, likewise a transformer added to the default view should also be applied in addition to the named views.
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.
@njbbaer @jhollinger Apologize I also meant the same thing.
class Sample
transformer_view_default
view_1 do
transformer_1
end
view_2 do
transformer_2
end
view_3 do
transformer_3
end
end
Execution order of transformer =>transformer_3,transformer_2,transformer_1
Do we need to merge transformer_view_default along with views transformer or only during views transformer not present.
But Adding it to first or last both seems to be a good addition.
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.
@jhollinger @bhooshiek-narendiran
I've made the PR changes described, so the execution order is now "top-down".
@bhooshiek-narendiran
Not quite. In your example (without include
statements) each view would execute these transformers in this order:
view_1
=> transformer_view_default
, transformer_1
view_2
=> transformer_view_default
, transformer_2
view_3
=> transformer_view_default
, transformer_3
Here's a more complete example, which uses included views:
class Sample
transform TransformViewDefault
view :view_1 do
transform Transformer1
end
view :view_2 do
transform Transformer2
include :view_1
end
view :view_3 do
transform Transformer3
include :view_2
end
end
view_1
=> TransformViewDefault
, Transformer1
view_2
=> TransformViewDefault
, Transformer1
, Transformer2
view_3
=> TransformViewDefault
, Transformer1
, Transformer2
, Transformer3
The idea is that the "more specific" transformers get executed later, so they can get the last word on their rendering.
Signed-off-by: Nate Baer <[email protected]>
def transformers | ||
view_transformers.empty? ? Blueprinter.configuration.default_transformers : view_transformers | ||
end | ||
|
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 logic has been moved inside of ViewCollection
since we need to aggregate the included transformers before deciding whether to apply the global default transformers. See #372 (comment) for details.
- Minor textual changes to view collection spec. Signed-off-by: Nate Baer <[email protected]>
@@ -89,5 +91,14 @@ def add_to_ordered_fields(ordered_fields, definition, fields, view_name_filter = | |||
ordered_fields[definition.name] = fields[definition.name] | |||
end | |||
end | |||
|
|||
def gather_transformers_from_included_views(view_name) |
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.
nit: Should we make this private
?
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.
Isn't it already?
spec/spec_helper.rb
Outdated
require 'pry' | ||
require 'json' |
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.
Are these require
's necessary?
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 hope not! :)
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 added require 'json'
here because without it many spec files fail when run individually (i.e. bundle exec rspec spec/units/view_spec.rb
) because JSON is uninitialized. The test suite works currently when run in its entirety because of the order of the specs. When we use active record in the integration tests, which run first, it imports JSON and allows the subsequent tests to pass. Is it a bad idea to import that dependency in the spec helper?
I added require 'pry'
simply for the convenience of debugging, but I understand if that's superfluous.
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.
Looks good, but I have some thoughts on the ordering of transformers.
lib/blueprinter/view_collection.rb
Outdated
all_transformers = included_transformers.concat(views[:default].view_transformers).uniq | ||
all_transformers.empty? ? Blueprinter.configuration.default_transformers : all_transformers |
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 is an edge case, but in what order should the transformers be applied? I would expect ones from the default
view first, followed by included views, and finally any from the final view. Looks like this might apply the default view ones last. Thoughts?
Others have good questions will wait for those to be addressed.
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.
small comments
spec/spec_helper.rb
Outdated
require 'pry' | ||
require 'json' |
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 hope not! :)
spec/units/view_collection_spec.rb
Outdated
# frozen_string_literal: true | ||
|
||
describe 'ViewCollection' do | ||
let(:view_collection) { Blueprinter::ViewCollection.new } |
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.
could we use subject
here?
let!(:default_view) { view_collection[:default] } | ||
let!(:view) { view_collection[:view] } |
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 are we using let!
here?
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.
When a view collection tries to fetch a view that doesn't exist it creates it, which is what we're doing here. We need that view to exist for our tests, but don't always call view
directly, so it won't be initialized unless we use let!
.
Signed-off-by: Nate Baer <[email protected]>
Signed-off-by: Nate Baer <[email protected]>
Signed-off-by: Nate Baer <[email protected]>
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.
👍
Allows transformers to be included by other views.
Branched off of #363 by @bhooshiek-narendiran and resolves #225.
Checklist: