administrate_ransack icon indicating copy to clipboard operation
administrate_ransack copied to clipboard

Support all ransack predicates

Open goosys opened this issue 2 years ago • 5 comments

I made the following changes to enable the use of Ransack's extensive predicates expressions, in addition to ransackable_scope. Please review and let me know if any further changes are needed.


Changes

  • Add AdministrateRansack.ransack? helper to check if a field name with predicates is valid.
  • Fix the labels to enable automatic translation of field names with predicates in i18n.

Breaking changes

  • Drop admin_scope option
    • For associations, admin_scope is no longer necessary as updated to use Field.associated_resource to get the collection.
    • If we want to do something similar, we can do it with Field::BelongsTo like this:
      • Field::BelongsTo.with_options(scope: ->{ Post.published })
    • Since Field::HasMany does not have a scope option, it cannot be done in the same way, but it can be achieved using the Field::ScopedHasMany plugin.
  • Drop admin_label option
    • For associations, use the value of display_resource as the label to match the title or heading of each admin pages.

goosys avatar Oct 27 '23 11:10 goosys

Hey @goosys Sorry for the loong delay on this PR. I came back only recently to this project.

Thank you very much for your contribution here. But I prefer to avoid merging this PR for a couple of reasons:

  • it contains breaking changes (the removal of admin_label) (which can be fine but they will need to wait a major version, plus I prefer to have them isolated in a specific PR);
  • it contains changes of different kind, I usually try to make the scope as small as possible for maintainability and clean history.

Since it passed quite some time, I'll evaluate to create specific PRs based on this one :+1:

blocknotes avatar Apr 19 '25 07:04 blocknotes

I also think the general idea is good, though I believe some of the details still need further discussion.
For example, there's quite a bit of logic in the view files right now, and I think we should try to separate that more cleanly.

Administrate still supports Rails 6.0, but since this gem has already dropped it, we could consider using Renderable Object from Rails 6.1 to encapsulate the logic. What do you think?

goosys avatar Apr 30 '25 17:04 goosys

I also think the general idea is good, though I believe some of the details still need further discussion. For example, there's quite a bit of logic in the view files right now, and I think we should try to separate that more cleanly.

Administrate still supports Rails 6.0, but since this gem has already dropped it, we could consider using Renderable Object from Rails 6.1 to encapsulate the logic. What do you think?

Those fields' view does their job for me, but any improvement could be nice (if proposed in a specific PR).

Do you mean having something similar to view components? My fear using that approach is related to the complexity of the views, for example I wonder how a view like app/views/administrate_ransack/components/_field_has_many.html.erb could become.

blocknotes avatar May 01 '25 20:05 blocknotes

The ViewComponent library is separate from Rails itself — Rails only provides support for Renderable objects.
Thanks to the render_in method, we can now pass objects that implement it directly to the render helper.
This allows us to encapsulate business logic within a Renderable object instead of putting it in the view.

Here’s a simplified example. A lot is omitted, but this is the general idea:
By moving business logic out of the view files, the views themselves become much cleaner and easier to maintain.

# lib/administrate_ransack/view/field/base.rb
module AdministrateRansack
  module View
    module Field
      class Base
        def initialize(); end
        def render_in(view_context); end
        def render?; end
        private
        def ransack?; end

# lib/administrate_ransack/view/field/has_many.rb
module AdministrateRansack
  module View
    module Field
      class HasMany < Base
        def render_in(view_context)
          @view_context = view_context
          return unless render?
          view_context.render template_name, local_assigns
        end
        def render?
          super && model.reflections[field.to_s]
        end
        private
        def template_name
          "components/field_has_many"
        end
        def local_assigns
          {
            form: @form,
            field_key: field_key,
            resource_field: resource_field,
            collection: collection
          }
        end
        ...
<%# app/views/administrate_ransack/_filters.html.erb %>
...
<% component = AdministrateRansack::FILTERS[input_type] || 'field_other' %>
<% view_component =  <<Some logics to find view class like `AdministrateRansack::View::Field::HasMany`>> %>
<div class="filter filter-...">
  <%= render view_component.new(
        form: f, model: model, field: field, label: label, type: type, options: options[field]
  ) %>
<%# app/views/administrate_ransack/components/_field_has_many.html.erb %>
<%= form.label(label || field_key, class: 'filter-label') %>
<%= form.select(field_key, collection, {}, multiple: true) %>

What do you think? Do you think it's worth discussing further?
If it seems promising, I'd be happy to open a dedicated issue and PR for it — would you be willing to take a look if I do?

goosys avatar May 02 '25 07:05 goosys

What do you think? Do you think it's worth discussing further? If it seems promising, I'd be happy to open a dedicated issue and PR for it — would you be willing to take a look if I do?

Yep, the approach is similar to view components.

I'll evaluate better your proposal, but to be honest I'm thinking to go in a different direction at the moment. Recently I started to work to a major refactoring, here is a preview: https://github.com/blocknotes/administrate_ransack/pull/44/files

In this way I'll be able to have simpler views and more freedom on the filters customization:

  RANSACK_SEARCH = {
    title: {
      type: :string,
      param: :title_cont
    },
    published: :boolean,
    position: :number,
    by_category: {
      type: :string,
      scope: true
    },
    dt: :date,
  }

I do not exclude the possibility to use renderable / view components / Phlex in the future.

blocknotes avatar May 04 '25 13:05 blocknotes