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

Allow transformers to be included across views #372

Merged
merged 14 commits into from
Jan 12, 2024
Merged

Conversation

njbbaer
Copy link
Contributor

@njbbaer njbbaer commented Dec 26, 2023

Allows transformers to be included by other views.

Branched off of #363 by @bhooshiek-narendiran and resolves #225.

  • Refactored logic for gathering included transformers.
  • Added a View Collection tests suite.
  • Fixed handling of global default transformers and transformers of the default view.

Checklist:

  • I have updated the necessary documentation
  • I have signed off all my commits as required by DCO
  • My build is green

@njbbaer njbbaer self-assigned this Dec 26, 2023
…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]>
Comment on lines 39 to 40
all_transformers = included_transformers.concat(views[:default].view_transformers).uniq
all_transformers.empty? ? Blueprinter.configuration.default_transformers : all_transformers
Copy link
Contributor Author

@njbbaer njbbaer Dec 27, 2023

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.

Copy link
Contributor

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?

Copy link
Contributor

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?

Copy link
Contributor

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?

Copy link
Contributor

@jhollinger jhollinger Jan 8, 2024

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:

  1. Transformers from intermediate views (top-down)
  2. Transformers from the named view
  3. 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":

  1. Transformers from :default view
  2. Transformers from intermediate views (top-down)
  3. Transformers from the named view

@bhooshiek-narendiran suggested the reverse of that order, which I also think would be fine:

  1. Transformers from the named view
  2. Transformers from intermediate views (bottom-up)
  3. Transformers from :default view

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

@njbbaer njbbaer Jan 9, 2024

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.

Comment on lines -19 to -22
def transformers
view_transformers.empty? ? Blueprinter.configuration.default_transformers : view_transformers
end

Copy link
Contributor Author

@njbbaer njbbaer Dec 27, 2023

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.

@njbbaer njbbaer changed the title Allow transformers to be included by other views Allow transformers to be included across views Dec 27, 2023
- Minor textual changes to view collection spec.

Signed-off-by: Nate Baer <[email protected]>
@njbbaer njbbaer marked this pull request as ready for review December 27, 2023 07:52
@njbbaer njbbaer requested review from ritikesh and a team as code owners December 27, 2023 07:52
jmeridth
jmeridth previously approved these changes Dec 27, 2023
@@ -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)
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it already?

Comment on lines 5 to 6
require 'pry'
require 'json'
Copy link
Contributor

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?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope not! :)

Copy link
Contributor Author

@njbbaer njbbaer Jan 8, 2024

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.

Copy link
Contributor

@jhollinger jhollinger left a 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.

Comment on lines 39 to 40
all_transformers = included_transformers.concat(views[:default].view_transformers).uniq
all_transformers.empty? ? Blueprinter.configuration.default_transformers : all_transformers
Copy link
Contributor

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?

@jmeridth jmeridth dismissed their stale review January 7, 2024 21:19

Others have good questions will wait for those to be addressed.

Copy link

@m-graf m-graf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small comments

Comment on lines 5 to 6
require 'pry'
require 'json'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope not! :)

# frozen_string_literal: true

describe 'ViewCollection' do
let(:view_collection) { Blueprinter::ViewCollection.new }
Copy link

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?

Comment on lines +6 to +7
let!(:default_view) { view_collection[:default] }
let!(:view) { view_collection[:view] }
Copy link

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?

Copy link
Contributor Author

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!.

Copy link
Contributor

@jhollinger jhollinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@njbbaer njbbaer merged commit 8b715cc into main Jan 12, 2024
12 checks passed
@njbbaer njbbaer deleted the nb/nested_transformers branch January 12, 2024 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Not able to use transformer when it is in included view
6 participants