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

Custom ransack attributes appear to be being reset after configuration #3706

Closed
tmtrademarked opened this issue Jul 14, 2020 · 9 comments
Closed

Comments

@tmtrademarked
Copy link
Contributor

Following the guide for customizing search fields, I have been trying to add a search field for a custom field on a model. When I follow the steps, though, it seems like the whitelisted_ransackable_attributes are being reset somewhere along the line after the configuration is applied.

Solidus Version:
Solidus: 2.10.0
Rails: 6.0.3.2
Ruby: 2.6.6

To Reproduce
I added a new field to my product model called product_type, and wanted to add an admin field to allow searching by type. After adding an entry to config/initializers/spree.rb, I can see the following behavior on application launch:

From: /Volumes/android/agora/config/initializers/spree.rb:85 :

    80: 
    81: Spree.user_class = "Spree::User"
    82: 
    83: # Custom ransackable attributes are defined here.
    84: binding.pry
 => 85: Spree::Product.whitelisted_ransackable_attributes << 'product_type'
    86: 
    87: # Rules for avoiding to store the current path into session for redirects
    88: # When at least one rule is matched, the request path will not be stored
    89: # in session.
    90: # You can add your custom rules by uncommenting this line and changing

[1] pry(main)> Spree::Product.whitelisted_ransackable_attributes
[
  "name",
  "slug"
]
[2] pry(main)> next

From: /Volumes/android/agora/config/initializers/spree.rb:96 :

    91: # the class name:
    92: #
    93: # Spree::UserLastUrlStorer.rules << 'Spree::UserLastUrlStorer::Rules::AuthenticationRule'
    94: 
    95: # Subscribe handlers to events as necessary.
 => 96: ProductCatalog::EventSubscriber.subscribe!

[2] pry(main)> Spree::Product.whitelisted_ransackable_attributes
[
  "name",
  "slug",
  "product_type"
]

... which looks like the configuration has been applied.

When adding my custom field product_type to an admin field, I added the following deface override:

Deface::Override.new(
  virtual_path: 'spree/admin/products/index',
  name: 'product_search_filter',
  replace: "[data-hook='admin_products_index_search']",
  text: "
   <... elided for brevity>
    <div class=\"col-2\">
      <div class=\"field\">
        <%= f.select :product_type_eq, Spree::Product.product_types.keys.map { |t| [t.humanize, t] }, include_blank: true %>
      </div>
    </div>
  "
)

If I put a breakpoint in the controller before the view is rendered, I can see that the ransackable attributes of the Product object have reverted back to the default. In fact, adding a dirty hack to the controller when the product collection is loaded appears to be a workaround, but seems like it shouldn't be necessary:

def collection
  Spree::Product.whitelisted_ransackable_attributes << 'product_type'
  super
end

Current behavior
When loading the products admin page, I get the following ActionView error:

ActionView::Template::Error (undefined method `product_type_eq' for #<Ransack::Search:0x00007fc0afdb2af0>):
    42: 
    43:     <div class="col-2">
    44:       <div class="field">
    45:         <%= f.select :product_type_eq, Spree::Product.product_types.keys.map { |t| [t.humanize, t] }, include_blank: true %>
    46:       </div>
    47:     </div>
    48:   

Expected behavior
I would have expected the config initializer to persist across the lifetime of the application rather than being reloaded.

@kennyadsl
Copy link
Member

Hey @tmtrademarked, thanks for reporting. Curious if this also happens in production where eager_load is enabled, just to be sure it's a code reloading issue, probably coming from Rails 6. Ah, are you using Zeitwerk or the classic autoloader?

@tmtrademarked
Copy link
Contributor Author

Good point - let me try to check that out. We're using zeitwerk in this application, which may be one of the relevant differences here.

@tmtrademarked
Copy link
Contributor Author

tmtrademarked commented Jul 15, 2020

Ah - in production, it seems like this works as expected. I was able to modify the initializer and have the change take effect as expected. I wonder if this has to do with the implementation of this feature as a class method and the interaction between zeitwerk and the class itself. (I'm hazy on the details of the new zeitwerk framework, but maybe it doesn't reload the initializers as previously expected?)

@kennyadsl
Copy link
Member

Yeah, I think that's the issue. I think it can be solved by adding a product decorator to the project, following the Zeitwerk expectations in terms of filename and content and it will be reloaded at each code reload (each request).

@tmtrademarked
Copy link
Contributor Author

I'm not totally sure what you mean - you mean in a product decorator (say, app/models/spree/product_decorator.rb) invoking the Spree::Product::whitelisted_ransackable_attributes code? It seems like that would potentially interfere with the code in the initializer, right? Or is the suggestion to only configure this via the decorator?

@kennyadsl
Copy link
Member

I mean to configure it only with the decorator.

What I mean with "Zeitwerk expectations" is to have a file and a module/class inside that follow the same naming pattern, like:

# app/decorators/your_app/spree/product/whitelist_raskackable_attributes.rb

module YourApp
  module Spree
    module Product
      module WhitelistRansackableAttributes
        # your code here

        Spree::Product.prepend self
      end
    end
  end
end

Otherwise it won't get considered by Zeitwerk.

@tmtrademarked
Copy link
Contributor Author

I gave that approach a try - and can confirm that it works as expected, which makes sense. I guess the only question here is whether we should update the documentation to make this more obvious?

@kennyadsl
Copy link
Member

Yes, that would be awesome. And I think it will work even prior Rails 6 and Zeitwerk so 👍. If you have time to submit a PR, it would be welcome. Thanks!

tmtrademarked added a commit to tmtrademarked/solidus that referenced this issue Jul 15, 2020
This PR updates the documentation around customizing Ransack attributes to recommend the use of a decorator instead of an initializer. This allows the code to reload correctly when using Zeitwerk under Rails 6+.

Addresses solidusio#3706
@tmtrademarked
Copy link
Contributor Author

Sounds great - opened #3709 to address this. Thanks for the help here!

DanielePalombo pushed a commit to DanielePalombo/solidus that referenced this issue Oct 30, 2020
This PR updates the documentation around customizing Ransack attributes to recommend the use of a decorator instead of an initializer. This allows the code to reload correctly when using Zeitwerk under Rails 6+.

Addresses solidusio#3706
mamhoff pushed a commit to mamhoff/solidus that referenced this issue Feb 1, 2021
This PR updates the documentation around customizing Ransack attributes to recommend the use of a decorator instead of an initializer. This allows the code to reload correctly when using Zeitwerk under Rails 6+.

Addresses solidusio#3706
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

No branches or pull requests

2 participants