Giter Club home page Giter Club logo

administrate_ransack's Introduction

Administrate Ransack

gem version gem downloads linters specs specs Rails7

A plugin for Administrate to use Ransack for filtering resources.

Features:

  • add Ransack search results using module prepend inside an Administrate controller;
  • offer a filters partial based on the resource's attributes;
  • customize searchable attributes.

Installation

  • After installing Administrate, add to Gemfile: gem 'administrate_ransack' (and execute bundle)
  • Edit your admin resource controller adding inside the class body:
prepend AdministrateRansack::Searchable
  • Add to your resource index view (ex. to generate the index for a Post model: bin/rails generate administrate:views:index Post):
<%= render('administrate_ransack/filters') %>
  • See the Usage section for extra options

Usage

  • The filters partial accepts some optional parameters:
    • attribute_labels: hash used to override the field labels, ex. { title: "The title" }
    • attribute_types: hash used to specify the filter fields, ex. { title: Administrate::Field::String }
    • search_path: the path to use for searching (form URL)
    • namespace: the namespace used by Administrate, ex. :supervisor
  • For associations (has many/belongs to) the label used can be customized adding an admin_label method to the target model which returns a string while the collection can by filtered with admin_scope. Example:
# Sample post model
class Post < ApplicationRecord
  scope :admin_scope, -> { where(published: true) }

  def admin_label
    title.upcase
  end
end
  • For has_many associations it is possible to use Selectize in place of the checkboxes using:
<!-- Set options for an association named: tags -->
<%= render('administrate_ransack/filters', options: { tags: 'select' } ) %>
  • To use scopes in filters it's needed to update also the ransackable_scopes in the model, example:
# Sample post model
class Post < ApplicationRecord
  scope :recents, ->(dt = 1.month.ago) { where('dt > ?', dt).order(dt: :desc) }
  scope :by_category, ->(category) { where(category: category) }

  class << self
    def ransackable_scopes(_auth_object = nil)
      %i[by_category recents]
    end
  end
end
<!-- Sample index view -->
<%= render(
  'administrate_ransack/filters',
  attribute_types: { recents: Administrate::Field::DateTime, by_category: Administrate::Field::String }
) %>

Notes

  • Administrate Search logic works independently from Ransack searches, I suggest to disable it eventually (ex. overriding show_search_bar? in the controller or removing the bar from the view)
  • Date/time filters use Rails datetime_field method which produces a datetime-local input field, at the moment this type of element is not broadly supported, a workaround is to include flatpickr datetime library.
    • This gem checks if flatpickr function is available in the global scope and applies it to the datetime-local filter inputs;
    • you can include the library using your application assets or via CDN, ex. adding to app/views/layouts/admin/application.html.erb:
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/flatpickr.min.css">
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/flatpickr.min.js"></script>

  <script>
    // optionally change the flatpikr options:
    window.flatpickr_filters_options = { dateFormat: "Y-m-d" };
  </script>

Customizations

  • Ransack options can be customized defining a ransack_options method in the controller, example:
module Admin
  class PostsController < Admin::ApplicationController
    prepend AdministrateRansack::Searchable

    def ransack_options
      # raises an exception on unknown parameters
      { ignore_unknown_conditions: false }
    end
  end
end
  • Sample call of the filters partial with different options provided:
<%
# In alternative prepare an hash in the dashboard like RANSACK_TYPES = {}
attribute_types = {
  title: Administrate::Field::String,
  author: Administrate::Field::BelongsTo,
  category: Administrate::Field::Select.with_options(collection: Post.categories.to_a),
  published: Administrate::Field::Boolean
}
attribute_labels = {
  author: 'Written by',
  title: nil
}
%>
<%= render(
  'administrate_ransack/filters',
  attribute_types: attribute_types,
  attribute_labels: attribute_labels,
  search_path: admin_root_path
) %>
  • Another option is to prepare some hashes constants in the dashboard (ex. RANSACK_TYPES):
<%= render('administrate_ransack/filters', attribute_types: @dashboard.class::RANSACK_TYPES) %>

Sample styles

  • Some basic style to setup the filters as a sidebar (see the screenshot below):
.main-content__body {
  display: inline-block;
  width: calc(100% - 320px);
  vertical-align: top;
}

[data-administrate-ransack-filters] {
  display: inline-block;
  padding-left: 10px;
  padding-top: 10px;
  width: 300px;
}

[data-administrate-ransack-filters] .filter {
  margin-bottom: 10px;
}

[data-administrate-ransack-filters] .filters-buttons {
  margin-top: 30px;
}

Screenshot: screenshot

  • Alternative styles for an horizontal search bar:
[data-administrate-ransack-filters] {
  border: 1px solid #ddd;
  padding: 10px;
  text-align: center;
}

[data-administrate-ransack-filters] .filters {
  display: inline-block;
}

[data-administrate-ransack-filters] .filter, [data-administrate-ransack-filters] .filter > label {
  display: inline-block;
}

[data-administrate-ransack-filters] .filter > input {
  display: inline-block;
  width: auto;
}

[data-administrate-ransack-filters] .filters-buttons {
  display: inline-block;
  margin-left: 20px;
}

Screenshot: screenshot2

Extra notes

  • If you need to define custom search logics you can skip prepending the module (AdministrateRansack::Searchable) and create your own search query in a controller (but you need to assign the Ransack search object to @ransack_results for the filters partial), for example:
  def scoped_resource
    @ransack_results = super.ransack(params[:q])
    @ransack_results.result(distinct: true)
  end
  • Sometimes it's easier to create a new Ransack field than overriding the search logic (there are a lot of good examples in the Ransack Wiki), example to search in a jsonb field adding to a Post model:
  ransacker :keywords do
    Arel.sql("posts.metadata ->> 'keywords'")
  end
  • With Administrate Ransack you can easily create links to other resources applying some filters, example to add in a tag show page the link to the related posts:
  <%= link_to("Tag's posts", admin_posts_path('q[tags_id_in][]': page.resource.id), class: "button") %>

Do you like it? Star it!

If you use this component just star it. A developer is more motivated to improve a project when there is some interest.

Or consider offering me a coffee, it's a small thing but it is greatly appreciated: about me.

Contributors

  • Mattia Roccoberton: author
  • The good guys that opened issues and pull requests from time to time

License

The gem is available as open source under the terms of the MIT License.

administrate_ransack's People

Contributors

blocknotes avatar mguidetti avatar zacharydanger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

administrate_ransack's Issues

undefined method `amount_eq' for Ransack::Search

Hi! I just tried the gem but there is an issue with it:

undefined method amount_eq' for Ransack::Search<class: Deposit, base: Grouping <combinator: and>>:Ransack::Search raised from <%= render('administrate_ransack/filters') %> .

I followed the installation guide, Is there something more I need to do?

Pass the auth_object to ransack

Hi, Thank you for this nice gem.

I also use ransack outside of Administrate, so I would like to be able to pass the auth_object to ransack.

# lib/administrate_ransack/searchable.rb
module AdministrateRansack
  module Searchable
    def scoped_resource
      @ransack_results = super.ransack(params[:q], auth_object: set_ransack_auth_object)
      @ransack_results.result(distinct: true)
    end

    def set_ransack_auth_object; end

Alternatively, for extensibility, I would like to be able to pass all options, not just the auth_object.

    def scoped_resource
      @ransack_results = super.ransack(params[:q], ransack_options)
      @ransack_results.result(distinct: true)
    end

    def ransack_options; end

Thank you.

Take advantage of administrate "selectize" usage

Another suggestion would be to reuse the selectize js lib within this gem for select fields.
Maybe we could think about an option to activate / deactivate selectize per field and/or globally.

And the option could be activated by default.
What do you think?

Choose between checkboxes or multiple select for has_many

Hey, first, thanks for this gem!

I must say it works pretty well!

I was thinking, would it be difficult to add an option to decide if we want to use checkboxes or a "select multiple" for a has_many association?
I'd rather use the select multiple when I have a lot of records to display.

What do you think?

Use of Enum

Hello. Thank you very ,\much for making this gem available to the community.

There is no examples how to use this gem with Enums and I was trying to use it with the field Select without any success. Is there a way for you to include some examples on how to use this gem in conjunction with enums and how to use the Select field without a has_many relationship?

My actual code.

# app/models/project.rb
class Project < ApplicationRecord
  enum status: { enabled: 1, disabled: 0 }
  ransacker :status, formatter: proc {|v| statuses[v]}
end
# index.html.erb
attribute_labels = {
    status: "Estatus",
  }

  attribute_types = {
    status: Administrate::Field::Select
  }
<%= render('administrate_ransack/filters',
           attribute_types: attribute_types, attribute_labels: attribute_labels) %>

But that gives me this error:

Showing /Users/ruan/.gem/gems/administrate_ransack-0.4.0/app/views/administrate_ransack/components/_field_select.html.erb where line #4 raised:

undefined method `options' for Administrate::Field::Select:Class

On the dashboard I am using a EnumFiedl

#project_dashboard.rb
class ProjectDashboard < Administrate::BaseDashboard
  ATTRIBUTE_TYPES = {
    id:                        Field::Number,
    title:                     Field::String,
    status:                    EnumField,
  }.freeze

  COLLECTION_ATTRIBUTES = %i[
    id
    title
    status
  ].freeze


  SHOW_PAGE_ATTRIBUTES = %i[
    id
    title
    status
  ].freeze

  FORM_ATTRIBUTES = %i[
    title
    status
  ].freeze

end
# app/fields/enum_field.rb
class EnumField < Administrate::Field::Base
  delegate :to_s, to: :data

  def select_field_values(form_builder)
    class_name = form_builder.object.class.to_s.downcase
    form_builder.object.class.public_send(attribute.to_s.pluralize).keys.map do |v|
      [I18n.t("enums.#{class_name}.#{self.attribute.to_s}.#{v}"), v]
    end
  end


  def translate(resource = nil)
    return I18n.t("enums.#{resource}.#{@attribute}.#{@data}") if resource
    @data
  end
end

Thank you

Can not work with Rails7.1

Application crashed at start with info:

! Unable to load application: LoadError: cannot load such file -- polyamorous/activerecord_7.1_ruby_2/join_association
15:44:39 web.1 | /Users/raykin/.rbenv/versions/3.3.0/lib/ruby/3.3.0/bundled_gems.rb:74:in `require': cannot load such file -- polyamorous/activerecord_7.1_ruby_2/join_association (LoadError)

Maybe it's because ransack dependency block?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.