From 992ca7887b0838ae520c6f9d570a9183357b0dd4 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 2 Nov 2024 22:00:18 +0100
Subject: [PATCH 001/157] Refactor

---
 lib/datagrid/form_builder.rb | 41 +++++++++++++++++++-----------------
 1 file changed, 22 insertions(+), 19 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index d3c6a3f..02cfc48 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -24,7 +24,7 @@ def datagrid_label(filter_or_attribute, text = nil, **options, &block)
     end
 
     # @visibility private
-    def datagrid_filter_input(attribute_or_filter, **options)
+    def datagrid_filter_input(attribute_or_filter, **options, &block)
       filter = datagrid_get_filter(attribute_or_filter)
       value = object.filter_value_as_string(filter)
       type = options[:type]&.to_sym
@@ -33,19 +33,33 @@ def datagrid_filter_input(attribute_or_filter, **options)
         options[:value] = ""
       end
       if type == :"datetime-local"
-        datetime_local_field filter.name, **options
+        datetime_local_field filter.name, **options, &block
       elsif type == :"date"
-        date_field filter.name, **options
+        date_field filter.name, **options, &block
       elsif type == :textarea
-        text_area filter.name, value: value, **options, type: nil
+        text_area filter.name, value: value, **options, type: nil, &block
+      elsif type == :select
+        select(
+          filter.name,
+          object.select_options(filter) || [],
+          {
+            include_blank: filter.include_blank,
+            prompt: filter.prompt,
+            include_hidden: false
+          },
+           multiple: filter.multiple?,
+           **options,
+           type: nil,
+           &block
+        )
       else
-        text_field filter.name, value: value, **options
+        text_field filter.name, value: value, **options, &block
       end
     end
 
     protected
-    def datagrid_extended_boolean_filter(filter, options = {})
-      datagrid_enum_filter(filter, options)
+    def datagrid_extended_boolean_filter(filter, options = {}, &block)
+      datagrid_filter_input(filter, **options, type: :select, &block)
     end
 
     def datagrid_boolean_filter(filter, options = {})
@@ -82,18 +96,7 @@ def datagrid_enum_filter(filter, options = {}, &block)
           }
         )
       else
-        select(
-          filter.name,
-          object.select_options(filter) || [],
-          {
-            include_blank: filter.include_blank,
-            prompt: filter.prompt,
-            include_hidden: false
-          },
-           multiple: filter.multiple?,
-           **options,
-           &block
-        )
+        datagrid_filter_input(filter, **options, type: :select, &block)
       end
     end
 

From a3daafcd9113adad75c68f201fa516a699b37f75 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 10:09:13 +0100
Subject: [PATCH 002/157] Add debugger

---
 Gemfile | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Gemfile b/Gemfile
index 4a624e7..aed7bd1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,6 +9,7 @@ group :development do
   gem "csv" # removed from standard library in Ruby 3.4
   gem "nokogiri" # used to test html output
   gem "pry-byebug"
+  gem 'debug'
   gem "rspec"
   gem "sequel"
   gem "sqlite3", "~> 1.7.0"

From f3337caf86402000b7869443eba7c8ec926fbb06 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 10:34:39 +0100
Subject: [PATCH 003/157] Migrate to rails-dom-testing

---
 Gemfile                      | 1 +
 lib/datagrid/form_builder.rb | 4 +++-
 spec/support/matchers.rb     | 8 +++++---
 3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/Gemfile b/Gemfile
index aed7bd1..cf03de9 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,6 +10,7 @@ group :development do
   gem "nokogiri" # used to test html output
   gem "pry-byebug"
   gem 'debug'
+  gem "rails-dom-testing", "~> 2.2"
   gem "rspec"
   gem "sequel"
   gem "sqlite3", "~> 1.7.0"
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 02cfc48..9542b27 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -38,6 +38,8 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
         date_field filter.name, **options, &block
       elsif type == :textarea
         text_area filter.name, value: value, **options, type: nil, &block
+      elsif type == :checkbox
+        check_box filter.name, **options
       elsif type == :select
         select(
           filter.name,
@@ -63,7 +65,7 @@ def datagrid_extended_boolean_filter(filter, options = {}, &block)
     end
 
     def datagrid_boolean_filter(filter, options = {})
-      check_box(filter.name, options)
+      datagrid_filter_input(filter.name, **options, type: :checkbox)
     end
 
     def datagrid_date_filter(filter, options = {})
diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb
index a0ae6cd..76dd2f1 100644
--- a/spec/support/matchers.rb
+++ b/spec/support/matchers.rb
@@ -1,4 +1,4 @@
-require "nokogiri"
+require 'rails/dom/testing'
 
 def equal_to_dom(text)
   EqualToDom.new(text)
@@ -8,7 +8,9 @@ def match_css_pattern(pattern)
   CssPattern.new(pattern)
 end
 
+
 class EqualToDom
+  include Rails::Dom::Testing::Assertions::DomAssertions
 
   def initialize(expectation)
     @expectation = normalize(expectation)
@@ -16,11 +18,11 @@ def initialize(expectation)
 
   def matches?(text)
     @matcher = normalize(text)
-    @matcher == @expectation
+    compare_doms(@expectation, @matcher, false)
   end
 
   def normalize(text)
-    Nokogiri::HTML::DocumentFragment.parse(text.split("\n").map(&:strip).join("")).to_s
+    fragment(text)
   end
 
   def failure_message

From c278f0c458e28c6b456bd66a824adef4b6b7e832 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 10:45:05 +0100
Subject: [PATCH 004/157] Refactor

---
 lib/datagrid/form_builder.rb | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 9542b27..e9461e9 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -10,7 +10,7 @@ module FormBuilder
     #   * <tt>text_field</tt> for other filter types
     def datagrid_filter(filter_or_attribute, partials: nil, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
-      options = add_html_classes({**filter.input_options, **options}, filter.name, datagrid_filter_html_class(filter))
+      options = add_html_classes({**filter.input_options, **options}, *datagrid_filter_html_classes(filter))
       self.send( filter.form_builder_helper_name, filter, **options, &block)
     end
 
@@ -26,7 +26,6 @@ def datagrid_label(filter_or_attribute, text = nil, **options, &block)
     # @visibility private
     def datagrid_filter_input(attribute_or_filter, **options, &block)
       filter = datagrid_get_filter(attribute_or_filter)
-      value = object.filter_value_as_string(filter)
       type = options[:type]&.to_sym
       if options.has_key?(:value) && options[:value].nil? && [:"datetime-local", :"date"].include?(type)
         # https://github.com/rails/rails/pull/53387
@@ -37,9 +36,11 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       elsif type == :"date"
         date_field filter.name, **options, &block
       elsif type == :textarea
-        text_area filter.name, value: value, **options, type: nil, &block
+        text_area filter.name, value: object.filter_value_as_string(filter) , **options, type: nil, &block
       elsif type == :checkbox
         check_box filter.name, **options
+      elsif type == :hidden
+        hidden_field filter.name, **options
       elsif type == :select
         select(
           filter.name,
@@ -55,7 +56,7 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
            &block
         )
       else
-        text_field filter.name, value: value, **options, &block
+        text_field filter.name, value: object.filter_value_as_string(filter) , **options, &block
       end
     end
 
@@ -209,8 +210,8 @@ def datagrid_get_filter(attribute_or_filter)
       end
     end
 
-    def datagrid_filter_html_class(filter)
-      filter.class.to_s.demodulize.underscore
+    def datagrid_filter_html_classes(filter)
+      [filter.name, filter.class.to_s.demodulize.underscore]
     end
 
     def add_html_classes(options, *classes)

From 494b1832cfc7d27895edc888fab183f814ea1850 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 13:41:37 +0100
Subject: [PATCH 005/157] Make CSS classes generation consistent

---
 app/views/datagrid/_enum_checkboxes.html.erb |  4 +-
 lib/datagrid/filters/base_filter.rb          |  5 +-
 lib/datagrid/filters/enum_filter.rb          |  6 +++
 lib/datagrid/form_builder.rb                 | 20 ++++---
 spec/datagrid/form_builder_spec.rb           | 56 ++++++++++----------
 5 files changed, 52 insertions(+), 39 deletions(-)

diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
index 9f48319..f114c17 100644
--- a/app/views/datagrid/_enum_checkboxes.html.erb
+++ b/app/views/datagrid/_enum_checkboxes.html.erb
@@ -4,8 +4,8 @@ You can add indent if whitespace doesn't matter for you
 %>
 <%- elements.each do |value, text, checked| -%>
 <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
-<%= form.label filter.name, options.merge(for: id) do -%>
-<%= form.check_box(filter.name, {multiple: true, id: id, checked: checked, include_hidden: false}, value.to_s, nil) -%>
+<%= form.datagrid_label(filter.name, **options, for: id) do -%>
+<%= form.datagrid_filter_input(filter.name, type: :checkbox, multiple: true, id: id, checked: checked, include_hidden: false, value: value.to_s) -%>
 <%= text -%>
 <%- end -%>
 <%- end -%>
diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index db468d7..710d244 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -136,6 +136,10 @@ def enabled?(grid)
     ::Datagrid::Utils.process_availability(grid, options[:if], options[:unless])
   end
 
+  def default_html_classes
+    [ name, self.class.to_s.demodulize.underscore ]
+  end
+
   protected
 
   def default_filter_where(scope, value)
@@ -179,6 +183,5 @@ def default_filter(value, scope, grid)
       default_filter_where(scope, value)
     end
   end
-
 end
 
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index a76b097..809d10c 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -17,6 +17,12 @@ def parse(value)
     value
   end
 
+  def default_html_classes
+    res = super
+    res.push('checkboxes') if checkboxes?
+    res
+  end
+
   def strict
     options[:strict]
   end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index e9461e9..45f0fb2 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -10,7 +10,6 @@ module FormBuilder
     #   * <tt>text_field</tt> for other filter types
     def datagrid_filter(filter_or_attribute, partials: nil, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
-      options = add_html_classes({**filter.input_options, **options}, *datagrid_filter_html_classes(filter))
       self.send( filter.form_builder_helper_name, filter, **options, &block)
     end
 
@@ -20,12 +19,13 @@ def datagrid_filter(filter_or_attribute, partials: nil, **options, &block)
     # @return [String] a form label tag for the corresponding filter name
     def datagrid_label(filter_or_attribute, text = nil, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
-      label(filter.name, text || filter.header, **filter.label_options, **options, &block)
+      label(filter.name, text || filter.header, class: filter.default_html_classes, **filter.label_options, **options, &block)
     end
 
     # @visibility private
     def datagrid_filter_input(attribute_or_filter, **options, &block)
       filter = datagrid_get_filter(attribute_or_filter)
+      options = add_filter_options(filter, **options)
       type = options[:type]&.to_sym
       if options.has_key?(:value) && options[:value].nil? && [:"datetime-local", :"date"].include?(type)
         # https://github.com/rails/rails/pull/53387
@@ -38,7 +38,8 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       elsif type == :textarea
         text_area filter.name, value: object.filter_value_as_string(filter) , **options, type: nil, &block
       elsif type == :checkbox
-        check_box filter.name, **options
+        # raise options.inspect
+        check_box filter.name, options, options.fetch(:value, 1)
       elsif type == :hidden
         hidden_field filter.name, **options
       elsif type == :select
@@ -83,7 +84,6 @@ def datagrid_default_filter(filter, options = {})
 
     def datagrid_enum_filter(filter, options = {}, &block)
       if filter.checkboxes?
-        options = add_html_classes(options, 'checkboxes')
         elements = object.select_options(filter).map do |element|
           text, value = @template.send(:option_text_and_value, element)
           checked = enum_checkbox_checked?(filter, value)
@@ -124,6 +124,7 @@ def datagrid_integer_filter(filter, options = {})
     def datagrid_dynamic_filter(filter, options = {})
       input_name = "#{object_name}[#{filter.name.to_s}][]"
       field, operation, value = object.filter_value(filter)
+      options = add_filter_options(filter, **options)
       options = options.merge(name: input_name)
       field_input = dynamic_filter_select(
         filter.name,
@@ -210,10 +211,6 @@ def datagrid_get_filter(attribute_or_filter)
       end
     end
 
-    def datagrid_filter_html_classes(filter)
-      [filter.name, filter.class.to_s.demodulize.underscore]
-    end
-
     def add_html_classes(options, *classes)
       Datagrid::Utils.add_html_classes(options, *classes)
     end
@@ -233,6 +230,13 @@ def render_partial(name, locals)
       @template.render partial: partial_path(name), locals: locals
     end
 
+    def add_filter_options(filter, **options)
+      add_html_classes(
+        {**filter.input_options, **options},
+        *filter.default_html_classes,
+      )
+    end
+
     class Error < StandardError
     end
   end
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index c374330..1ca89a3 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -161,27 +161,27 @@ class MyTemplate
         let(:_filter_options) { {:id => "hello"} }
         let(:_range) { [1,2]}
         it { should equal_to_dom(
-          '<input value="1" id="from_hello" class="group_id integer_filter from" multiple type="text" name="report[group_id][]"/>' +
+          '<input value="1" id="from_hello" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
           '<span class="separator integer"> - </span>' +
-          '<input value="2" id="to_hello" class="group_id integer_filter to" multiple type="text" name="report[group_id][]"/>'
+          '<input value="2" id="to_hello" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
         )}
       end
       context "with only left bound" do
 
         let(:_range) { [10, nil]}
         it { should equal_to_dom(
-          '<input value="10" class="group_id integer_filter from" multiple type="text" name="report[group_id][]"/>' +
+          '<input value="10" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
           '<span class="separator integer"> - </span>' +
-          '<input class="group_id integer_filter to" multiple type="text" name="report[group_id][]"/>'
+          '<input class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
         )}
         it { should be_html_safe }
       end
       context "with only right bound" do
         let(:_range) { [nil, 10]}
         it { should equal_to_dom(
-          '<input class="group_id integer_filter from" multiple type="text" name="report[group_id][]"/>' +
+          '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
           '<span class="separator integer"> - </span>' +
-          '<input value="10" class="group_id integer_filter to" multiple type="text" name="report[group_id][]"/>'
+          '<input value="10" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
         )}
         it { should be_html_safe }
       end
@@ -189,9 +189,9 @@ class MyTemplate
       context "with invalid range value" do
         let(:_range) { 2..1 }
         it { should equal_to_dom(
-          '<input value="1" class="group_id integer_filter from" multiple type="text" name="report[group_id][]"/>' +
+          '<input value="1" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
           '<span class="separator integer"> - </span>' +
-          '<input value="2" class="group_id integer_filter to" multiple type="text" name="report[group_id][]"/>'
+          '<input value="2" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
         )}
       end
 
@@ -207,7 +207,7 @@ class MyTemplate
         let(:view_options) { { :partials => 'not_existed' } }
         let(:_range) { nil }
         it { should equal_to_dom(
-          '<input class="group_id integer_filter from" multiple type="text" name="report[group_id][]"><span class="separator integer"> - </span><input class="group_id integer_filter to" multiple type="text" name="report[group_id][]">'
+          '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"><span class="separator integer"> - </span><input class="to group_id integer_filter" multiple type="text" name="report[group_id][]">'
         ) }
 
       end
@@ -223,9 +223,9 @@ class MyTemplate
       }
       let(:_range) { [1.5,2.5]}
       it { should equal_to_dom(
-        '<input value="1.5" class="rating float_filter from" multiple type="text" name="report[rating][]"/>' +
+        '<input value="1.5" class="from rating float_filter" multiple type="text" name="report[rating][]"/>' +
         '<span class="separator float"> - </span>' +
-        '<input value="2.5" class="rating float_filter to" multiple type="text" name="report[rating][]"/>'
+        '<input value="2.5" class="to rating float_filter" multiple type="text" name="report[rating][]"/>'
       )}
     end
 
@@ -241,9 +241,9 @@ class MyTemplate
 
         let(:_range) { ["2012-01-03", nil]}
         it { should equal_to_dom(
-          '<input value="2012-01-03" class="created_at date_filter from" multiple type="text" name="report[created_at][]"/>' +
+          '<input value="2012-01-03" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
           '<span class="separator date"> - </span>' +
-          '<input class="created_at date_filter to" multiple type="text" name="report[created_at][]"/>'
+          '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
         )}
         it { should be_html_safe }
       end
@@ -255,18 +255,18 @@ class MyTemplate
         end
         let(:_range) { ["2013/01/01", '2013/02/02']}
         it { should equal_to_dom(
-          '<input value="01/01/2013" class="created_at date_filter from" multiple type="text" name="report[created_at][]"/>' +
+          '<input value="01/01/2013" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
           '<span class="separator date"> - </span>' +
-          '<input value="02/02/2013" class="created_at date_filter to" multiple type="text" name="report[created_at][]"/>'
+          '<input value="02/02/2013" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
         )}
       end
       context "with only right bound" do
 
         let(:_range) { [nil, "2012-01-03"]}
         it { should equal_to_dom(
-          '<input class="created_at date_filter from" multiple type="text" name="report[created_at][]"/>' +
+          '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
           '<span class="separator date"> - </span>' +
-          '<input value="2012-01-03" class="created_at date_filter to" multiple type="text"  name="report[created_at][]"/>'
+          '<input value="2012-01-03" class="to created_at date_filter" multiple type="text"  name="report[created_at][]"/>'
         )}
         it { should be_html_safe }
       end
@@ -274,9 +274,9 @@ class MyTemplate
       context "with invalid range value" do
         let(:_range) { Date.parse('2012-01-02')..Date.parse('2012-01-01') }
         it { should equal_to_dom(
-          '<input value="2012-01-01" class="created_at date_filter from" multiple type="text" name="report[created_at][]"/>' +
+          '<input value="2012-01-01" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
           '<span class="separator date"> - </span>' +
-          '<input value="2012-01-02" class="created_at date_filter to" multiple type="text" name="report[created_at][]"/>'
+          '<input value="2012-01-02" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
         )}
       end
       context "with blank range value" do
@@ -287,9 +287,9 @@ class MyTemplate
         end
         let(:_range) { [nil, nil] }
         it { should equal_to_dom(
-          '<input class="created_at date_filter from" multiple type="text" name="report[created_at][]"/>' +
+          '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
           '<span class="separator date"> - </span>' +
-          '<input class="created_at date_filter to" multiple type="text" name="report[created_at][]"/>'
+          '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
         )}
       end
     end
@@ -372,8 +372,8 @@ class MyTemplate
         let(:_category_filter_options) { {checkboxes: true} }
         it { should equal_to_dom(
           '
-<label class="category enum_filter checkboxes" for="report_category_first"><input id="report_category_first" type="checkbox" value="first" name="report[category][]" />first</label>
-<label class="category enum_filter checkboxes" for="report_category_second"><input id="report_category_second" type="checkbox" value="second" name="report[category][]" />second</label>
+<label class="category enum_filter checkboxes" for="report_category_first"><input class="category enum_filter checkboxes" type="checkbox" id="report_category_first" value="first" name="report[category][]" />first</label>
+<label class="category enum_filter checkboxes" for="report_category_second"><input class="category enum_filter checkboxes" type="checkbox" id="report_category_second" value="second" name="report[category][]" />second</label>
           '
         )}
 
@@ -522,9 +522,9 @@ class MyTemplate
       let(:_filter) { :column_names }
       let(:expected_html) do
         <<DOM
-<label class="column_names enum_filter checkboxes" for="report_column_names_id"><input id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">Id</label>
-<label class="column_names enum_filter checkboxes" for="report_column_names_name"><input id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]">Name</label>
-<label class="column_names enum_filter checkboxes" for="report_column_names_category"><input id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">Category</label>
+<label class="column_names enum_filter checkboxes" for="report_column_names_id"><input class="column_names enum_filter checkboxes" id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">Id</label>
+<label class="column_names enum_filter checkboxes" for="report_column_names_name"><input class="column_names enum_filter checkboxes" id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]">Name</label>
+<label class="column_names enum_filter checkboxes" for="report_column_names_category"><input class="column_names enum_filter checkboxes" id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">Category</label>
 DOM
       end
 
@@ -641,7 +641,7 @@ class MyTemplate
     end
     it "should generate label for filter" do
       expect(view.datagrid_label(:name)).to equal_to_dom(
-        '<label for="report_name">Name</label>'
+        '<label class="name string_filter", for="report_name">Name</label>'
       )
     end
     it "should pass options through to the helper" do
@@ -656,7 +656,7 @@ class MyTemplate
     end
     it "should support explicit label" do
       expect(view.datagrid_label(:name, "The Name")).to equal_to_dom(
-        '<label for="report_name">The Name</label>'
+        '<label class="name string_filter" for="report_name">The Name</label>'
       )
     end
   end

From bf68e7db36e38fe6dd049c4fb8a62a5d002a9f5a Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 16:01:36 +0100
Subject: [PATCH 006/157] Never override built-in generated html classes

---
 app/views/datagrid/_form.html.erb  |  2 +-
 lib/datagrid/form_builder.rb       | 15 +++++++++------
 spec/datagrid/form_builder_spec.rb | 12 ++++++------
 3 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index 7e175c1..b0132f5 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_for grid, options do |f| -%>
+<%= form_for grid, **options do |f| -%>
   <% grid.filters.each do |filter| %>
     <div class="datagrid-filter filter">
       <%= f.datagrid_label filter %>
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 45f0fb2..bfdd80e 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -19,24 +19,28 @@ def datagrid_filter(filter_or_attribute, partials: nil, **options, &block)
     # @return [String] a form label tag for the corresponding filter name
     def datagrid_label(filter_or_attribute, text = nil, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
-      label(filter.name, text || filter.header, class: filter.default_html_classes, **filter.label_options, **options, &block)
+      options = add_html_classes(
+        {**filter.label_options, **options},
+        filter.default_html_classes
+      )
+      label(filter.name, text || filter.header, **options, &block)
     end
 
     # @visibility private
     def datagrid_filter_input(attribute_or_filter, **options, &block)
       filter = datagrid_get_filter(attribute_or_filter)
       options = add_filter_options(filter, **options)
-      type = options[:type]&.to_sym
-      if options.has_key?(:value) && options[:value].nil? && [:"datetime-local", :"date"].include?(type)
+      type = options.delete(:type)&.to_sym
+      if options.has_key?(:value) && options[:value].nil? && [:"datetime-local", :date].include?(type)
         # https://github.com/rails/rails/pull/53387
         options[:value] = ""
       end
       if type == :"datetime-local"
         datetime_local_field filter.name, **options, &block
-      elsif type == :"date"
+      elsif type == :date
         date_field filter.name, **options, &block
       elsif type == :textarea
-        text_area filter.name, value: object.filter_value_as_string(filter) , **options, type: nil, &block
+        text_area filter.name, value: object.filter_value_as_string(filter) , **options, &block
       elsif type == :checkbox
         # raise options.inspect
         check_box filter.name, options, options.fetch(:value, 1)
@@ -53,7 +57,6 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
           },
            multiple: filter.multiple?,
            **options,
-           type: nil,
            &block
         )
       else
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 1ca89a3..6aa5826 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -636,22 +636,22 @@ class MyTemplate
     end
     it "should generate label for filter" do
       expect(view.datagrid_label(:created_at)).to equal_to_dom(
-        '<label class="js-date-selector" for="report_created_at">Created at</label>'
+        '<label class="js-date-selector created_at date_filter" for="report_created_at">Created at</label>'
       )
     end
     it "should generate label for filter" do
       expect(view.datagrid_label(:name)).to equal_to_dom(
-        '<label class="name string_filter", for="report_name">Name</label>'
+        '<label class="name string_filter" for="report_name">Name</label>'
       )
     end
     it "should pass options through to the helper" do
-      expect(view.datagrid_label(:name, :class => 'foo')).to equal_to_dom(
-        '<label class="foo" for="report_name">Name</label>'
+      expect(view.datagrid_label(:name, class: 'foo')).to equal_to_dom(
+        '<label class="foo name string_filter" for="report_name">Name</label>'
       )
     end
     it "should support block" do
-      expect(view.datagrid_label(:name, :class => 'foo') { 'The Name' }).to equal_to_dom(
-        '<label class="foo" for="report_name">The Name</label>'
+      expect(view.datagrid_label(:name, class: 'foo') { 'The Name' }).to equal_to_dom(
+        '<label class="foo name string_filter" for="report_name">The Name</label>'
       )
     end
     it "should support explicit label" do

From 1f0b5a6f114c06b7c2993ac542c4e888c7aa4084 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 19:30:01 +0100
Subject: [PATCH 007/157] Remove filter class from wrapping block

---
 app/views/datagrid/_form.html.erb | 2 +-
 spec/datagrid/helper_spec.rb      | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index b0132f5..911d163 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -1,6 +1,6 @@
 <%= form_for grid, **options do |f| -%>
   <% grid.filters.each do |filter| %>
-    <div class="datagrid-filter filter">
+    <div class="datagrid-filter">
       <%= f.datagrid_label filter %>
       <%= f.datagrid_filter filter %>
     </div>
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 1cd558c..2ec195f 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -483,8 +483,8 @@ class FormForGrid
       expect(subject.datagrid_form_for(object, url: "/grid")).to match_css_pattern(
         "form.datagrid-form.form_for_grid[action='/grid']" => 1,
         "form input[name=utf8]" => 1,
-        "form .filter label" => "Category",
-        "form .filter input.category.default_filter[name='form_for_grid[category]'][value=hello]" => 1,
+        "form .datagrid-filter label" => "Category",
+        "form .datagrid-filter input.category.default_filter[name='form_for_grid[category]'][value=hello]" => 1,
         "form input[name=commit][value=Search]" => 1,
         "form a.datagrid-reset[href='/location']" => 1
       )

From e1ad783f2d3bf43d0a34fd209d591837b10c9722 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 19:37:37 +0100
Subject: [PATCH 008/157] Rubocop install

---
 .rubocop.yml                                  |    3 +
 .rubocop_todo.yml                             | 1021 +++++++++++++++++
 Gemfile                                       |    6 +-
 Rakefile                                      |   22 +-
 datagrid.gemspec                              |    5 +-
 gemfiles/rails_6.1.gemfile                    |    2 +-
 gemfiles/rails_7.0.gemfile                    |    2 +-
 gemfiles/rails_7.1.gemfile                    |    2 +-
 gemfiles/rails_7.2.gemfile                    |    2 +-
 lib/datagrid.rb                               |    8 +-
 lib/datagrid/active_model.rb                  |   10 +-
 lib/datagrid/column_names_attribute.rb        |   15 +-
 lib/datagrid/columns.rb                       |   43 +-
 lib/datagrid/columns/column.rb                |   24 +-
 lib/datagrid/configuration.rb                 |    1 -
 lib/datagrid/core.rb                          |   44 +-
 lib/datagrid/drivers.rb                       |    1 -
 lib/datagrid/drivers/abstract_driver.rb       |   22 +-
 lib/datagrid/drivers/active_record.rb         |   28 +-
 lib/datagrid/drivers/array.rb                 |   10 +-
 lib/datagrid/drivers/mongo_mapper.rb          |   23 +-
 lib/datagrid/drivers/mongoid.rb               |   28 +-
 lib/datagrid/drivers/sequel.rb                |   17 +-
 lib/datagrid/engine.rb                        |    5 +-
 lib/datagrid/filters.rb                       |   22 +-
 lib/datagrid/filters/base_filter.rb           |   28 +-
 lib/datagrid/filters/boolean_filter.rb        |    2 -
 lib/datagrid/filters/composite_filters.rb     |   20 +-
 lib/datagrid/filters/date_filter.rb           |   11 +-
 lib/datagrid/filters/date_time_filter.rb      |    2 -
 lib/datagrid/filters/dynamic_filter.rb        |   48 +-
 lib/datagrid/filters/enum_filter.rb           |   11 +-
 .../filters/extended_boolean_filter.rb        |    7 +-
 lib/datagrid/filters/float_filter.rb          |    2 +-
 lib/datagrid/filters/integer_filter.rb        |    5 +-
 lib/datagrid/filters/ranged_filter.rb         |   18 +-
 lib/datagrid/filters/select_options.rb        |   17 +-
 lib/datagrid/filters/string_filter.rb         |    1 -
 lib/datagrid/form_builder.rb                  |   47 +-
 lib/datagrid/helper.rb                        |    7 +-
 lib/datagrid/ordering.rb                      |   46 +-
 lib/datagrid/renderer.rb                      |   41 +-
 lib/datagrid/rspec.rb                         |   14 +-
 lib/datagrid/scaffold.rb                      |   38 +-
 lib/datagrid/utils.rb                         |   42 +-
 lib/tasks/datagrid_tasks.rake                 |    1 -
 spec/datagrid/active_model_spec.rb            |    8 +-
 spec/datagrid/column_names_attribute_spec.rb  |   34 +-
 spec/datagrid/columns/column_spec.rb          |    7 +-
 spec/datagrid/columns_spec.rb                 |  233 ++--
 spec/datagrid/core_spec.rb                    |   79 +-
 spec/datagrid/drivers/active_record_spec.rb   |   31 +-
 spec/datagrid/drivers/array_spec.rb           |   66 +-
 spec/datagrid/drivers/mongo_mapper_spec.rb    |   72 +-
 spec/datagrid/drivers/mongoid_spec.rb         |   68 +-
 spec/datagrid/drivers/sequel_spec.rb          |   73 +-
 spec/datagrid/filters/base_filter_spec.rb     |   17 +-
 .../filters/composite_filters_spec.rb         |   33 +-
 spec/datagrid/filters/date_filter_spec.rb     |   58 +-
 .../datagrid/filters/date_time_filter_spec.rb |   56 +-
 spec/datagrid/filters/dynamic_filter_spec.rb  |  127 +-
 spec/datagrid/filters/enum_filter_spec.rb     |   35 +-
 .../filters/extended_boolean_filter_spec.rb   |   16 +-
 spec/datagrid/filters/float_filter_spec.rb    |    9 +-
 spec/datagrid/filters/integer_filter_spec.rb  |   31 +-
 spec/datagrid/filters/string_filter_spec.rb   |   39 +-
 spec/datagrid/filters_spec.rb                 |  137 +--
 spec/datagrid/form_builder_spec.rb            |  540 +++++----
 spec/datagrid/helper_spec.rb                  |  213 ++--
 spec/datagrid/ordering_spec.rb                |   43 +-
 spec/datagrid/scaffold_spec.rb                |   17 +-
 spec/datagrid/stylesheet_spec.rb              |    4 +-
 spec/datagrid/utils_spec.rb                   |    4 +-
 spec/datagrid_spec.rb                         |   27 +-
 spec/spec_helper.rb                           |   87 +-
 spec/support/active_record.rb                 |    7 +-
 spec/support/configuration.rb                 |   37 +-
 spec/support/i18n_helpers.rb                  |    2 +-
 spec/support/matchers.rb                      |   17 +-
 spec/support/mongo_mapper.rb                  |    2 -
 spec/support/mongoid.rb                       |    7 +-
 spec/support/sequel.rb                        |    5 -
 spec/support/simple_report.rb                 |   23 +-
 83 files changed, 2442 insertions(+), 1596 deletions(-)
 create mode 100644 .rubocop.yml
 create mode 100644 .rubocop_todo.yml

diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000..e5ccac6
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,3 @@
+# inherit_from: .rubocop_todo.yml
+Style/StringLiterals:
+  EnforcedStyle: double_quotes
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 0000000..88da75f
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,1021 @@
+# This configuration was generated by
+# `rubocop --auto-gen-config`
+# on 2024-11-03 18:32:09 UTC using RuboCop version 1.68.0.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
+# Include: **/*.gemfile, **/Gemfile, **/gems.rb
+Bundler/OrderedGems:
+  Exclude:
+    - 'Gemfile'
+    - 'gemfiles/rails_6.1.gemfile'
+    - 'gemfiles/rails_7.0.gemfile'
+    - 'gemfiles/rails_7.1.gemfile'
+    - 'gemfiles/rails_7.2.gemfile'
+
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentationWidth.
+# SupportedStyles: with_first_argument, with_fixed_indentation
+Layout/ArgumentAlignment:
+  Exclude:
+    - 'lib/datagrid/filters.rb'
+    - 'lib/datagrid/form_builder.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleAlignWith.
+# SupportedStylesAlignWith: either, start_of_block, start_of_line
+Layout/BlockAlignment:
+  Exclude:
+    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
+    - 'spec/datagrid/filters/enum_filter_spec.rb'
+
+# Offense count: 35
+# This cop supports safe autocorrection (--autocorrect).
+Layout/BlockEndNewline:
+  Exclude:
+    - 'spec/datagrid/form_builder_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/datagrid_spec.rb'
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+Layout/ClosingHeredocIndentation:
+  Exclude:
+    - 'lib/datagrid/scaffold.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/datagrid/scaffold_spec.rb'
+
+# Offense count: 40
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLineAfterGuardClause:
+  Exclude:
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/columns/column.rb'
+    - 'lib/datagrid/drivers/active_record.rb'
+    - 'lib/datagrid/drivers/array.rb'
+    - 'lib/datagrid/drivers/mongo_mapper.rb'
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/drivers/sequel.rb'
+    - 'lib/datagrid/filters.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'lib/datagrid/filters/dynamic_filter.rb'
+    - 'lib/datagrid/filters/enum_filter.rb'
+    - 'lib/datagrid/filters/float_filter.rb'
+    - 'lib/datagrid/filters/integer_filter.rb'
+    - 'lib/datagrid/ordering.rb'
+    - 'lib/datagrid/utils.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLineAfterMagicComment:
+  Exclude:
+    - 'spec/datagrid/form_builder_spec.rb'
+
+# Offense count: 14
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines.
+Layout/EmptyLineBetweenDefs:
+  Exclude:
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/columns/column.rb'
+    - 'lib/datagrid/core.rb'
+    - 'lib/datagrid/filters/date_filter.rb'
+    - 'lib/datagrid/helper.rb'
+    - 'lib/datagrid/ordering.rb'
+    - 'spec/datagrid/columns_spec.rb'
+    - 'spec/support/active_record.rb'
+    - 'spec/support/matchers.rb'
+    - 'spec/support/sequel.rb'
+
+# Offense count: 60
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLines:
+  Enabled: false
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: around, only_before
+Layout/EmptyLinesAroundAccessModifier:
+  Exclude:
+    - 'lib/datagrid/core.rb'
+    - 'lib/datagrid/drivers/abstract_driver.rb'
+    - 'lib/datagrid/form_builder.rb'
+    - 'lib/datagrid/renderer.rb'
+    - 'lib/datagrid/scaffold.rb'
+    - 'lib/datagrid/utils.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowAliasSyntax, AllowedMethods.
+# AllowedMethods: alias_method, public, protected, private
+Layout/EmptyLinesAroundAttributeAccessor:
+  Exclude:
+    - 'spec/datagrid/columns_spec.rb'
+    - 'spec/datagrid/filters_spec.rb'
+
+# Offense count: 96
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: empty_lines, no_empty_lines
+Layout/EmptyLinesAroundBlockBody:
+  Enabled: false
+
+# Offense count: 37
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only
+Layout/EmptyLinesAroundClassBody:
+  Enabled: false
+
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+Layout/EmptyLinesAroundMethodBody:
+  Exclude:
+    - 'lib/datagrid/columns/column.rb'
+    - 'lib/datagrid/filters/ranged_filter.rb'
+    - 'lib/datagrid/utils.rb'
+
+# Offense count: 16
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines
+Layout/EmptyLinesAroundModuleBody:
+  Exclude:
+    - 'lib/datagrid.rb'
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/configuration.rb'
+    - 'lib/datagrid/core.rb'
+    - 'lib/datagrid/filters.rb'
+    - 'lib/datagrid/filters/composite_filters.rb'
+    - 'lib/datagrid/filters/ranged_filter.rb'
+    - 'lib/datagrid/helper.rb'
+    - 'lib/datagrid/ordering.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleAlignWith, Severity.
+# SupportedStylesAlignWith: keyword, variable, start_of_line
+Layout/EndAlignment:
+  Exclude:
+    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
+
+# Offense count: 16
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
+Layout/ExtraSpacing:
+  Exclude:
+    - 'lib/datagrid/active_model.rb'
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/drivers/active_record.rb'
+    - 'lib/datagrid/drivers/sequel.rb'
+    - 'lib/datagrid/filters/composite_filters.rb'
+    - 'lib/datagrid/ordering.rb'
+    - 'spec/datagrid/columns_spec.rb'
+    - 'spec/datagrid/filters/dynamic_filter_spec.rb'
+    - 'spec/datagrid/filters/enum_filter_spec.rb'
+    - 'spec/datagrid/ordering_spec.rb'
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_brackets
+Layout/FirstArrayElementIndentation:
+  EnforcedStyle: consistent
+
+# Offense count: 16
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: IndentationWidth.
+# SupportedStyles: special_inside_parentheses, consistent, align_braces
+Layout/FirstHashElementIndentation:
+  EnforcedStyle: consistent
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
+# SupportedHashRocketStyles: key, separator, table
+# SupportedColonStyles: key, separator, table
+# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
+Layout/HashAlignment:
+  Exclude:
+    - 'spec/spec_helper.rb'
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+Layout/HeredocIndentation:
+  Exclude:
+    - 'lib/datagrid/scaffold.rb'
+    - 'spec/datagrid/form_builder_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: normal, indented_internal_methods
+Layout/IndentationConsistency:
+  Exclude:
+    - 'spec/datagrid/columns_spec.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Width, AllowedPatterns.
+Layout/IndentationWidth:
+  Exclude:
+    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+
+# Offense count: 11
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment, AllowRBSInlineAnnotation, AllowSteepAnnotation.
+Layout/LeadingCommentSpace:
+  Exclude:
+    - 'lib/datagrid/drivers/mongo_mapper.rb'
+    - 'lib/datagrid/rspec.rb'
+    - 'spec/datagrid/filters/dynamic_filter_spec.rb'
+    - 'spec/spec_helper.rb'
+    - 'spec/support/active_record.rb'
+    - 'spec/support/mongoid.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Layout/LeadingEmptyLines:
+  Exclude:
+    - 'spec/support/simple_report.rb'
+
+# Offense count: 35
+# This cop supports safe autocorrection (--autocorrect).
+Layout/MultilineBlockLayout:
+  Exclude:
+    - 'spec/datagrid/form_builder_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/datagrid_spec.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentationWidth.
+# SupportedStyles: aligned, indented
+Layout/MultilineOperationIndentation:
+  Exclude:
+    - 'lib/datagrid/filters/integer_filter.rb'
+
+# Offense count: 36
+# This cop supports safe autocorrection (--autocorrect).
+Layout/SpaceAfterComma:
+  Exclude:
+    - 'lib/datagrid/columns.rb'
+    - 'spec/datagrid/drivers/array_spec.rb'
+    - 'spec/datagrid/drivers/sequel_spec.rb'
+    - 'spec/datagrid/filters/date_filter_spec.rb'
+    - 'spec/datagrid/filters/date_time_filter_spec.rb'
+    - 'spec/datagrid/filters/enum_filter_spec.rb'
+    - 'spec/datagrid/filters/integer_filter_spec.rb'
+    - 'spec/datagrid/filters_spec.rb'
+    - 'spec/datagrid/form_builder_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+Layout/SpaceAfterNot:
+  Exclude:
+    - 'lib/datagrid/columns/column.rb'
+    - 'lib/datagrid/drivers/active_record.rb'
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/drivers/sequel.rb'
+
+# Offense count: 14
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.
+# SupportedStylesForExponentOperator: space, no_space
+# SupportedStylesForRationalLiterals: space, no_space
+Layout/SpaceAroundOperators:
+  Exclude:
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/drivers/active_record.rb'
+    - 'lib/datagrid/drivers/mongo_mapper.rb'
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/drivers/sequel.rb'
+    - 'spec/datagrid/drivers/array_spec.rb'
+    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
+    - 'spec/datagrid/drivers/mongoid_spec.rb'
+    - 'spec/datagrid/drivers/sequel_spec.rb'
+    - 'spec/datagrid/filters/date_time_filter_spec.rb'
+    - 'spec/datagrid/filters/enum_filter_spec.rb'
+    - 'spec/datagrid/filters/integer_filter_spec.rb'
+    - 'spec/spec_helper.rb'
+
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
+# SupportedStyles: space, no_space
+# SupportedStylesForEmptyBraces: space, no_space
+Layout/SpaceBeforeBlockBraces:
+  Exclude:
+    - 'lib/datagrid/filters.rb'
+    - 'spec/datagrid/filters/dynamic_filter_spec.rb'
+    - 'spec/datagrid/ordering_spec.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+Layout/SpaceBeforeComma:
+  Exclude:
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/filters.rb'
+    - 'lib/datagrid/form_builder.rb'
+
+# Offense count: 5
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowForAlignment.
+Layout/SpaceBeforeFirstArg:
+  Exclude:
+    - 'lib/datagrid/active_model.rb'
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/filters/composite_filters.rb'
+    - 'lib/datagrid/ordering.rb'
+    - 'spec/datagrid/columns_spec.rb'
+
+# Offense count: 16
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets.
+# SupportedStyles: space, no_space, compact
+# SupportedStylesForEmptyBrackets: space, no_space
+Layout/SpaceInsideArrayLiteralBrackets:
+  Exclude:
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'spec/datagrid/column_names_attribute_spec.rb'
+    - 'spec/datagrid/drivers/array_spec.rb'
+    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
+    - 'spec/datagrid/drivers/mongoid_spec.rb'
+    - 'spec/datagrid/drivers/sequel_spec.rb'
+
+# Offense count: 425
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
+# SupportedStyles: space, no_space
+# SupportedStylesForEmptyBraces: space, no_space
+Layout/SpaceInsideBlockBraces:
+  Enabled: false
+
+# Offense count: 162
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
+# SupportedStyles: space, no_space, compact
+# SupportedStylesForEmptyBraces: space, no_space
+Layout/SpaceInsideHashLiteralBraces:
+  Enabled: false
+
+# Offense count: 18
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: space, compact, no_space
+Layout/SpaceInsideParens:
+  Exclude:
+    - 'lib/datagrid/form_builder.rb'
+    - 'lib/datagrid/ordering.rb'
+    - 'spec/datagrid/filters/enum_filter_spec.rb'
+    - 'spec/datagrid/filters/integer_filter_spec.rb'
+    - 'spec/datagrid/filters/string_filter_spec.rb'
+    - 'spec/datagrid/filters_spec.rb'
+    - 'spec/datagrid/form_builder_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+
+# Offense count: 15
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: final_newline, final_blank_line
+Layout/TrailingEmptyLines:
+  Exclude:
+    - 'Rakefile'
+    - 'datagrid.gemspec'
+    - 'lib/datagrid.rb'
+    - 'lib/datagrid/column_names_attribute.rb'
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'lib/datagrid/filters/date_filter.rb'
+    - 'lib/datagrid/filters/date_time_filter.rb'
+    - 'lib/datagrid/filters/integer_filter.rb'
+    - 'lib/datagrid/helper.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/support/configuration.rb'
+    - 'spec/support/mongoid.rb'
+    - 'spec/support/sequel.rb'
+    - 'spec/support/simple_report.rb'
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowInHeredoc.
+Layout/TrailingWhitespace:
+  Exclude:
+    - 'spec/datagrid/columns/column_spec.rb'
+    - 'spec/datagrid/filters/base_filter_spec.rb'
+    - 'spec/datagrid/filters/float_filter_spec.rb'
+    - 'spec/datagrid/utils_spec.rb'
+
+# Offense count: 4
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowSafeAssignment.
+Lint/AssignmentInCondition:
+  Exclude:
+    - 'lib/datagrid/columns/column.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'lib/datagrid/form_builder.rb'
+    - 'lib/datagrid/renderer.rb'
+
+# Offense count: 31
+# Configuration parameters: AllowedMethods.
+# AllowedMethods: enums
+Lint/ConstantDefinitionInBlock:
+  Exclude:
+    - 'spec/datagrid/active_model_spec.rb'
+    - 'spec/datagrid/columns/column_spec.rb'
+    - 'spec/datagrid/columns_spec.rb'
+    - 'spec/datagrid/core_spec.rb'
+    - 'spec/datagrid/drivers/array_spec.rb'
+    - 'spec/datagrid/drivers/sequel_spec.rb'
+    - 'spec/datagrid/filters/enum_filter_spec.rb'
+    - 'spec/datagrid/filters_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/datagrid/ordering_spec.rb'
+    - 'spec/support/active_record.rb'
+
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Lint/NonDeterministicRequireOrder:
+  Exclude:
+    - 'spec/spec_helper.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Lint/RedundantStringCoercion:
+  Exclude:
+    - 'lib/datagrid/form_builder.rb'
+
+# Offense count: 6
+# Configuration parameters: AllowComments, AllowNil.
+Lint/SuppressedException:
+  Exclude:
+    - 'lib/datagrid/active_model.rb'
+    - 'lib/datagrid/utils.rb'
+    - 'spec/spec_helper.rb'
+
+# Offense count: 8
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
+Lint/UnusedBlockArgument:
+  Exclude:
+    - 'lib/datagrid/filters/extended_boolean_filter.rb'
+    - 'spec/datagrid/columns_spec.rb'
+    - 'spec/datagrid/drivers/active_record_spec.rb'
+    - 'spec/datagrid/form_builder_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/datagrid/ordering_spec.rb'
+
+# Offense count: 20
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods.
+Lint/UnusedMethodArgument:
+  Exclude:
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/drivers/array.rb'
+    - 'lib/datagrid/drivers/mongo_mapper.rb'
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'lib/datagrid/filters/composite_filters.rb'
+    - 'lib/datagrid/form_builder.rb'
+    - 'spec/support/i18n_helpers.rb'
+
+# Offense count: 3
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AutoCorrect.
+Lint/UselessMethodDefinition:
+  Exclude:
+    - 'lib/datagrid/core.rb'
+    - 'lib/datagrid/drivers/sequel.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, CheckForMethodsWithNoSideEffects.
+Lint/Void:
+  Exclude:
+    - 'spec/support/matchers.rb'
+
+# Offense count: 14
+# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
+Metrics/AbcSize:
+  Max: 39
+
+# Offense count: 64
+# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
+# AllowedMethods: refine
+Metrics/BlockLength:
+  Max: 606
+
+# Offense count: 5
+# Configuration parameters: CountComments, CountAsOne.
+Metrics/ClassLength:
+  Max: 146
+
+# Offense count: 10
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+Metrics/CyclomaticComplexity:
+  Max: 14
+
+# Offense count: 32
+# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
+Metrics/MethodLength:
+  Max: 34
+
+# Offense count: 3
+# Configuration parameters: CountComments, CountAsOne.
+Metrics/ModuleLength:
+  Max: 193
+
+# Offense count: 10
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+Metrics/PerceivedComplexity:
+  Max: 14
+
+# Offense count: 9
+# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros.
+# NamePrefix: is_, has_, have_
+# ForbiddenPrefixes: is_, has_, have_
+# AllowedMethods: is_a?
+# MethodDefinitionMacros: define_method, define_singleton_method
+Naming/PredicateName:
+  Exclude:
+    - 'spec/**/*'
+    - 'lib/datagrid/drivers/abstract_driver.rb'
+    - 'lib/datagrid/drivers/active_record.rb'
+    - 'lib/datagrid/drivers/array.rb'
+    - 'lib/datagrid/drivers/mongo_mapper.rb'
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/drivers/sequel.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: prefer_alias, prefer_alias_method
+Style/Alias:
+  Exclude:
+    - 'lib/datagrid/ordering.rb'
+
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: always, conditionals
+Style/AndOr:
+  Exclude:
+    - 'spec/support/matchers.rb'
+
+# Offense count: 28
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
+# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
+# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
+# FunctionalMethods: let, let!, subject, watch
+# AllowedMethods: lambda, proc, it
+Style/BlockDelimiters:
+  Exclude:
+    - 'spec/datagrid/columns_spec.rb'
+    - 'spec/datagrid/core_spec.rb'
+    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
+    - 'spec/datagrid/drivers/mongoid_spec.rb'
+    - 'spec/datagrid/drivers/sequel_spec.rb'
+    - 'spec/datagrid/filters/dynamic_filter_spec.rb'
+    - 'spec/datagrid/filters_spec.rb'
+    - 'spec/datagrid/form_builder_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/datagrid/ordering_spec.rb'
+    - 'spec/datagrid_spec.rb'
+
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowOnConstant, AllowOnSelfClass.
+Style/CaseEquality:
+  Exclude:
+    - 'lib/datagrid/filters/select_options.rb'
+
+# Offense count: 2
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: MinBranchesCount.
+Style/CaseLikeIf:
+  Exclude:
+    - 'lib/datagrid/form_builder.rb'
+    - 'spec/support/matchers.rb'
+
+# Offense count: 16
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: nested, compact
+Style/ClassAndModuleChildren:
+  Exclude:
+    - 'lib/datagrid/columns/column.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'lib/datagrid/filters/boolean_filter.rb'
+    - 'lib/datagrid/filters/date_filter.rb'
+    - 'lib/datagrid/filters/date_time_filter.rb'
+    - 'lib/datagrid/filters/default_filter.rb'
+    - 'lib/datagrid/filters/dynamic_filter.rb'
+    - 'lib/datagrid/filters/enum_filter.rb'
+    - 'lib/datagrid/filters/extended_boolean_filter.rb'
+    - 'lib/datagrid/filters/float_filter.rb'
+    - 'lib/datagrid/filters/integer_filter.rb'
+    - 'lib/datagrid/filters/ranged_filter.rb'
+    - 'lib/datagrid/filters/select_options.rb'
+    - 'lib/datagrid/filters/string_filter.rb'
+    - 'lib/datagrid/scaffold.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Keywords, RequireColon.
+# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE
+Style/CommentAnnotation:
+  Exclude:
+    - 'lib/datagrid/drivers/mongo_mapper.rb'
+    - 'spec/spec_helper.rb'
+
+# Offense count: 25
+# Configuration parameters: AllowedConstants.
+Style/Documentation:
+  Enabled: false
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AutoCorrect, EnforcedStyle, AllowComments.
+# SupportedStyles: empty, nil, both
+Style/EmptyElse:
+  Exclude:
+    - 'lib/datagrid/ordering.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Style/EmptyLiteral:
+  Exclude:
+    - 'spec/datagrid/drivers/array_spec.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+Style/Encoding:
+  Exclude:
+    - 'Rakefile'
+    - 'spec/datagrid/form_builder_spec.rb'
+
+# Offense count: 3
+# This cop supports safe autocorrection (--autocorrect).
+Style/ExpandPathArguments:
+  Exclude:
+    - 'lib/datagrid.rb'
+    - 'spec/spec_helper.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+Style/ExplicitBlockArgument:
+  Exclude:
+    - 'lib/datagrid/drivers/mongo_mapper.rb'
+    - 'lib/datagrid/drivers/mongoid.rb'
+
+# Offense count: 82
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: always, always_true, never
+Style/FrozenStringLiteralComment:
+  Enabled: false
+
+# Offense count: 3
+# Configuration parameters: AllowedVariables.
+Style/GlobalVars:
+  Exclude:
+    - 'spec/datagrid/filters_spec.rb'
+
+# Offense count: 9
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
+Style/GuardClause:
+  Exclude:
+    - 'lib/datagrid/core.rb'
+    - 'lib/datagrid/drivers/abstract_driver.rb'
+    - 'lib/datagrid/filters/dynamic_filter.rb'
+    - 'lib/datagrid/filters/ranged_filter.rb'
+    - 'lib/datagrid/filters/select_options.rb'
+    - 'lib/datagrid/form_builder.rb'
+    - 'lib/datagrid/ordering.rb'
+    - 'spec/support/matchers.rb'
+
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowedReceivers.
+# AllowedReceivers: Thread.current
+Style/HashEachMethods:
+  Exclude:
+    - 'spec/datagrid/form_builder_spec.rb'
+
+# Offense count: 226
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
+# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
+# SupportedShorthandSyntax: always, never, either, consistent, either_consistent
+Style/HashSyntax:
+  Enabled: false
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowIfModifier.
+Style/IfInsideElse:
+  Exclude:
+    - 'lib/datagrid/column_names_attribute.rb'
+    - 'lib/datagrid/ordering.rb'
+
+# Offense count: 45
+# This cop supports safe autocorrection (--autocorrect).
+Style/IfUnlessModifier:
+  Enabled: false
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: line_count_dependent, lambda, literal
+Style/Lambda:
+  Exclude:
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/support/simple_report.rb'
+
+# Offense count: 20
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/LineEndConcatenation:
+  Exclude:
+    - 'spec/datagrid/form_builder_spec.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+Style/MethodCallWithoutArgsParentheses:
+  Exclude:
+    - 'spec/datagrid/form_builder_spec.rb'
+
+# Offense count: 2
+Style/MissingRespondToMissing:
+  Exclude:
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/renderer.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+Style/MultilineTernaryOperator:
+  Exclude:
+    - 'lib/datagrid/drivers/active_record.rb'
+    - 'lib/datagrid/filters/select_options.rb'
+
+# Offense count: 14
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: literals, strict
+Style/MutableConstant:
+  Exclude:
+    - 'lib/datagrid/drivers/abstract_driver.rb'
+    - 'lib/datagrid/filters.rb'
+    - 'lib/datagrid/filters/dynamic_filter.rb'
+    - 'lib/datagrid/filters/extended_boolean_filter.rb'
+    - 'lib/datagrid/utils.rb'
+    - 'lib/datagrid/version.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: both, prefix, postfix
+Style/NegatedIf:
+  Exclude:
+    - 'spec/support/matchers.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Style/NestedTernaryOperator:
+  Exclude:
+    - 'lib/datagrid/helper.rb'
+
+# Offense count: 20
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedOctalStyle.
+# SupportedOctalStyles: zero_with_o, zero_only
+Style/NumericLiteralPrefix:
+  Exclude:
+    - 'spec/datagrid/filters/date_filter_spec.rb'
+    - 'spec/datagrid/filters/date_time_filter_spec.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns.
+Style/NumericLiterals:
+  MinDigits: 6
+
+# Offense count: 4
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
+# SupportedStyles: predicate, comparison
+Style/NumericPredicate:
+  Exclude:
+    - 'spec/**/*'
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/utils.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Style/ParallelAssignment:
+  Exclude:
+    - 'lib/datagrid/utils.rb'
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: PreferredDelimiters.
+Style/PercentLiteralDelimiters:
+  Exclude:
+    - 'lib/datagrid/filters/dynamic_filter.rb'
+    - 'spec/datagrid/columns_spec.rb'
+    - 'spec/datagrid/form_builder_spec.rb'
+
+# Offense count: 7
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: short, verbose
+Style/PreferredHashMethods:
+  Exclude:
+    - 'lib/datagrid/columns/column.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'lib/datagrid/filters/dynamic_filter.rb'
+    - 'lib/datagrid/filters/select_options.rb'
+    - 'lib/datagrid/form_builder.rb'
+    - 'lib/datagrid/renderer.rb'
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantBegin:
+  Exclude:
+    - 'lib/datagrid/utils.rb'
+    - 'spec/support/configuration.rb'
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowMultipleReturnValues.
+Style/RedundantReturn:
+  Exclude:
+    - 'lib/datagrid/drivers/mongoid.rb'
+
+# Offense count: 38
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantSelf:
+  Exclude:
+    - 'lib/datagrid/active_model.rb'
+    - 'lib/datagrid/columns.rb'
+    - 'lib/datagrid/columns/column.rb'
+    - 'lib/datagrid/core.rb'
+    - 'lib/datagrid/drivers/abstract_driver.rb'
+    - 'lib/datagrid/filters.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'lib/datagrid/filters/enum_filter.rb'
+    - 'lib/datagrid/filters/select_options.rb'
+    - 'lib/datagrid/form_builder.rb'
+    - 'lib/datagrid/ordering.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/support/simple_report.rb'
+
+# Offense count: 2
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
+# SupportedStyles: slashes, percent_r, mixed
+Style/RegexpLiteral:
+  Exclude:
+    - 'lib/datagrid/scaffold.rb'
+
+# Offense count: 4
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
+# AllowedMethods: present?, blank?, presence, try, try!
+Style/SafeNavigation:
+  Exclude:
+    - 'lib/datagrid/column_names_attribute.rb'
+    - 'lib/datagrid/filters/base_filter.rb'
+    - 'lib/datagrid/filters/string_filter.rb'
+    - 'lib/datagrid/renderer.rb'
+
+# Offense count: 4
+# This cop supports safe autocorrection (--autocorrect).
+Style/StderrPuts:
+  Exclude:
+    - 'Rakefile'
+    - 'spec/spec_helper.rb'
+
+# Offense count: 17
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Mode.
+Style/StringConcatenation:
+  Exclude:
+    - 'lib/datagrid/scaffold.rb'
+    - 'lib/tasks/datagrid_tasks.rake'
+    - 'spec/datagrid/form_builder_spec.rb'
+
+# Offense count: 1090
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
+# SupportedStyles: single_quotes, double_quotes
+Style/StringLiterals:
+  Enabled: false
+
+# Offense count: 1
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/StructInheritance:
+  Exclude:
+    - 'spec/datagrid/drivers/array_spec.rb'
+
+# Offense count: 20
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: MinSize.
+# SupportedStyles: percent, brackets
+Style/SymbolArray:
+  EnforcedStyle: brackets
+
+# Offense count: 1
+# This cop supports safe autocorrection (--autocorrect).
+Style/SymbolLiteral:
+  Exclude:
+    - 'spec/datagrid/columns_spec.rb'
+
+# Offense count: 6
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
+# AllowedMethods: define_method
+Style/SymbolProc:
+  Exclude:
+    - 'lib/datagrid/column_names_attribute.rb'
+    - 'spec/datagrid/filters/enum_filter_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/support/simple_report.rb'
+
+# Offense count: 9
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleForMultiline.
+# SupportedStylesForMultiline: comma, consistent_comma, no_comma
+Style/TrailingCommaInArguments:
+  Exclude:
+    - 'lib/datagrid/column_names_attribute.rb'
+    - 'lib/datagrid/form_builder.rb'
+    - 'spec/datagrid/drivers/active_record_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleForMultiline.
+# SupportedStylesForMultiline: comma, consistent_comma, no_comma
+Style/TrailingCommaInArrayLiteral:
+  Exclude:
+    - 'datagrid.gemspec'
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/filters/dynamic_filter.rb'
+    - 'spec/datagrid/drivers/array_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/spec_helper.rb'
+
+# Offense count: 6
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleForMultiline.
+# SupportedStylesForMultiline: comma, consistent_comma, no_comma
+Style/TrailingCommaInHashLiteral:
+  Exclude:
+    - 'datagrid.gemspec'
+    - 'lib/datagrid/drivers/mongoid.rb'
+    - 'lib/datagrid/form_builder.rb'
+    - 'lib/datagrid/scaffold.rb'
+    - 'spec/datagrid/filters_spec.rb'
+
+# Offense count: 31
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, MinSize, WordRegex.
+# SupportedStyles: percent, brackets
+Style/WordArray:
+  Exclude:
+    - 'spec/datagrid/column_names_attribute_spec.rb'
+    - 'spec/datagrid/columns_spec.rb'
+    - 'spec/datagrid/core_spec.rb'
+    - 'spec/datagrid/drivers/array_spec.rb'
+    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
+    - 'spec/datagrid/drivers/mongoid_spec.rb'
+    - 'spec/datagrid/drivers/sequel_spec.rb'
+    - 'spec/datagrid/filters/date_filter_spec.rb'
+    - 'spec/datagrid/filters/extended_boolean_filter_spec.rb'
+    - 'spec/datagrid/filters/string_filter_spec.rb'
+    - 'spec/datagrid/filters_spec.rb'
+    - 'spec/datagrid/form_builder_spec.rb'
+    - 'spec/datagrid/helper_spec.rb'
+    - 'spec/support/simple_report.rb'
+
+# Offense count: 36
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
+# URISchemes: http, https
+Layout/LineLength:
+  Max: 250
diff --git a/Gemfile b/Gemfile
index cf03de9..909dbe7 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,13 +3,13 @@ source "https://rubygems.org"
 gemspec
 
 group :development do
-  gem "appraisal"
   gem "activerecord"
+  gem "appraisal"
   gem "bump"
   gem "csv" # removed from standard library in Ruby 3.4
+  gem "debug"
   gem "nokogiri" # used to test html output
   gem "pry-byebug"
-  gem 'debug'
   gem "rails-dom-testing", "~> 2.2"
   gem "rspec"
   gem "sequel"
@@ -20,3 +20,5 @@ group :development do
     gem "mongoid", "~> 9.0"
   end
 end
+
+gem "rubocop", "~> 1.68", group: :development
diff --git a/Rakefile b/Rakefile
index 7fd32b9..4de31ea 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,29 +1,27 @@
-# encoding: utf-8
 # frozen_string_literal: true
 
-require 'rubygems'
-require 'bundler'
+require "rubygems"
+require "bundler"
 begin
   Bundler.setup(:default, :development)
 rescue Bundler::BundlerError => e
-  $stderr.puts e.message
-  $stderr.puts "Run `bundle install` to install missing gems"
+  warn e.message
+  warn "Run `bundle install` to install missing gems"
   exit e.status_code
 end
-require 'rake'
-require 'bundler/gem_tasks'
+require "rake"
+require "bundler/gem_tasks"
 
-require 'rspec/core'
-require 'rspec/core/rake_task'
+require "rspec/core"
+require "rspec/core/rake_task"
 
 RSpec::Core::RakeTask.new(:spec) do |spec|
-  spec.pattern = FileList['spec/**/*_spec.rb']
+  spec.pattern = FileList["spec/**/*_spec.rb"]
 end
 
 RSpec::Core::RakeTask.new(:rcov) do |spec|
-  spec.pattern = 'spec/**/*_spec.rb'
+  spec.pattern = "spec/**/*_spec.rb"
   spec.rcov = true
 end
 
 task default: :spec
-
diff --git a/datagrid.gemspec b/datagrid.gemspec
index 44299c9..662bdee 100644
--- a/datagrid.gemspec
+++ b/datagrid.gemspec
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
     "LICENSE.txt",
     "CHANGELOG.md",
     "Readme.markdown",
-    "datagrid.gemspec",
+    "datagrid.gemspec"
   ]
   s.files += `git ls-files | grep -E '^(app|lib|templates)'`.split("\n")
   s.homepage = "https://github.com/bogdan/datagrid"
@@ -29,9 +29,8 @@ Gem::Specification.new do |s|
     "bug_tracker_uri" => "#{s.homepage}/issues",
     "documentation_uri" => "#{s.homepage}/wiki",
     "changelog_uri" => "#{s.homepage}/blob/master/CHANGELOG.md",
-    "source_code_uri" => s.homepage,
+    "source_code_uri" => s.homepage
   }
 
   s.add_dependency "railties", ">= 6.1"
 end
-
diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile
index d92e1f4..512aa8a 100644
--- a/gemfiles/rails_6.1.gemfile
+++ b/gemfiles/rails_6.1.gemfile
@@ -8,10 +8,10 @@ group :development do
   gem "csv"
   gem "nokogiri"
   gem "pry-byebug"
+  gem "rails", "~> 6.1.0"
   gem "rspec"
   gem "sequel"
   gem "sqlite3", "~> 1.7.0"
-  gem "rails", "~> 6.1.0"
 
   group :mongo do
     gem "bson"
diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile
index 92a7e74..61566c8 100644
--- a/gemfiles/rails_7.0.gemfile
+++ b/gemfiles/rails_7.0.gemfile
@@ -8,10 +8,10 @@ group :development do
   gem "csv"
   gem "nokogiri"
   gem "pry-byebug"
+  gem "rails", "~> 7.0.0"
   gem "rspec"
   gem "sequel"
   gem "sqlite3", "~> 1.7.0"
-  gem "rails", "~> 7.0.0"
 
   group :mongo do
     gem "bson"
diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile
index ab3907f..bdd7ff4 100644
--- a/gemfiles/rails_7.1.gemfile
+++ b/gemfiles/rails_7.1.gemfile
@@ -8,10 +8,10 @@ group :development do
   gem "csv"
   gem "nokogiri"
   gem "pry-byebug"
+  gem "rails", "~> 7.1.0"
   gem "rspec"
   gem "sequel"
   gem "sqlite3", "~> 1.7.0"
-  gem "rails", "~> 7.1.0"
 
   group :mongo do
     gem "bson"
diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile
index d1385b0..753268c 100644
--- a/gemfiles/rails_7.2.gemfile
+++ b/gemfiles/rails_7.2.gemfile
@@ -8,10 +8,10 @@ group :development do
   gem "csv"
   gem "nokogiri"
   gem "pry-byebug"
+  gem "rails", "~> 7.2.0"
   gem "rspec"
   gem "sequel"
   gem "sqlite3", "~> 2.0.0"
-  gem "rails", "~> 7.2.0"
 
   group :mongo do
     gem "bson"
diff --git a/lib/datagrid.rb b/lib/datagrid.rb
index b8fb9aa..82abe93 100644
--- a/lib/datagrid.rb
+++ b/lib/datagrid.rb
@@ -3,7 +3,6 @@
 require "datagrid/engine"
 
 module Datagrid
-
   extend ActiveSupport::Autoload
 
   autoload :Core
@@ -24,24 +23,19 @@ module Datagrid
   # @!visibility private
   def self.included(base)
     base.class_eval do
-
       include ::Datagrid::Core
       include ::Datagrid::ActiveModel
       include ::Datagrid::Filters
       include ::Datagrid::Columns
       include ::Datagrid::ColumnNamesAttribute
       include ::Datagrid::Ordering
-
     end
   end
 
   class ConfigurationError < StandardError; end
   class ArgumentError < ::ArgumentError; end
   class ColumnUnavailableError < StandardError; end
-
 end
 
 require "datagrid/scaffold"
-I18n.load_path << File.expand_path('../datagrid/locale/en.yml', __FILE__)
-
-
+I18n.load_path << File.expand_path("datagrid/locale/en.yml", __dir__)
diff --git a/lib/datagrid/active_model.rb b/lib/datagrid/active_model.rb
index deb7184..5284711 100644
--- a/lib/datagrid/active_model.rb
+++ b/lib/datagrid/active_model.rb
@@ -3,15 +3,15 @@ module Datagrid
   module ActiveModel
     # @!visibility private
     def self.included(base)
-      base.extend         ClassMethods
+      base.extend ClassMethods
       base.class_eval do
         begin
-          require 'active_model/naming'
+          require "active_model/naming"
           extend ::ActiveModel::Naming
         rescue LoadError
         end
         begin
-          require 'active_model/attributes_assignment'
+          require "active_model/attributes_assignment"
           extend ::ActiveModel::AttributesAssignment
         rescue LoadError
         end
@@ -21,7 +21,7 @@ def self.included(base)
     module ClassMethods
       # @return [String] URL query parameter name of the grid class
       def param_name
-        self.to_s.underscore.tr('/', '_')
+        to_s.underscore.tr("/", "_")
       end
     end
 
@@ -48,7 +48,7 @@ def to_model
     end
 
     def to_param
-      self.param_name
+      param_name
     end
   end
 end
diff --git a/lib/datagrid/column_names_attribute.rb b/lib/datagrid/column_names_attribute.rb
index ae1a344..c04027c 100644
--- a/lib/datagrid/column_names_attribute.rb
+++ b/lib/datagrid/column_names_attribute.rb
@@ -31,7 +31,7 @@ def column_names_filter(**options)
           select: :optional_columns_select,
           multiple: true,
           dummy: true,
-          **options,
+          **options
         )
       end
     end
@@ -45,7 +45,7 @@ def columns(*args, **options)
     # If no mandatory columns specified than all of them considered mandatory
     # @return [Array<Datagrid::Columns::Column>]
     def mandatory_columns
-      available_columns.select {|c| c.mandatory? }
+      available_columns.select { |c| c.mandatory? }
     end
 
     # Returns a list of enabled columns without <tt>mandatory: true</tt> option
@@ -58,7 +58,7 @@ def optional_columns
     protected
 
     def optional_columns_select
-      optional_columns.map {|c| [c.header, c.name] }
+      optional_columns.map { |c| [c.header, c.name] }
     end
 
     def selected_column_names(*args)
@@ -68,12 +68,10 @@ def selected_column_names(*args)
           column.is_a?(Datagrid::Columns::Column) ? column.name : column.to_sym
         end
         args
+      elsif column_names && column_names.any?
+        column_names + mandatory_columns.map(&:name)
       else
-        if column_names && column_names.any?
-          column_names + mandatory_columns.map(&:name)
-        else
-          columns_enabled_by_default.map(&:name)
-        end
+        columns_enabled_by_default.map(&:name)
       end
     end
 
@@ -88,4 +86,3 @@ def columns_enabled_by_default
     end
   end
 end
-
diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index b8bba41..d2fd850 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -2,7 +2,6 @@
 require "active_support/core_ext/class/attribute"
 
 module Datagrid
-
   module Columns
     require "datagrid/columns/column"
 
@@ -34,7 +33,7 @@ module Columns
 
     # @visibility private
     def self.included(base)
-      base.extend         ClassMethods
+      base.extend ClassMethods
       base.class_eval do
         include Datagrid::Core
 
@@ -47,7 +46,6 @@ def self.included(base)
     end
 
     module ClassMethods
-
       # @param data [Boolean] if true returns only columns with data representation. Default: false.
       # @param html [Boolean] if true returns only columns with html columns. Default: false.
       # @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
@@ -146,23 +144,25 @@ def decorate(model = nil, &block)
         end
         return self.decorator = block unless model
         return model unless decorator
+
         presenter = ::Datagrid::Utils.apply_args(model, &decorator)
-        presenter = presenter.is_a?(Class) ?  presenter.new(model) : presenter
+        presenter = presenter.is_a?(Class) ? presenter.new(model) : presenter
         block_given? ? yield(presenter) : presenter
       end
 
       # @!visibility private
       def inherited(child_class)
         super(child_class)
-        child_class.columns_array = self.columns_array.clone
+        child_class.columns_array = columns_array.clone
       end
 
       # @!visibility private
       def filter_columns(columns_array, *names, data: false, html: false)
         names.compact!
-        if names.size >= 1 && names.all? {|n| n.is_a?(Datagrid::Columns::Column) && n.grid_class == self.class}
+        if names.size >= 1 && names.all? { |n| n.is_a?(Datagrid::Columns::Column) && n.grid_class == self.class }
           return names
         end
+
         names.map!(&:to_sym)
         columns_array.select do |column|
           (!data || column.data?) &&
@@ -186,13 +186,13 @@ def define_column(columns, name, query = nil, **options, &block)
       end
 
       # @!visibility private
-      def find_column_by_name(columns,name)
+      def find_column_by_name(columns, name)
         return name if name.is_a?(Datagrid::Columns::Column)
+
         columns.find do |col|
           col.name.to_sym == name.to_sym
         end
       end
-
     end
 
     # @!visibility private
@@ -223,7 +223,7 @@ def row_for(asset, *column_names)
     # @return [Hash] A mapping where keys are column names and values are column values for the given asset
     def hash_for(asset)
       result = {}
-      self.data_columns.each do |column|
+      data_columns.each do |column|
         result[column.name] = data_value(column, asset)
       end
       result
@@ -233,14 +233,14 @@ def hash_for(asset)
     # @return [Array<Array<Object>>] with data for each row in datagrid assets without header
     def rows(*column_names)
       map_with_batches do |asset|
-        self.row_for(asset, *column_names)
+        row_for(asset, *column_names)
       end
     end
 
     # @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
     # @return [Array<Array<Object>>] data for each row in datagrid assets with header.
     def data(*column_names)
-      self.rows(*column_names).unshift(self.header(*column_names))
+      rows(*column_names).unshift(header(*column_names))
     end
 
     # @return [Array<{Symbol => Object}>] an array of hashes representing the rows in the filtered datagrid relation
@@ -273,7 +273,7 @@ def data_hash
     def to_csv(*column_names, **options)
       require "csv"
       CSV.generate(
-        headers: self.header(*column_names),
+        headers: header(*column_names),
         write_headers: true,
         **options
       ) do |csv|
@@ -283,7 +283,6 @@ def to_csv(*column_names, **options)
       end
     end
 
-
     # @param column_names [Array<Symbol, String>]
     # @return [Array<Datagrid::Columns::Column>] all columns selected in grid instance
     # @example
@@ -302,13 +301,13 @@ def columns(*column_names, data: false, html: false)
     # @param column_names [Array<String, Symbol>] list of column names if you want to limit data only to specified columns
     # @return [Array<Datagrid::Columns::Column>] columns that can be represented in plain data(non-html) way
     def data_columns(*column_names, **options)
-      self.columns(*column_names, **options, data: true)
+      columns(*column_names, **options, data: true)
     end
 
     # @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
     # @return all columns that can be represented in HTML table
     def html_columns(*column_names, **options)
-      self.columns(*column_names, **options, html: true)
+      columns(*column_names, **options, html: true)
     end
 
     # Finds a column definition by name
@@ -401,6 +400,7 @@ def data_value(column_name, asset)
       column = column_by_name(column_name)
       cache(column, asset, :data_value) do
         raise "no data value for #{column.name} column" unless column.data?
+
         result = generic_value(column, asset)
         result.is_a?(Datagrid::Columns::Column::ResponseFormat) ? result.call_data : result
       end
@@ -408,7 +408,7 @@ def data_value(column_name, asset)
 
     # @return [Object] a cell HTML value for given column name and asset and view context
     def html_value(column_name, context, asset)
-      column  = column_by_name(column_name)
+      column = column_by_name(column_name)
       cache(column, asset, :html_value) do
         if column.html? && column.html_block
           value_from_html_block(context, asset, column)
@@ -461,9 +461,8 @@ def cache(column, asset, type)
         return yield
       end
       key = cache_key(asset)
-      unless key
-        raise(Datagrid::CacheKeyError, "Datagrid Cache key is #{key.inspect} for #{asset.inspect}.")
-      end
+      raise(Datagrid::CacheKeyError, "Datagrid Cache key is #{key.inspect} for #{asset.inspect}.") unless key
+
       @cache[column.name] ||= {}
       @cache[column.name][key] ||= {}
       @cache[column.name][key][type] ||= yield
@@ -476,10 +475,10 @@ def cache_key(asset)
         driver.default_cache_key(asset)
       end
     rescue NotImplementedError
-      raise Datagrid::ConfigurationError, "#{self} is setup to use cache. But there was appropriate cache key found for #{asset.inspect}. Please set cached option to block with asset as argument and cache key as returning value to resolve the issue."
+      raise Datagrid::ConfigurationError,
+            "#{self} is setup to use cache. But there was appropriate cache key found for #{asset.inspect}. Please set cached option to block with asset as argument and cache key as returning value to resolve the issue."
     end
 
-
     def map_with_batches(&block)
       result = []
       each_with_batches do |asset|
@@ -522,7 +521,7 @@ def initialize(grid, model)
         @model = model
       end
 
-      def method_missing(meth, *args, &blk)
+      def method_missing(meth, *_args)
         @grid.data_value(meth, @model)
       end
     end
diff --git a/lib/datagrid/columns/column.rb b/lib/datagrid/columns/column.rb
index 7236202..1d50c40 100644
--- a/lib/datagrid/columns/column.rb
+++ b/lib/datagrid/columns/column.rb
@@ -1,9 +1,7 @@
 class Datagrid::Columns::Column
-
   # Datagrid class holding an information of
   # how a column should be rendered in data/console/csv format and HTML format
   class ResponseFormat
-
     attr_accessor :data_block, :html_block
 
     def initialize
@@ -42,9 +40,7 @@ def initialize(grid_class, name, query, options = {}, &block)
     else
       self.data_block = block
 
-      if options[:html].is_a? Proc
-        self.html_block = options[:html]
-      end
+      self.html_block = options[:html] if options[:html].is_a? Proc
     end
     self.query = query
   end
@@ -54,9 +50,8 @@ def data_value(model, grid)
     grid.data_value(name, model)
   end
 
-
   def label
-    self.options[:label]
+    options[:label]
   end
 
   def header
@@ -69,7 +64,7 @@ def header
 
   def order
     if options.has_key?(:order) && options[:order] != true
-      self.options[:order]
+      options[:order]
     else
       driver.default_order(grid_class.scope, name)
     end
@@ -88,12 +83,13 @@ def order_by_value(model, grid)
   end
 
   def order_by_value?
-    !! options[:order_by_value]
+    !!options[:order_by_value]
   end
 
   def order_desc
     return nil unless order
-    self.options[:order_desc]
+
+    options[:order_desc]
   end
 
   def html?
@@ -101,11 +97,11 @@ def html?
   end
 
   def data?
-    self.data_block != nil
+    data_block != nil
   end
 
   def mandatory?
-    !! options[:mandatory]
+    !!options[:mandatory]
   end
 
   def mandatory_explicitly_set?
@@ -128,15 +124,16 @@ def html_value(context, asset, grid)
     grid.html_value(name, context, asset)
   end
 
-
   def generic_value(model, grid)
     grid.generic_value(self, model)
   end
 
   def append_preload(relation)
     return relation unless preload
+
     if preload.respond_to?(:call)
       return relation unless preload
+
       if preload.arity == 1
         preload.call(relation)
       else
@@ -155,7 +152,6 @@ def preload
     else
       preload
     end
-
   end
 
   def driver
diff --git a/lib/datagrid/configuration.rb b/lib/datagrid/configuration.rb
index 0032c2b..65a1653 100644
--- a/lib/datagrid/configuration.rb
+++ b/lib/datagrid/configuration.rb
@@ -1,5 +1,4 @@
 module Datagrid
-
   def self.configuration
     @configuration ||= Configuration.new
   end
diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index 4444aca..0f560a4 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -18,21 +18,20 @@ def self.included(base)
     end
 
     module ClassMethods
-
       # @!visibility private
       def datagrid_attribute(name, &block)
-        unless datagrid_attributes.include?(name)
-          block ||= lambda do |value|
-            value
-          end
-          datagrid_attributes << name
-          define_method name do
-            instance_variable_get("@#{name}")
-          end
+        return if datagrid_attributes.include?(name)
 
-          define_method :"#{name}=" do |value|
-            instance_variable_set("@#{name}", instance_exec(value, &block))
-          end
+        block ||= lambda do |value|
+          value
+        end
+        datagrid_attributes << name
+        define_method name do
+          instance_variable_get("@#{name}")
+        end
+
+        define_method :"#{name}=" do |value|
+          instance_variable_set("@#{name}", instance_exec(value, &block))
         end
       end
 
@@ -117,9 +116,8 @@ def check_scope_defined!(message = nil)
 
       def inherited(child_class)
         super(child_class)
-        child_class.datagrid_attributes = self.datagrid_attributes.clone
+        child_class.datagrid_attributes = datagrid_attributes.clone
       end
-
     end
 
     # @param [{String, Symbol => Object}, nil] attributes a hash of attributes to initialize the object
@@ -128,21 +126,18 @@ def inherited(child_class)
     def initialize(attributes = nil, &block)
       super()
 
-      if attributes
-        self.attributes = attributes
-      end
+      self.attributes = attributes if attributes
 
       instance_eval(&dynamic_block) if dynamic_block
-      if block_given?
-        self.scope(&block)
-      end
-    end
+      return unless block_given?
 
+      scope(&block)
+    end
 
     # @return [Hash<Symbol, Object>] grid attributes including filter values and ordering values
     def attributes
       result = {}
-      self.datagrid_attributes.each do |name|
+      datagrid_attributes.each do |name|
         result[name] = self[name]
       end
       result
@@ -161,7 +156,7 @@ def attributes=(attributes)
 
     # @return [Object] Any datagrid attribute value
     def [](attribute)
-      self.public_send(attribute)
+      public_send(attribute)
     end
 
     # Assigns any datagrid attribute
@@ -169,7 +164,7 @@ def [](attribute)
     # @param value [Object] Datagrid attribute value
     # @return [void]
     def []=(attribute, value)
-      self.public_send(:"#{attribute}=", value)
+      public_send(:"#{attribute}=", value)
     end
 
     # @return [Object] a scope relation (e.g ActiveRecord::Relation) with all applied filters
@@ -270,6 +265,7 @@ def reset
     end
 
     protected
+
     def sanitize_for_mass_assignment(attributes)
       forbidden_attributes_protection ? super(attributes) : attributes
     end
diff --git a/lib/datagrid/drivers.rb b/lib/datagrid/drivers.rb
index 6194337..3e72d3f 100644
--- a/lib/datagrid/drivers.rb
+++ b/lib/datagrid/drivers.rb
@@ -5,7 +5,6 @@
 require "datagrid/drivers/mongo_mapper"
 require "datagrid/drivers/sequel"
 
-
 module Datagrid
   # @!visibility private
   module Drivers
diff --git a/lib/datagrid/drivers/abstract_driver.rb b/lib/datagrid/drivers/abstract_driver.rb
index bf9d940..99530a8 100644
--- a/lib/datagrid/drivers/abstract_driver.rb
+++ b/lib/datagrid/drivers/abstract_driver.rb
@@ -2,18 +2,17 @@ module Datagrid
   module Drivers
     # @!visibility private
     class AbstractDriver
-
       TIMESTAMP_CLASSES = [DateTime, Time, ActiveSupport::TimeWithZone]
 
       class_attribute :subclasses, default: []
 
       def self.inherited(base)
         super(base)
-        self.subclasses << base
+        subclasses << base
       end
 
       def self.guess_driver(scope)
-        self.subclasses.find do |driver_class|
+        subclasses.find do |driver_class|
           driver_class.match?(scope)
         end || raise(Datagrid::ConfigurationError, "ORM Driver not found for scope: #{scope.inspect}.")
       end
@@ -83,11 +82,9 @@ def batch_each(scope, batch_size, &block)
       end
 
       def append_column_queries(assets, columns)
-        if columns.present?
-          raise NotImplementedError
-        else
-          assets
-        end
+        raise NotImplementedError if columns.present?
+
+        assets
       end
 
       def default_cache_key(asset)
@@ -96,12 +93,8 @@ def default_cache_key(asset)
 
       def where_by_timestamp_gotcha(scope, name, value)
         value = Datagrid::Utils.format_date_as_timestamp(value)
-        if value.first
-          scope = greater_equal(scope, name, value.first)
-        end
-        if value.last
-          scope = less_equal(scope, name, value.last)
-        end
+        scope = greater_equal(scope, name, value.first) if value.first
+        scope = less_equal(scope, name, value.last) if value.last
         scope
       end
 
@@ -114,6 +107,7 @@ def can_preload?(scope, association)
       end
 
       protected
+
       def timestamp_class?(klass)
         TIMESTAMP_CLASSES.include?(klass)
       end
diff --git a/lib/datagrid/drivers/active_record.rb b/lib/datagrid/drivers/active_record.rb
index 25224e3..3135df8 100644
--- a/lib/datagrid/drivers/active_record.rb
+++ b/lib/datagrid/drivers/active_record.rb
@@ -2,9 +2,9 @@ module Datagrid
   module Drivers
     # @!visibility private
     class ActiveRecord < AbstractDriver
-
       def self.match?(scope)
         return false unless defined?(::ActiveRecord)
+
         if scope.is_a?(Class)
           scope.ancestors.include?(::ActiveRecord::Base)
         else
@@ -14,6 +14,7 @@ def self.match?(scope)
 
       def to_scope(scope)
         return scope if scope.is_a?(::ActiveRecord::Relation)
+
         # Model class or Active record association
         # ActiveRecord association class hides itself under an Array
         # We can only reveal it by checking if it respond to some specific
@@ -32,7 +33,7 @@ def append_column_queries(assets, columns)
           if assets.select_values.empty?
             assets = assets.select(Arel.respond_to?(:star) ? assets.klass.arel_table[Arel.star] : "#{assets.quoted_table_name}.*")
           end
-          columns = columns.map {|c| "#{c.query} AS #{c.name}"}
+          columns = columns.map { |c| "#{c.query} AS #{c.name}" }
           assets = assets.select(*columns)
         end
         assets
@@ -89,13 +90,14 @@ def contains(scope, field, value)
 
       def normalized_column_type(scope, field)
         return nil unless has_column?(scope, field)
+
         builtin_type = scope.columns_hash[field.to_s].type
         {
-          [:string, :text, :time, :binary] => :string,
-          [:integer, :primary_key] => :integer,
-          [:float, :decimal] => :float,
+          %i[string text time binary] => :string,
+          %i[integer primary_key] => :integer,
+          %i[float decimal] => :float,
           [:date] => :date,
-          [:datetime, :timestamp] => :timestamp,
+          %i[datetime timestamp] => :timestamp,
           [:boolean] => :boolean
         }.each do |keys, value|
           return value if keys.include?(builtin_type)
@@ -106,6 +108,7 @@ def batch_each(scope, batch_size, &block)
         if scope.limit_value
           raise Datagrid::ConfigurationError, "ActiveRecord can not use batches in combination with SQL limit"
         end
+
         options = batch_size ? { batch_size: batch_size } : {}
         scope.find_each(**options, &block)
       end
@@ -119,19 +122,22 @@ def default_preload(scope, value)
       end
 
       def can_preload?(scope, association)
-        !! scope.klass.reflect_on_association(association)
+        !!scope.klass.reflect_on_association(association)
       end
 
       protected
 
       def prefix_table_name(scope, field)
-        has_column?(scope, field) ?  [scope.table_name, field].join(".") : field
+        has_column?(scope, field) ? [scope.table_name, field].join(".") : field
       end
 
       def contains_predicate
-        defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) &&
-          ::ActiveRecord::Base.connection.is_a?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) ?
-          'ilike' : 'like'
+        if defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) &&
+           ::ActiveRecord::Base.connection.is_a?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
+          "ilike"
+        else
+          "like"
+        end
       end
     end
   end
diff --git a/lib/datagrid/drivers/array.rb b/lib/datagrid/drivers/array.rb
index 99d320c..3d2da1d 100644
--- a/lib/datagrid/drivers/array.rb
+++ b/lib/datagrid/drivers/array.rb
@@ -2,7 +2,6 @@ module Datagrid
   module Drivers
     # @!visibility private
     class Array < AbstractDriver
-
       def self.match?(scope)
         !Datagrid::Drivers::ActiveRecord.match?(scope) && (
           scope.is_a?(::Array) || scope.is_a?(Enumerator) ||
@@ -23,6 +22,7 @@ def where(scope, attribute, value)
       def asc(scope, order)
         return scope unless order
         return scope if order.empty?
+
         scope.sort_by do |object|
           get(object, order)
         end
@@ -32,7 +32,7 @@ def desc(scope, order)
         asc(scope, order).reverse
       end
 
-      def default_order(scope, column_name)
+      def default_order(_scope, column_name)
         column_name
       end
 
@@ -67,11 +67,11 @@ def contains(scope, field, value)
         end
       end
 
-      def column_names(scope)
+      def column_names(_scope)
         []
       end
 
-      def batch_each(scope, batch_size, &block)
+      def batch_each(scope, _batch_size, &block)
         scope.each(&block)
       end
 
@@ -79,7 +79,7 @@ def default_cache_key(asset)
         asset
       end
 
-      def can_preload?(scope, association)
+      def can_preload?(_scope, _association)
         false
       end
 
diff --git a/lib/datagrid/drivers/mongo_mapper.rb b/lib/datagrid/drivers/mongo_mapper.rb
index 6d7d55b..e36fa51 100644
--- a/lib/datagrid/drivers/mongo_mapper.rb
+++ b/lib/datagrid/drivers/mongo_mapper.rb
@@ -2,9 +2,9 @@ module Datagrid
   module Drivers
     # @!visibility private
     class MongoMapper < AbstractDriver
-
       def self.match?(scope)
         return false unless defined?(::MongoMapper)
+
         if scope.is_a?(Class)
           scope.ancestors.include?(::MongoMapper::Document)
         else
@@ -33,27 +33,27 @@ def default_order(scope, column_name)
       end
 
       def greater_equal(scope, field, value)
-        scope.where(field => {"$gte" => value})
+        scope.where(field => { "$gte" => value })
       end
 
       def less_equal(scope, field, value)
-        scope.where(field => {"$lte" => value})
+        scope.where(field => { "$lte" => value })
       end
 
       def has_column?(scope, column_name)
         scope.key?(column_name)
       end
 
-      def is_timestamp?(scope, column_name)
-        #TODO implement the support
+      def is_timestamp?(_scope, _column_name)
+        # TODO: implement the support
         false
       end
 
-      def contains(scope, field, value)
+      def contains(_scope, field, value)
         scope(field => Regexp.compile(Regexp.escape(value)))
       end
 
-      def column_names(scope)
+      def column_names(_scope)
         [] # TODO: implement support
       end
 
@@ -62,10 +62,9 @@ def batch_each(scope, batch_size, &block)
         loop do
           batch = scope.skip(current_page * batch_size).limit(batch_size).to_a
           return if batch.empty?
-          scope.skip(current_page * batch_size).limit(batch_size).each do |item|
-            yield(item)
-          end
-          current_page+=1
+
+          scope.skip(current_page * batch_size).limit(batch_size).each(&block)
+          current_page += 1
         end
       end
 
@@ -77,7 +76,7 @@ def default_preload(scope, value)
         raise NotImplementedError
       end
 
-      def can_preload?(scope, association)
+      def can_preload?(_scope, _association)
         false
       end
     end
diff --git a/lib/datagrid/drivers/mongoid.rb b/lib/datagrid/drivers/mongoid.rb
index 5bb30c2..c737ff4 100644
--- a/lib/datagrid/drivers/mongoid.rb
+++ b/lib/datagrid/drivers/mongoid.rb
@@ -2,9 +2,9 @@ module Datagrid
   module Drivers
     # @!visibility private
     class Mongoid < AbstractDriver
-
       def self.match?(scope)
         return false unless defined?(::Mongoid)
+
         if scope.is_a?(Class)
           scope.ancestors.include?(::Mongoid::Document)
         else
@@ -21,9 +21,7 @@ def to_scope(scope)
       end
 
       def where(scope, attribute, value)
-        if value.is_a?(Range)
-          value = {"$gte" => value.first, "$lte" => value.last}
-        end
+        value = { "$gte" => value.first, "$lte" => value.last } if value.is_a?(Range)
         scope.where(attribute => value)
       end
 
@@ -40,11 +38,11 @@ def default_order(scope, column_name)
       end
 
       def greater_equal(scope, field, value)
-        scope.where(field => {"$gte" => value})
+        scope.where(field => { "$gte" => value })
       end
 
       def less_equal(scope, field, value)
-        scope.where(field => {"$lte" => value})
+        scope.where(field => { "$lte" => value })
       end
 
       def has_column?(scope, column_name)
@@ -62,8 +60,9 @@ def column_names(scope)
       def normalized_column_type(scope, field)
         type = to_scope(scope).klass.fields[field.to_s]&.type
         return nil unless type
+
         {
-          [BigDecimal , String, Symbol, Range, Array, Hash, ] => :string,
+          [BigDecimal, String, Symbol, Range, Array, Hash] => :string,
           [::Mongoid::Boolean] => :boolean,
 
           [Date] => :date,
@@ -72,11 +71,11 @@ def normalized_column_type(scope, field)
 
           [Float] => :float,
 
-          [Integer] => :integer,
+          [Integer] => :integer
         }.each do |keys, value|
           return value if keys.include?(type)
         end
-        return :string
+        :string
       end
 
       def batch_each(scope, batch_size, &block)
@@ -84,10 +83,9 @@ def batch_each(scope, batch_size, &block)
         loop do
           batch = scope.skip(current_page * batch_size).limit(batch_size).to_a
           return if batch.empty?
-          scope.skip(current_page * batch_size).limit(batch_size).each do |item|
-            yield(item)
-          end
-          current_page+=1
+
+          scope.skip(current_page * batch_size).limit(batch_size).each(&block)
+          current_page += 1
         end
       end
 
@@ -100,10 +98,8 @@ def default_preload(scope, value)
       end
 
       def can_preload?(scope, association)
-        !! scope.klass.reflect_on_association(association)
+        !!scope.klass.reflect_on_association(association)
       end
-
     end
   end
 end
-
diff --git a/lib/datagrid/drivers/sequel.rb b/lib/datagrid/drivers/sequel.rb
index 4ca8716..ce603b8 100644
--- a/lib/datagrid/drivers/sequel.rb
+++ b/lib/datagrid/drivers/sequel.rb
@@ -2,9 +2,9 @@ module Datagrid
   module Drivers
     # @!visibility private
     class Sequel < AbstractDriver
-
       def self.match?(scope)
         return false unless defined?(::Sequel)
+
         if scope.is_a?(Class)
           scope.ancestors.include?(::Sequel::Model)
         else
@@ -14,6 +14,7 @@ def self.match?(scope)
 
       def to_scope(scope)
         return scope if scope.is_a?(::Sequel::Dataset)
+
         scope.where({})
       end
 
@@ -38,7 +39,7 @@ def reverse_order(scope)
       end
 
       def default_order(scope, column_name)
-        has_column?(scope, column_name) ?  ::Sequel.lit(prefix_table_name(scope, column_name)) : nil
+        has_column?(scope, column_name) ? ::Sequel.lit(prefix_table_name(scope, column_name)) : nil
       end
 
       def greater_equal(scope, field, value)
@@ -65,10 +66,11 @@ def contains(scope, field, value)
       def normalized_column_type(scope, field)
         type = column_type(scope, field)
         return nil unless type
+
         {
-          [:string, :blob, :time] => :string,
-          [:integer, :primary_key] => :integer,
-          [:float, :decimal] => :float,
+          %i[string blob time] => :string,
+          %i[integer primary_key] => :integer,
+          %i[float decimal] => :float,
           [:date] => :date,
           [:datetime] => :timestamp,
           [:boolean] => :boolean
@@ -96,14 +98,13 @@ def default_preload(scope, value)
       end
 
       def can_preload?(scope, association)
-        !! scope.model.association_reflection(association)
+        !!scope.model.association_reflection(association)
       end
 
-
       protected
 
       def prefix_table_name(scope, field)
-        has_column?(scope, field) ?  [to_scope(scope).row_proc.table_name, field].join(".") : field
+        has_column?(scope, field) ? [to_scope(scope).row_proc.table_name, field].join(".") : field
       end
 
       def column_type(scope, field)
diff --git a/lib/datagrid/engine.rb b/lib/datagrid/engine.rb
index 11ae96b..b37dd4e 100644
--- a/lib/datagrid/engine.rb
+++ b/lib/datagrid/engine.rb
@@ -1,6 +1,6 @@
 require "rails/engine"
-require 'datagrid/helper'
-require 'datagrid/form_builder'
+require "datagrid/helper"
+require "datagrid/form_builder"
 
 module Datagrid
   # @!private
@@ -15,6 +15,5 @@ def self.extend_modules
         Engine.extend_modules
       end
     end
-
   end
 end
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index a453f1b..3283c81 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -2,7 +2,6 @@
 
 module Datagrid
   module Filters
-
     require "datagrid/filters/base_filter"
     require "datagrid/filters/enum_filter"
     require "datagrid/filters/extended_boolean_filter"
@@ -22,8 +21,8 @@ module Filters
       datetime: Filters::DateTimeFilter,
       string: Filters::StringFilter,
       default: Filters::DefaultFilter,
-      xboolean: Filters::ExtendedBooleanFilter ,
-      boolean: Filters::BooleanFilter ,
+      xboolean: Filters::ExtendedBooleanFilter,
+      boolean: Filters::BooleanFilter,
       integer: Filters::IntegerFilter,
       enum: Filters::EnumFilter,
       float: Filters::FloatFilter,
@@ -37,25 +36,23 @@ module Filters
     def self.included(base)
       base.extend ClassMethods
       base.class_eval do
-
         include Datagrid::Core
         include Datagrid::Filters::CompositeFilters
         class_attribute :filters_array, default: []
-
       end
     end
 
     module ClassMethods
-
       # @return [Datagrid::Filters::BaseFilter, nil] filter definition object by name
       def filter_by_name(attribute)
         if attribute.is_a?(Datagrid::Filters::BaseFilter)
           unless ancestors.include?(attribute.grid_class)
             raise ArgumentError, "#{attribute.grid_class}##{attribute.name} filter doen't belong to #{self.class}"
           end
+
           return attribute
         end
-        self.filters.find do |filter|
+        filters.find do |filter|
           filter.name == attribute.to_sym
         end
       end
@@ -129,11 +126,12 @@ def filters
 
       def inherited(child_class)
         super(child_class)
-        child_class.filters_array = self.filters_array.clone
+        child_class.filters_array = filters_array.clone
       end
 
       def filters_inspection
         return "no filters" if filters.empty?
+
         filters.map do |filter|
           "#{filter.name}: #{filter.type}"
         end.join(", ")
@@ -143,7 +141,7 @@ def filters_inspection
     # @!visibility private
     def initialize(*args, &block)
       self.filters_array = self.class.filters_array.clone
-      self.filters_array.each do |filter|
+      filters_array.each do |filter|
         self[filter.name] = filter.default(self)
       end
       super(*args, &block)
@@ -164,7 +162,7 @@ def filter_value_as_string(name)
       filter = filter_by_name(name)
       value = filter_value(filter)
       if value.is_a?(Array)
-        value.map {|v| filter.format(v) }.join(filter.separator)
+        value.map { |v| filter.format(v) }.join(filter.separator)
       else
         filter.format(value)
       end
@@ -177,7 +175,7 @@ def filter_by_name(name)
 
     # @return [Array<Object>] assets filtered only by specified filters
     def filter_by(*filters)
-      apply_filters(scope, filters.map{|f| filter_by_name(f)})
+      apply_filters(scope, filters.map { |f| filter_by_name(f) })
     end
 
     # @return [Array] the select options for the filter
@@ -215,7 +213,7 @@ def find_select_filter(filter)
       filter = filter_by_name(filter)
       unless filter.class.included_modules.include?(::Datagrid::Filters::SelectOptions)
         raise ::Datagrid::ArgumentError,
-          "#{self.class.name}##{filter.name} with type #{FILTER_TYPES.invert[filter.class].inspect} can not have select options"
+              "#{self.class.name}##{filter.name} with type #{FILTER_TYPES.invert[filter.class].inspect} can not have select options"
       end
       filter
     end
diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index 710d244..608e8d9 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -5,7 +5,6 @@ class Datagrid::FilteringError < StandardError
 
 # @!visibility private
 class Datagrid::Filters::BaseFilter
-
   class_attribute :input_helper_name, instance_writer: false
   attr_accessor :grid_class, :options, :block, :name
 
@@ -30,11 +29,11 @@ def apply(grid_object, scope, value)
     result = execute(value, scope, grid_object)
 
     return scope unless result
-    if result == Datagrid::Filters::DEFAULT_FILTER_BLOCK
-      result = default_filter(value, scope, grid_object)
-    end
+
+    result = default_filter(value, scope, grid_object) if result == Datagrid::Filters::DEFAULT_FILTER_BLOCK
     unless grid_object.driver.match?(result)
-      raise Datagrid::FilteringError, "Can not apply #{name.inspect} filter: result #{result.inspect} no longer match #{grid_object.driver.class}."
+      raise Datagrid::FilteringError,
+            "Can not apply #{name.inspect} filter: result #{result.inspect} no longer match #{grid_object.driver.class}."
     end
 
     result
@@ -43,6 +42,7 @@ def apply(grid_object, scope, value)
   def parse_values(value)
     if multiple?
       return nil if value.nil?
+
       normalize_multiple_value(value).map do |v|
         parse(v)
       end
@@ -66,7 +66,7 @@ def header
   end
 
   def default(object)
-    default = self.options[:default]
+    default = options[:default]
     if default.is_a?(Symbol)
       object.send(default)
     elsif default.respond_to?(:call)
@@ -77,7 +77,7 @@ def default(object)
   end
 
   def multiple?
-    self.options[:multiple]
+    options[:multiple]
   end
 
   def allow_nil?
@@ -101,7 +101,7 @@ def form_builder_helper_name
   end
 
   def self.form_builder_helper_name
-    :"datagrid_#{self.to_s.demodulize.underscore}"
+    :"datagrid_#{to_s.demodulize.underscore}"
   end
 
   def default_filter_block
@@ -125,9 +125,7 @@ def dummy?
 
   def type
     Datagrid::Filters::FILTER_TYPES.each do |type, klass|
-      if is_a?(klass)
-        return type
-      end
+      return type if is_a?(klass)
     end
     raise "wtf is #{inspect}"
   end
@@ -137,7 +135,7 @@ def enabled?(grid)
   end
 
   def default_html_classes
-    [ name, self.class.to_s.demodulize.underscore ]
+    [name, self.class.to_s.demodulize.underscore]
   end
 
   protected
@@ -168,15 +166,16 @@ def normalize_multiple_value(value)
   end
 
   def default_separator
-    ','
+    ","
   end
 
   def driver
     grid_class.driver
   end
 
-  def default_filter(value, scope, grid)
+  def default_filter(value, scope, _grid)
     return nil if dummy?
+
     if !driver.has_column?(scope, name) && scope.respond_to?(name, true)
       scope.public_send(name, value)
     else
@@ -184,4 +183,3 @@ def default_filter(value, scope, grid)
     end
   end
 end
-
diff --git a/lib/datagrid/filters/boolean_filter.rb b/lib/datagrid/filters/boolean_filter.rb
index 554fad7..89aed9b 100644
--- a/lib/datagrid/filters/boolean_filter.rb
+++ b/lib/datagrid/filters/boolean_filter.rb
@@ -1,9 +1,7 @@
 require "datagrid/utils"
 # @!visibility private
 class Datagrid::Filters::BooleanFilter < Datagrid::Filters::BaseFilter
-
   def parse(value)
     Datagrid::Utils.booleanize(value)
   end
-
 end
diff --git a/lib/datagrid/filters/composite_filters.rb b/lib/datagrid/filters/composite_filters.rb
index c81f358..3eb405a 100644
--- a/lib/datagrid/filters/composite_filters.rb
+++ b/lib/datagrid/filters/composite_filters.rb
@@ -2,22 +2,21 @@ module Datagrid
   module Filters
     # @!visibility private
     module CompositeFilters
-
       def self.included(base)
-        base.extend         ClassMethods
+        base.extend ClassMethods
         base.class_eval do
         end
       end
 
       # @!visibility private
       module ClassMethods
-
         def date_range_filters(field, from_options = {}, to_options = {})
-          Utils.warn_once('date_range_filters is deprecated in favor of range option for date filter')
+          Utils.warn_once("date_range_filters is deprecated in favor of range option for date filter")
           from_options = normalize_composite_filter_options(from_options, field)
           to_options = normalize_composite_filter_options(to_options, field)
 
-          filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :date, **from_options) do |date, scope, grid|
+          filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :date,
+                 **from_options) do |date, scope, grid|
             grid.driver.greater_equal(scope, field, date)
           end
           filter(to_options[:name] || :"to_#{field.to_s.tr('.', '_')}", :date, **to_options) do |date, scope, grid|
@@ -26,10 +25,11 @@ def date_range_filters(field, from_options = {}, to_options = {})
         end
 
         def integer_range_filters(field, from_options = {}, to_options = {})
-          Utils.warn_once('integer_range_filters is deprecated in favor of range option for integer filter')
+          Utils.warn_once("integer_range_filters is deprecated in favor of range option for integer filter")
           from_options = normalize_composite_filter_options(from_options, field)
           to_options = normalize_composite_filter_options(to_options, field)
-          filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :integer, **from_options) do |value, scope, grid|
+          filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :integer,
+                 **from_options) do |value, scope, grid|
             grid.driver.greater_equal(scope, field, value)
           end
           filter(to_options[:name] || :"to_#{field.to_s.tr('.', '_')}", :integer, **to_options) do |value, scope, grid|
@@ -37,10 +37,8 @@ def integer_range_filters(field, from_options = {}, to_options = {})
           end
         end
 
-        def normalize_composite_filter_options(options, field)
-          if options.is_a?(String) || options.is_a?(Symbol)
-            options = {name: options}
-          end
+        def normalize_composite_filter_options(options, _field)
+          options = { name: options } if options.is_a?(String) || options.is_a?(Symbol)
           options
         end
       end
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index db9c1d4..f9f47c9 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -1,13 +1,10 @@
 require "datagrid/filters/ranged_filter"
 
 class Datagrid::Filters::DateFilter < Datagrid::Filters::BaseFilter
-
   include Datagrid::Filters::RangedFilter
 
   def apply(grid_object, scope, value)
-    if value.is_a?(Range)
-      value = value.begin&.beginning_of_day..value.end&.end_of_day
-    end
+    value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
     super(grid_object, scope, value)
   end
 
@@ -15,7 +12,6 @@ def parse(value)
     Datagrid::Utils.parse_date(value)
   end
 
-
   def format(value)
     if formats.any? && value
       value.strftime(formats.first)
@@ -25,9 +21,7 @@ def format(value)
   end
 
   def default_filter_where(scope, value)
-    if driver.is_timestamp?(scope, name)
-      value = Datagrid::Utils.format_date_as_timestamp(value)
-    end
+    value = Datagrid::Utils.format_date_as_timestamp(value) if driver.is_timestamp?(scope, name)
     super(scope, value)
   end
 
@@ -37,4 +31,3 @@ def formats
     Array(Datagrid.configuration.date_formats)
   end
 end
-
diff --git a/lib/datagrid/filters/date_time_filter.rb b/lib/datagrid/filters/date_time_filter.rb
index 50d1331..9809f32 100644
--- a/lib/datagrid/filters/date_time_filter.rb
+++ b/lib/datagrid/filters/date_time_filter.rb
@@ -1,7 +1,6 @@
 require "datagrid/filters/ranged_filter"
 
 class Datagrid::Filters::DateTimeFilter < Datagrid::Filters::BaseFilter
-
   include Datagrid::Filters::RangedFilter
 
   def parse(value)
@@ -22,4 +21,3 @@ def formats
     Array(Datagrid.configuration.datetime_formats)
   end
 end
-
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index f3d0d0b..c2b29d3 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -1,28 +1,27 @@
 require "datagrid/filters/select_options"
 
 class Datagrid::Filters::DynamicFilter < Datagrid::Filters::BaseFilter
-
   include Datagrid::Filters::SelectOptions
 
-  EQUAL_OPERATION = '='
-  LIKE_OPERATION = '=~'
-  MORE_EQUAL_OPERATION = '>='
-  LESS_EQUAL_OPERATION = '<='
+  EQUAL_OPERATION = "="
+  LIKE_OPERATION = "=~"
+  MORE_EQUAL_OPERATION = ">="
+  LESS_EQUAL_OPERATION = "<="
   DEFAULT_OPERATIONS = [
     EQUAL_OPERATION,
     LIKE_OPERATION,
     MORE_EQUAL_OPERATION,
-    LESS_EQUAL_OPERATION,
+    LESS_EQUAL_OPERATION
   ]
-  AVAILABLE_OPERATIONS = %w(= =~ >= <=)
+  AVAILABLE_OPERATIONS = %w[= =~ >= <=]
 
   def initialize(*)
     super
     options[:select] ||= default_select
     options[:operations] ||= DEFAULT_OPERATIONS
-    unless options.has_key?(:include_blank)
-      options[:include_blank] = false
-    end
+    return if options.has_key?(:include_blank)
+
+    options[:include_blank] = false
   end
 
   def parse_values(filter)
@@ -41,36 +40,32 @@ def default_filter_where(scope, filter)
     date_conversion = value.is_a?(Date) && driver.is_timestamp?(scope, field)
 
     return scope if field.blank? || operation.blank?
+
     unless operations.include?(operation)
-      raise Datagrid::FilteringError, "Unknown operation: #{operation.inspect}. Available operations: #{operations.join(' ')}"
+      raise Datagrid::FilteringError,
+            "Unknown operation: #{operation.inspect}. Available operations: #{operations.join(' ')}"
     end
+
     case operation
     when EQUAL_OPERATION
-      if date_conversion
-        value = Datagrid::Utils.format_date_as_timestamp(value)
-      end
+      value = Datagrid::Utils.format_date_as_timestamp(value) if date_conversion
       driver.where(scope, field, value)
     when LIKE_OPERATION
       if column_type(field) == :string
         driver.contains(scope, field, value)
       else
-        if date_conversion
-          value = Datagrid::Utils.format_date_as_timestamp(value)
-        end
+        value = Datagrid::Utils.format_date_as_timestamp(value) if date_conversion
         driver.where(scope, field, value)
       end
     when MORE_EQUAL_OPERATION
-      if date_conversion
-        value = value.beginning_of_day
-      end
+      value = value.beginning_of_day if date_conversion
       driver.greater_equal(scope, field, value)
     when LESS_EQUAL_OPERATION
-      if date_conversion
-        value = value.end_of_day
-      end
+      value = value.end_of_day if date_conversion
       driver.less_equal(scope, field, value)
     else
-      raise Datagrid::FilteringError, "Unknown operation: #{operation.inspect}. Use filter block argument to implement operation"
+      raise Datagrid::FilteringError,
+            "Unknown operation: #{operation.inspect}. Use filter block argument to implement operation"
     end
   end
 
@@ -87,11 +82,11 @@ def operations_select
   protected
 
   def default_select
-    proc {|grid|
+    proc { |grid|
       grid.driver.column_names(grid.scope).map do |name|
         # Mongodb/Rails problem:
         # '_id'.humanize returns ''
-        [name.gsub(/^_/, '').humanize.strip, name]
+        [name.gsub(/^_/, "").humanize.strip, name]
       end
     }
   end
@@ -99,6 +94,7 @@ def default_select
   def type_cast(field, value)
     type = column_type(field)
     return nil if value.blank?
+
     case type
     when :string
       value.to_s
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 809d10c..bc65cc6 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -1,25 +1,23 @@
 require "datagrid/filters/select_options"
 
 class Datagrid::Filters::EnumFilter < Datagrid::Filters::BaseFilter
-
   include Datagrid::Filters::SelectOptions
 
   def initialize(*args)
     super(*args)
-    if checkboxes?
-      options[:multiple] = true
-    end
+    options[:multiple] = true if checkboxes?
     raise Datagrid::ConfigurationError, ":select option not specified" unless options[:select]
   end
 
   def parse(value)
-    return nil if self.strict && !select.include?(value)
+    return nil if strict && !select.include?(value)
+
     value
   end
 
   def default_html_classes
     res = super
-    res.push('checkboxes') if checkboxes?
+    res.push("checkboxes") if checkboxes?
     res
   end
 
@@ -30,5 +28,4 @@ def strict
   def checkboxes?
     options[:checkboxes]
   end
-
 end
diff --git a/lib/datagrid/filters/extended_boolean_filter.rb b/lib/datagrid/filters/extended_boolean_filter.rb
index cdad91a..521cf5e 100644
--- a/lib/datagrid/filters/extended_boolean_filter.rb
+++ b/lib/datagrid/filters/extended_boolean_filter.rb
@@ -1,10 +1,9 @@
 # @!visibility private
 class Datagrid::Filters::ExtendedBooleanFilter < Datagrid::Filters::EnumFilter
-
   YES = "YES"
   NO = "NO"
-  TRUTH_VALUES = [true, 'true', "y", "yes"]
-  FALSE_VALUES = [false, 'false', "n", "no"]
+  TRUTH_VALUES = [true, "true", "y", "yes"]
+  FALSE_VALUES = [false, "false", "n", "no"]
 
   def initialize(report, attribute, options = {}, &block)
     options[:select] = -> { boolean_select }
@@ -33,7 +32,7 @@ def parse(value)
   protected
 
   def boolean_select
-    [YES, NO].map do |key, value|
+    [YES, NO].map do |key, _value|
       [I18n.t("datagrid.filters.xboolean.#{key.downcase}"), key]
     end
   end
diff --git a/lib/datagrid/filters/float_filter.rb b/lib/datagrid/filters/float_filter.rb
index 85676fa..4361d60 100644
--- a/lib/datagrid/filters/float_filter.rb
+++ b/lib/datagrid/filters/float_filter.rb
@@ -1,9 +1,9 @@
 class Datagrid::Filters::FloatFilter < Datagrid::Filters::BaseFilter
-
   include Datagrid::Filters::RangedFilter
 
   def parse(value)
     return nil if value.blank?
+
     value.to_f
   end
 end
diff --git a/lib/datagrid/filters/integer_filter.rb b/lib/datagrid/filters/integer_filter.rb
index e1943aa..e05a14e 100644
--- a/lib/datagrid/filters/integer_filter.rb
+++ b/lib/datagrid/filters/integer_filter.rb
@@ -1,17 +1,16 @@
 require "datagrid/filters/ranged_filter"
 
 class Datagrid::Filters::IntegerFilter < Datagrid::Filters::BaseFilter
-
   include Datagrid::Filters::RangedFilter
 
   def parse(value)
     return nil if value.blank?
     if defined?(ActiveRecord) && value.is_a?(ActiveRecord::Base) &&
-        value.respond_to?(:id) && value.id.is_a?(Integer)
+       value.respond_to?(:id) && value.id.is_a?(Integer)
       return value.id
     end
     return value if value.is_a?(Range)
+
     value.to_i
   end
 end
-
diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index 399a82e..05ac2c9 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -1,10 +1,9 @@
 module Datagrid::Filters::RangedFilter
-
   def initialize(grid, name, options, &block)
     super(grid, name, options, &block)
-    if range?
-      options[:multiple] = true
-    end
+    return unless range?
+
+    options[:multiple] = true
   end
 
   def parse_values(value)
@@ -30,7 +29,6 @@ def parse_values(value)
     else
       raise ArgumentError, "Can not create a date range from array of more than two: #{result.inspect}"
     end
-
   end
 
   def range?
@@ -40,17 +38,11 @@ def range?
   def default_filter_where(scope, value)
     if range? && value.is_a?(Array)
       left, right = value
-      if left
-        scope = driver.greater_equal(scope, name, left)
-      end
-      if right
-        scope = driver.less_equal(scope, name, right)
-      end
+      scope = driver.greater_equal(scope, name, left) if left
+      scope = driver.less_equal(scope, name, right) if right
       scope
     else
       super(scope, value)
     end
   end
-
-
 end
diff --git a/lib/datagrid/filters/select_options.rb b/lib/datagrid/filters/select_options.rb
index bf560e4..68ae60b 100644
--- a/lib/datagrid/filters/select_options.rb
+++ b/lib/datagrid/filters/select_options.rb
@@ -1,6 +1,6 @@
 module Datagrid::Filters::SelectOptions
   def select(object)
-    select = self.options[:select]
+    select = options[:select]
     if select.is_a?(Symbol)
       object.send(select)
     elsif select.respond_to?(:call)
@@ -15,7 +15,7 @@ def select_values(object)
     groups_used = grouped_choices?(options)
     options.map do |option|
       if groups_used
-        option[1].map {|o| option_text_and_value(o)}
+        option[1].map { |o| option_text_and_value(o) }
       else
         option_text_and_value(option)
       end
@@ -23,9 +23,12 @@ def select_values(object)
   end
 
   def include_blank
-    unless prompt
-      options.has_key?(:include_blank) ?
-        Datagrid::Utils.callable(options[:include_blank]) : !multiple?
+    return if prompt
+
+    if options.has_key?(:include_blank)
+      Datagrid::Utils.callable(options[:include_blank])
+    else
+      !multiple?
     end
   end
 
@@ -41,7 +44,7 @@ def prompt
   def option_text_and_value(option)
     # Options are [text, value] pairs or strings used for both.
     if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
-      option = option.reject { |e| Hash === e } if Array === option
+      option = option.reject { |e| e.is_a?(Hash) } if option.is_a?(Array)
       [option.first, option.last]
     else
       [option, option]
@@ -52,6 +55,6 @@ def option_text_and_value(option)
   # https://github.com/rails/rails/blob/f95c0b7e96eb36bc3efc0c5beffbb9e84ea664e4/actionview/lib/action_view/helpers/tags/select.rb#L36
   # Code reuse is difficult, so it is easier to duplicate it
   def grouped_choices?(choices)
-    !choices.blank? && choices.first.respond_to?(:last) && Array === choices.first.last
+    !choices.blank? && choices.first.respond_to?(:last) && choices.first.last.is_a?(Array)
   end
 end
diff --git a/lib/datagrid/filters/string_filter.rb b/lib/datagrid/filters/string_filter.rb
index 9ea428c..35d20dd 100644
--- a/lib/datagrid/filters/string_filter.rb
+++ b/lib/datagrid/filters/string_filter.rb
@@ -1,5 +1,4 @@
 class Datagrid::Filters::StringFilter < Datagrid::Filters::BaseFilter
-
   include Datagrid::Filters::RangedFilter
 
   def parse(value)
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index bfdd80e..a99888f 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -10,7 +10,7 @@ module FormBuilder
     #   * <tt>text_field</tt> for other filter types
     def datagrid_filter(filter_or_attribute, partials: nil, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
-      self.send( filter.form_builder_helper_name, filter, **options, &block)
+      send(filter.form_builder_helper_name, filter, **options, &block)
     end
 
     # @param filter_or_attribute [Datagrid::Filters::BaseFilter, String, Symbol] filter object or filter name
@@ -20,7 +20,7 @@ def datagrid_filter(filter_or_attribute, partials: nil, **options, &block)
     def datagrid_label(filter_or_attribute, text = nil, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
       options = add_html_classes(
-        {**filter.label_options, **options},
+        { **filter.label_options, **options },
         filter.default_html_classes
       )
       label(filter.name, text || filter.header, **options, &block)
@@ -31,7 +31,7 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       filter = datagrid_get_filter(attribute_or_filter)
       options = add_filter_options(filter, **options)
       type = options.delete(:type)&.to_sym
-      if options.has_key?(:value) && options[:value].nil? && [:"datetime-local", :date].include?(type)
+      if options.has_key?(:value) && options[:value].nil? && %i[datetime-local date].include?(type)
         # https://github.com/rails/rails/pull/53387
         options[:value] = ""
       end
@@ -40,7 +40,7 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       elsif type == :date
         date_field filter.name, **options, &block
       elsif type == :textarea
-        text_area filter.name, value: object.filter_value_as_string(filter) , **options, &block
+        text_area filter.name, value: object.filter_value_as_string(filter), **options, &block
       elsif type == :checkbox
         # raise options.inspect
         check_box filter.name, options, options.fetch(:value, 1)
@@ -55,16 +55,17 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
             prompt: filter.prompt,
             include_hidden: false
           },
-           multiple: filter.multiple?,
-           **options,
-           &block
+          multiple: filter.multiple?,
+          **options,
+          &block
         )
       else
-        text_field filter.name, value: object.filter_value_as_string(filter) , **options, &block
+        text_field filter.name, value: object.filter_value_as_string(filter), **options, &block
       end
     end
 
     protected
+
     def datagrid_extended_boolean_filter(filter, options = {}, &block)
       datagrid_filter_input(filter, **options, type: :select, &block)
     end
@@ -93,12 +94,12 @@ def datagrid_enum_filter(filter, options = {}, &block)
           [value, text, checked]
         end
         render_partial(
-          'enum_checkboxes',
+          "enum_checkboxes",
           {
             elements: elements,
             form: self,
             filter: filter,
-            options: options,
+            options: options
           }
         )
       else
@@ -118,14 +119,12 @@ def enum_checkbox_checked?(filter, option_value)
     end
 
     def datagrid_integer_filter(filter, options = {})
-      if filter.multiple? && object[filter.name].blank?
-        options[:value] = ""
-      end
+      options[:value] = "" if filter.multiple? && object[filter.name].blank?
       datagrid_range_filter(:integer, filter, options)
     end
 
     def datagrid_dynamic_filter(filter, options = {})
-      input_name = "#{object_name}[#{filter.name.to_s}][]"
+      input_name = "#{object_name}[#{filter.name}][]"
       field, operation, value = object.filter_value(filter)
       options = add_filter_options(filter, **options)
       options = options.merge(name: input_name)
@@ -146,7 +145,7 @@ def datagrid_dynamic_filter(filter, options = {})
           include_blank: false,
           include_hidden: false,
           prompt: false,
-          selected: operation,
+          selected: operation
         },
         add_html_classes(options, "operation")
       )
@@ -166,12 +165,12 @@ def dynamic_filter_select(name, variants, select_options, html_options)
       end
     end
 
-    def datagrid_range_filter(type, filter, options = {})
+    def datagrid_range_filter(_type, filter, options = {})
       if filter.range?
         options = options.merge(multiple: true)
         from_options = datagrid_range_filter_options(object, filter, :from, options)
         to_options = datagrid_range_filter_options(object, filter, :to, options)
-        render_partial 'range_filter', {
+        render_partial "range_filter", {
           from_options: from_options, to_options: to_options, filter: filter, form: self
         }
       else
@@ -180,7 +179,7 @@ def datagrid_range_filter(type, filter, options = {})
     end
 
     def datagrid_range_filter_options(object, filter, type, options)
-      type_method_map = {from: :first, to: :last}
+      type_method_map = { from: :first, to: :last }
       options = add_html_classes(options, type)
       options[:value] = filter.format(object[filter.name]&.public_send(type_method_map[type]))
       # In case of datagrid ranged filter
@@ -219,14 +218,12 @@ def add_html_classes(options, *classes)
     end
 
     def partial_path(name)
-      if partials = self.options[:partials]
+      if partials = options[:partials]
         partial_name = File.join(partials, name)
         # Second argument is []: no magical namespaces to lookup added from controller
-        if @template.lookup_context.template_exists?(partial_name, [], true)
-          return partial_name
-        end
+        return partial_name if @template.lookup_context.template_exists?(partial_name, [], true)
       end
-      File.join('datagrid', name)
+      File.join("datagrid", name)
     end
 
     def render_partial(name, locals)
@@ -235,8 +232,8 @@ def render_partial(name, locals)
 
     def add_filter_options(filter, **options)
       add_html_classes(
-        {**filter.input_options, **options},
-        *filter.default_html_classes,
+        { **filter.input_options, **options },
+        *filter.default_html_classes
       )
     end
 
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index 2a76698..039bc13 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -2,7 +2,6 @@
 
 module Datagrid
   module Helper
-
     # @param grid [Datagrid] grid object
     # @param column [Datagrid::Columns::Column, String, Symbol] column name
     # @param model [Object] an object from grid scope
@@ -62,7 +61,6 @@ def datagrid_header(grid, options = {})
       datagrid_renderer.header(grid, options)
     end
 
-
     # Renders HTML table rows using given grid definition using columns defined in it.
     # Allows to provide a custom layout for each for in place with a block
     #
@@ -149,9 +147,10 @@ def datagrid_renderer
     end
 
     def datagrid_column_classes(grid, column)
-      order_class = grid.ordered_by?(column) ? ["ordered", grid.descending ? "desc" : "asc"] : nil
+      order_class = if grid.ordered_by?(column)
+                      ["ordered", grid.descending ? "desc" : "asc"]
+                    end
       [column.name, order_class, column.options[:class]].compact.join(" ")
     end
   end
 end
-
diff --git a/lib/datagrid/ordering.rb b/lib/datagrid/ordering.rb
index 0f6cbee..83577c6 100644
--- a/lib/datagrid/ordering.rb
+++ b/lib/datagrid/ordering.rb
@@ -4,39 +4,32 @@ module Datagrid
   # Raised when grid order value is incorrect
   class OrderUnsupported < StandardError
   end
-  module Ordering
 
+  module Ordering
     # @!visibility private
     def self.included(base)
-      base.extend         ClassMethods
+      base.extend ClassMethods
       base.class_eval do
         include Datagrid::Columns
 
         datagrid_attribute :order do |value|
-          if value.present?
-            value.to_sym
-          else
-            nil
-          end
-
+          value.to_sym if value.present?
         end
 
         datagrid_attribute :descending do |value|
           Datagrid::Utils.booleanize(value)
         end
-        alias descending? descending
-
+        alias_method :descending?, :descending
       end
     end
 
     # @!visibility private
     module ClassMethods
       def order_unsupported(name, reason)
-        raise Datagrid::OrderUnsupported, "Can not sort #{self.inspect} by ##{name}: #{reason}"
+        raise Datagrid::OrderUnsupported, "Can not sort #{inspect} by ##{name}: #{reason}"
       end
     end
 
-
     # @!visibility private
     def assets
       check_order_valid!
@@ -65,33 +58,31 @@ def ordered_by?(column)
 
     def apply_order(assets)
       return assets unless order
+
       if order_column.order_by_value?
         assets = assets.sort_by do |asset|
           order_column.order_by_value(asset, self)
         end
         descending? ? assets.reverse : assets
-      else
-        if descending?
-          if order_column.order_desc
-            apply_asc_order(assets, order_column.order_desc)
-          else
-            apply_desc_order(assets, order_column.order)
-          end
+      elsif descending?
+        if order_column.order_desc
+          apply_asc_order(assets, order_column.order_desc)
         else
-          apply_asc_order(assets, order_column.order)
+          apply_desc_order(assets, order_column.order)
         end
+      else
+        apply_asc_order(assets, order_column.order)
       end
     end
 
     def check_order_valid!
       return unless order
+
       column = column_by_name(order)
-      unless column
-        self.class.order_unsupported(order, "no column #{order} in #{self.class}")
-      end
-      unless column.supports_order?
-        self.class.order_unsupported(column.name, "column don't support order" )
-      end
+      self.class.order_unsupported(order, "no column #{order} in #{self.class}") unless column
+      return if column.supports_order?
+
+      self.class.order_unsupported(column.name, "column don't support order")
     end
 
     def apply_asc_order(assets, order)
@@ -113,7 +104,8 @@ def apply_desc_order(assets, order)
     def reverse_order(assets)
       driver.reverse_order(assets)
     rescue NotImplementedError
-      self.class.order_unsupported(order_column.name, "Your ORM do not support reverse order: please specify :order_desc option manually")
+      self.class.order_unsupported(order_column.name,
+                                   "Your ORM do not support reverse order: please specify :order_desc option manually")
     end
 
     def apply_block_order(assets, order)
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index 3b472f0..3b23449 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -3,7 +3,6 @@
 module Datagrid
   # @!visibility private
   class Renderer
-
     def self.for(template)
       new(template)
     end
@@ -13,9 +12,7 @@ def initialize(template)
     end
 
     def format_value(grid, column, asset)
-      if column.is_a?(String) || column.is_a?(Symbol)
-        column = grid.column_by_name(column)
-      end
+      column = grid.column_by_name(column) if column.is_a?(String) || column.is_a?(Symbol)
 
       value = grid.html_value(column, @template, asset)
 
@@ -32,14 +29,14 @@ def form_for(grid, options = {})
       options[:html] ||= {}
       options[:html][:class] ||= "datagrid-form #{@template.dom_class(grid)}"
       options[:as] ||= grid.param_name
-      _render_partial('form', options[:partials], {:grid => grid, :options => options})
+      _render_partial("form", options[:partials], { grid: grid, options: options })
     end
 
     def table(grid, assets, **options)
       options[:html] ||= {}
       options[:html][:class] ||= "datagrid #{@template.dom_class(grid)}"
 
-      _render_partial('table', options[:partials],
+      _render_partial("table", options[:partials],
                       {
                         grid: grid,
                         options: options,
@@ -50,8 +47,8 @@ def table(grid, assets, **options)
     def header(grid, options = {})
       options[:order] = true unless options.has_key?(:order)
 
-      _render_partial('head', options[:partials],
-                      { :grid => grid, :options => options })
+      _render_partial("head", options[:partials],
+                      { grid: grid, options: options })
     end
 
     def rows(grid, assets = grid.assets, **options, &block)
@@ -64,22 +61,20 @@ def rows(grid, assets = grid.assets, **options, &block)
 
     def row(grid, asset, **options, &block)
       Datagrid::Helper::HtmlRow.new(self, grid, asset, options).tap do |row|
-        if block_given?
-          return @template.capture(row, &block)
-        end
+        return @template.capture(row, &block) if block_given?
       end
     end
 
     def order_for(grid, column, options = {})
-      _render_partial('order_for', options[:partials],
-                      { :grid => grid, :column => column })
+      _render_partial("order_for", options[:partials],
+                      { grid: grid, column: column })
     end
 
     def order_path(grid, column, descending, request)
       column = grid.column_by_name(column)
       query = request ? request.query_parameters : {}
       ActionDispatch::Http::URL.path_for(
-        path: request ? request.path : '/',
+        path: request ? request.path : "/",
         params: query.merge(grid.query_params(order: column.name, descending: descending))
       )
     end
@@ -92,9 +87,9 @@ def _safe(string)
 
     def _render_partial(partial_name, partials_path, locals = {})
       @template.render({
-        :partial => File.join(partials_path || 'datagrid', partial_name),
-        :locals => locals
-      })
+                         partial: File.join(partials_path || "datagrid", partial_name),
+                         locals: locals
+                       })
     end
   end
 
@@ -110,7 +105,6 @@ module Helper
     #     puts value
     #   end
     class HtmlRow
-
       include Enumerable
 
       attr_reader :grid, :asset, :options
@@ -137,14 +131,15 @@ def each(&block)
       end
 
       def to_s
-        @renderer.send(:_render_partial, 'row', options[:partials], {
-          :grid => grid,
-          :options => options,
-          :asset => asset
-        })
+        @renderer.send(:_render_partial, "row", options[:partials], {
+                         grid: grid,
+                         options: options,
+                         asset: asset
+                       })
       end
 
       protected
+
       def method_missing(method, *args, &blk)
         if column = @grid.column_by_name(method)
           get(column)
diff --git a/lib/datagrid/rspec.rb b/lib/datagrid/rspec.rb
index ce7ff83..6b07ea7 100644
--- a/lib/datagrid/rspec.rb
+++ b/lib/datagrid/rspec.rb
@@ -1,16 +1,14 @@
 require "datagrid"
 
-#TODO: refactor this experimental shit
+# TODO: refactor this experimental shit
 shared_examples_for "Datagrid" do
   describe "as Datagrid" do
-
     it "should have at least one entry if assets" do
       subject.assets.should_not be_empty
     end
 
-    described_class.columns(:data => true).each do |column|
+    described_class.columns(data: true).each do |column|
       describe "column ##{column.name}" do
-
         it "should has value in #data_hash" do
           subject.data_hash.first.should have_key(column.name)
         end
@@ -25,14 +23,11 @@
           subject.assets.first.should_not be_nil
         end
       end
-
     end
 
     described_class.filters.each do |filter|
       describe "filter ##{filter.name}" do
-
         let(:filter_value) do
-
           case Datagrid::Filters::FILTER_TYPES.invert[filter.class]
           when :default, :string
             "text"
@@ -53,16 +48,15 @@
         end
 
         before(:each) do
-          subject.attributes = {filter.name => filter_value}
+          subject.attributes = { filter.name => filter_value }
           subject.public_send(filter.name).should_not be_nil
         end
 
         it "should be supported" do
           subject.assets.should_not be_nil
-          #TODO: better matcher.
+          # TODO: better matcher.
         end
       end
     end
-
   end
 end
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index aad238f..d6c6d23 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -2,36 +2,31 @@
 
 # @!visibility private
 class Datagrid::Scaffold < Rails::Generators::NamedBase
-
   include Rails::Generators::ResourceHelpers
 
   check_class_collision suffix: "Grid"
   source_root File.expand_path(__FILE__ + "/../../../templates")
 
   def create_scaffold
-    unless file_exists?(base_grid_file)
-      template "base.rb.erb", base_grid_file
-    end
+    template "base.rb.erb", base_grid_file unless file_exists?(base_grid_file)
     template "grid.rb.erb", "app/grids/#{grid_class_name.underscore}.rb"
     if file_exists?(grid_controller_file)
-      inject_into_file grid_controller_file, index_action, after: %r{class .*#{grid_controller_class_name}.*\n}
+      inject_into_file grid_controller_file, index_action, after: /class .*#{grid_controller_class_name}.*\n/
     else
       template "controller.rb.erb", grid_controller_file
     end
     template "index.html.erb", view_file
     route(generate_routing_namespace("resources :#{grid_controller_short_name}"))
-    unless defined?(::Kaminari) || defined?(::WillPaginate)
-      gem 'kaminari'
-    end
+    gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate)
     in_root do
       {
         "css" => " *= require datagrid",
         "css.sass" => " *= require datagrid",
-        "css.scss" => " *= require datagrid",
+        "css.scss" => " *= require datagrid"
       }.each do |extension, string|
         file = "app/assets/stylesheets/application.#{extension}"
         if file_exists?(file)
-          inject_into_file file, string + "\n", {before: %r{.*require_self}} # before all
+          inject_into_file file, string + "\n", { before: /.*require_self/ } # before all
         end
       end
     end
@@ -83,22 +78,23 @@ def grid_route_name
   end
 
   def index_action
-    indent(<<-RUBY)
-def index
-  @grid = #{grid_class_name}.new(grid_params) do |scope|
-    scope.page(params[:page])
-  end
-end
+    indent(<<~RUBY)
+      def index
+        @grid = #{grid_class_name}.new(grid_params) do |scope|
+          scope.page(params[:page])
+        end
+      end
 
-protected
+      protected
 
-def grid_params
-  params.fetch(:#{grid_param_name}, {}).permit!
-end
-RUBY
+      def grid_params
+        params.fetch(:#{grid_param_name}, {}).permit!
+      end
+    RUBY
   end
 
   protected
+
   def generate_routing_namespace(code)
     depth = regular_class_path.length
     # Create 'namespace' ladder
diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index 1114c85..254ab02 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -2,26 +2,19 @@ module Datagrid
   # @!visibility private
   module Utils
     class << self
-
-
       TRUTH = [true, 1, "1", "true", "yes", "on"]
 
       def booleanize(value)
-        if value.respond_to?(:downcase)
-          value = value.downcase
-        end
+        value = value.downcase if value.respond_to?(:downcase)
         TRUTH.include?(value)
       end
 
       def translate_from_namespace(namespace, grid_class, key)
-
         lookups = []
         namespaced_key = "#{namespace}.#{key}"
 
         grid_class.ancestors.each do |ancestor|
-          if ancestor.respond_to?(:model_name)
-            lookups << :"datagrid.#{ancestor.model_name.i18n_key}.#{namespaced_key}"
-          end
+          lookups << :"datagrid.#{ancestor.model_name.i18n_key}.#{namespaced_key}" if ancestor.respond_to?(:model_name)
         end
         lookups << :"datagrid.defaults.#{namespaced_key}"
         lookups << key.to_s.humanize
@@ -32,6 +25,7 @@ def warn_once(message, delay = 5)
         @warnings ||= {}
         timestamp = @warnings[message]
         return false if timestamp && timestamp >= Time.now - delay
+
         warn message
         @warnings[message] = Time.now
         true
@@ -39,6 +33,7 @@ def warn_once(message, delay = 5)
 
       def add_html_classes(options, *classes)
         return options if classes.empty?
+
         options = options.clone
         options[:class] ||= []
         array = options[:class].is_a?(Array)
@@ -52,18 +47,18 @@ def string_like?(value)
       end
 
       def extract_position_from_options(array, options)
-        before, after = options[:before], options[:after]
-        if before && after
-          raise Datagrid::ConfigurationError, "Options :before and :after can not be used together"
-        end
+        before = options[:before]
+        after = options[:after]
+        raise Datagrid::ConfigurationError, "Options :before and :after can not be used together" if before && after
         # Consider as before all
         return 0 if before == true
+
         if before
           before = before.to_sym
-          array.index {|c| c.name.to_sym == before }
+          array.index { |c| c.name.to_sym == before }
         elsif after
           after = after.to_sym
-          array.index {|c| c.name.to_sym == after } + 1
+          array.index { |c| c.name.to_sym == after } + 1
         else
           -1
         end
@@ -77,16 +72,16 @@ def apply_args(*args, &block)
       def parse_date(value)
         return nil if value.blank?
         return value if value.is_a?(Range)
+
         if value.is_a?(String)
           Array(Datagrid.configuration.date_formats).each do |format|
-            begin
-              return Date.strptime(value, format)
-            rescue ::ArgumentError
-            end
+            return Date.strptime(value, format)
+          rescue ::ArgumentError
           end
         end
         return Date.parse(value) if value.is_a?(String)
         return value.to_date if value.respond_to?(:to_date)
+
         value
       rescue ::ArgumentError
         nil
@@ -95,16 +90,16 @@ def parse_date(value)
       def parse_datetime(value)
         return nil if value.blank?
         return value if value.is_a?(Range)
+
         if value.is_a?(String)
           Array(Datagrid.configuration.datetime_formats).each do |format|
-            begin
-              return Time.strptime(value, format)
-            rescue ::ArgumentError
-            end
+            return Time.strptime(value, format)
+          rescue ::ArgumentError
           end
         end
         return Time.parse(value) if value.is_a?(String)
         return value.to_time if value.respond_to?(:to_time)
+
         value
       rescue ::ArgumentError
         nil
@@ -132,6 +127,7 @@ def callable(value)
       end
 
       protected
+
       def property_availability(grid, option, default)
         case option
         when nil
diff --git a/lib/tasks/datagrid_tasks.rake b/lib/tasks/datagrid_tasks.rake
index 5285bd0..27eb792 100644
--- a/lib/tasks/datagrid_tasks.rake
+++ b/lib/tasks/datagrid_tasks.rake
@@ -1,5 +1,4 @@
 namespace :datagrid do
-
   desc "Copy table partials into rails application"
   task :copy_partials do
     require "fileutils"
diff --git a/spec/datagrid/active_model_spec.rb b/spec/datagrid/active_model_spec.rb
index d0e4220..b42adbc 100644
--- a/spec/datagrid/active_model_spec.rb
+++ b/spec/datagrid/active_model_spec.rb
@@ -1,7 +1,6 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::ActiveModel do
-
   class ActiveReport
     include Datagrid::ActiveModel
   end
@@ -23,11 +22,10 @@ class ActiveReport
 
   describe ".param_name" do
     it "should make right param key from simple class name" do
-      expect(ActiveReport.param_name).to eq('active_report')
+      expect(ActiveReport.param_name).to eq("active_report")
     end
     it "should make right param key from class of module" do
-      expect(Grid::ActiveReport.param_name).to eq('grid_active_report')
+      expect(Grid::ActiveReport.param_name).to eq("grid_active_report")
     end
   end
-
 end
diff --git a/spec/datagrid/column_names_attribute_spec.rb b/spec/datagrid/column_names_attribute_spec.rb
index 4a35c24..321cc5f 100644
--- a/spec/datagrid/column_names_attribute_spec.rb
+++ b/spec/datagrid/column_names_attribute_spec.rb
@@ -1,7 +1,6 @@
 require "spec_helper"
 
 describe Datagrid::ColumnNamesAttribute do
-
   let(:column_names_filter_options) do
     {}
   end
@@ -12,37 +11,36 @@
       scope { Entry }
       column_names_filter(**options)
       column(:id)
-      column(:name, :mandatory => true)
+      column(:name, mandatory: true)
       column(:category)
     end
   end
   subject { report }
 
-
   let!(:entry) do
-    Entry.create!(:name => 'hello', :category => 'greeting')
+    Entry.create!(name: "hello", category: "greeting")
   end
 
   it "should work" do
     subject.column_names = [:id]
     expect(subject.mandatory_columns.map(&:name)).to eq([:name])
-    expect(subject.optional_columns.map(&:name)).to eq([:id, :category])
-    expect(subject.data).to eq([["Id", "Name"], [entry.id, "hello"]])
+    expect(subject.optional_columns.map(&:name)).to eq(%i[id category])
+    expect(subject.data).to eq([%w[Id Name], [entry.id, "hello"]])
     columns_filter = subject.filter_by_name(:column_names)
     expect(columns_filter).not_to be_nil
     expect(columns_filter.select(subject)).to eq([["Id", :id], ["Category", :category]])
   end
 
   it "should show only mandatory columns by default" do
-    expect(subject.row_for(entry)).to eq([ "hello" ])
-    subject.column_names = ["name", "category"]
-    expect(subject.row_for(entry)).to eq(["hello", "greeting"])
+    expect(subject.row_for(entry)).to eq(["hello"])
+    subject.column_names = %w[name category]
+    expect(subject.row_for(entry)).to eq(%w[hello greeting])
   end
 
   it "should show mandatory columns even if they are unselected" do
     subject.column_names = ["category"]
-    expect(subject.row_for(entry)).to eq(["hello", "greeting"])
-    expect(subject.data).to eq([["Name", "Category"], ["hello", "greeting"]])
+    expect(subject.row_for(entry)).to eq(%w[hello greeting])
+    expect(subject.data).to eq([%w[Name Category], %w[hello greeting]])
   end
 
   it "should find any column by name" do
@@ -51,27 +49,25 @@
     expect(subject.column_by_name(:category)).not_to be_nil
   end
 
-
   context "when default option is passed to column_names_filter" do
     let(:column_names_filter_options) do
-      { :default => [:id] }
+      { default: [:id] }
     end
 
-    describe '#data' do
+    describe "#data" do
       subject { super().data }
-      it { should == [["Id", "Name"], [entry.id, 'hello']] }
+      it { should == [%w[Id Name], [entry.id, "hello"]] }
     end
-
   end
 
   context "when some columns are disabled" do
     subject do
       test_report do
-        scope {Entry}
-        column(:id, :mandatory => true)
+        scope { Entry }
+        column(:id, mandatory: true)
         column(:name)
         column(:category, if: proc { false })
-        column(:group, :mandatory => true, if: proc { false })
+        column(:group, mandatory: true, if: proc { false })
       end
     end
 
diff --git a/spec/datagrid/columns/column_spec.rb b/spec/datagrid/columns/column_spec.rb
index 8fc3d75..086ecbc 100644
--- a/spec/datagrid/columns/column_spec.rb
+++ b/spec/datagrid/columns/column_spec.rb
@@ -1,19 +1,18 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Columns::Column do
-  
   describe ".inspect" do
     subject do
       class ColumnInspectTest
         include Datagrid
-        scope {Entry}
+        scope { Entry }
         column(:id, header: "ID")
       end
       ColumnInspectTest.column_by_name(:id)
     end
 
     it "shows inspect information" do
-      expect(subject.inspect).to eq("#<Datagrid::Columns::Column ColumnInspectTest#id {:header=>\"ID\"}>")
+      expect(subject.inspect).to eq('#<Datagrid::Columns::Column ColumnInspectTest#id {:header=>"ID"}>')
     end
   end
 end
diff --git a/spec/datagrid/columns_spec.rb b/spec/datagrid/columns_spec.rb
index bdefe02..c644800 100644
--- a/spec/datagrid/columns_spec.rb
+++ b/spec/datagrid/columns_spec.rb
@@ -1,7 +1,6 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Columns do
-
   let(:group) { Group.create!(name: "Pop") }
 
   subject do
@@ -9,7 +8,6 @@
   end
 
   describe "basic methods" do
-
     let!(:entry) do
       Entry.create!(
         group: group,
@@ -17,8 +15,8 @@
         disabled: false,
         confirmed: false,
         category: "first",
-        access_level: 'admin',
-        pet: 'rottweiler',
+        access_level: "admin",
+        pet: "rottweiler",
         shipping_date: Date.new(2013, 8, 1)
       )
     end
@@ -27,19 +25,19 @@
 
     it "should have data columns without html columns" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:name)
         column(:action, html: true) do
-          'dummy'
+          "dummy"
         end
       end
       expect(grid.data_columns.map(&:name)).to eq([:name])
-      expect(grid.html_columns.map(&:name)).to eq([:name, :action])
+      expect(grid.html_columns.map(&:name)).to eq(%i[name action])
     end
 
     it "allows a column argument" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:name)
       end
       expect(grid.data_columns(grid.column_by_name(:name)).map(&:name)).to eq([:name])
@@ -47,27 +45,26 @@
 
     it "should build rows of data" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:name)
         column(:action, html: true) do
-          'dummy'
+          "dummy"
         end
       end
       expect(grid.rows).to eq([["Star"]])
     end
-    it  "should generate header without html columns" do
+    it "should generate header without html columns" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:name)
         column(:action, html: true) do
-          'dummy'
+          "dummy"
         end
       end
       expect(grid.header).to eq(["Name"])
     end
 
     describe "translations" do
-
       module ::Ns45
         class TranslatedReport
           include Datagrid
@@ -76,7 +73,7 @@ class TranslatedReport
         end
       end
       it "translates column-header with namespace" do
-        store_translations(:en, datagrid: {:"ns45/translated_report" => {columns: {name: "Navn"}}}) do
+        store_translations(:en, datagrid: { "ns45/translated_report": { columns: { name: "Navn" } } }) do
           expect(Ns45::TranslatedReport.new.header.first).to eq("Navn")
         end
       end
@@ -84,11 +81,11 @@ class TranslatedReport
       it "translates column-header without namespace" do
         class Report27
           include Datagrid
-          scope {Entry}
+          scope { Entry }
           column(:name)
         end
 
-        store_translations(:en, datagrid: {:"report27" => {columns: {name: "Nombre"}}}) do
+        store_translations(:en, datagrid: { "report27": { columns: { name: "Nombre" } } }) do
           expect(Report27.new.header.first).to eq("Nombre")
         end
       end
@@ -96,20 +93,19 @@ class Report27
       it "translates column-header in using defaults namespace" do
         class Report27
           include Datagrid
-          scope {Entry}
+          scope { Entry }
           column(:name)
         end
 
-        store_translations(:en, datagrid: {defaults: {columns: {name: "Nombre"}}}) do
+        store_translations(:en, datagrid: { defaults: { columns: { name: "Nombre" } } }) do
           expect(Report27.new.header.first).to eq("Nombre")
         end
       end
-
     end
 
     it "should return html_columns" do
       report = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:id)
         column(:name, html: false)
       end
@@ -118,7 +114,7 @@ class Report27
 
     it "should return html_columns when column definition has 2 arguments" do
       report = test_report(name: "Hello") do
-        scope {Entry}
+        scope { Entry }
         filter(:name)
         column(:id)
         column(:name, html: false) do |model, grid|
@@ -131,14 +127,14 @@ class Report27
 
     it "should generate table data" do
       expect(subject.data).to eq([
-        subject.header,
-        subject.row_for(entry)
-      ])
+                                   subject.header,
+                                   subject.row_for(entry)
+                                 ])
     end
 
     it "supports dynamic header" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:id, header: proc { rand(10**9) })
       end
 
@@ -147,12 +143,12 @@ class Report27
 
     it "should generate hash for given asset" do
       expect(subject.hash_for(entry)).to eq({
-        group: "Pop",
-        name: "Star",
-        access_level: 'admin',
-        pet: 'ROTTWEILER',
-        shipping_date: date
-      })
+                                              group: "Pop",
+                                              name: "Star",
+                                              access_level: "admin",
+                                              pet: "ROTTWEILER",
+                                              shipping_date: date
+                                            })
     end
 
     it "should support csv export" do
@@ -166,12 +162,11 @@ class Report27
     it "should support csv export options" do
       expect(subject.to_csv(col_sep: ";")).to eq("Shipping date;Group;Name;Access level;Pet\n#{date};Pop;Star;admin;ROTTWEILER\n")
     end
-
   end
 
   it "should support columns with model and report arguments" do
     report = test_report(category: "foo") do
-      scope {Entry.order(:category)}
+      scope { Entry.order(:category) }
       filter(:category) do |value|
         where("category LIKE '%#{value}%'")
       end
@@ -203,64 +198,64 @@ class Report27
   end
   it "should support defining a query for a column" do
     report = test_report do
-      scope {Entry}
+      scope { Entry }
       filter(:name)
       column(:id)
-      column(:sum_group_id, 'sum(group_id)')
+      column(:sum_group_id, "sum(group_id)")
     end
     Entry.create!(group: group)
     expect(report.assets.first.sum_group_id).to eq(group.id)
   end
 
-    it "should support post formatting for column defined with query" do
-      report = test_report do
-        scope {Group.joins(:entries).group("groups.id")}
-        filter(:name)
-        column(:entries_count, 'count(entries.id)') do |model|
-          format("(#{model.entries_count})") do |value|
-            content_tag(:span, value)
-          end
+  it "should support post formatting for column defined with query" do
+    report = test_report do
+      scope { Group.joins(:entries).group("groups.id") }
+      filter(:name)
+      column(:entries_count, "count(entries.id)") do |model|
+        format("(#{model.entries_count})") do |value|
+          content_tag(:span, value)
         end
       end
-      3.times { Entry.create!(group: group) }
-      expect(report.rows).to eq([["(3)"]])
     end
-    it "should support hidding columns through if and unless" do
-      report = test_report do
-        scope {Entry}
-        column(:id, if: :show?)
-        column(:name, unless: proc {|grid| !grid.show? })
-        column(:category)
+    3.times { Entry.create!(group: group) }
+    expect(report.rows).to eq([["(3)"]])
+  end
+  it "should support hidding columns through if and unless" do
+    report = test_report do
+      scope { Entry }
+      column(:id, if: :show?)
+      column(:name, unless: proc { |grid| !grid.show? })
+      column(:category)
 
-        def show?
-          false
-        end
+      def show?
+        false
       end
-      expect(report.columns(:id)).to eq([])
-      expect(report.columns(:name)).to eq([])
-      expect(report.available_columns.map(&:name)).to eq([:category])
     end
+    expect(report.columns(:id)).to eq([])
+    expect(report.columns(:name)).to eq([])
+    expect(report.available_columns.map(&:name)).to eq([:category])
+  end
 
-    it "raises when incorrect unless option is given" do
-      expect do
-        test_report do
-          column(:id, if: Object.new)
-        end
-      end.to raise_error(Datagrid::ConfigurationError)
-    end
+  it "raises when incorrect unless option is given" do
+    expect do
+      test_report do
+        column(:id, if: Object.new)
+      end
+    end.to raise_error(Datagrid::ConfigurationError)
+  end
 
-    it "raises when :before and :after used together" do
-      expect do
-        test_report do
-          column(:id)
-          column(:name, before: :id, after: :name)
-        end
-      end.to raise_error(Datagrid::ConfigurationError)
-    end
+  it "raises when :before and :after used together" do
+    expect do
+      test_report do
+        column(:id)
+        column(:name, before: :id, after: :name)
+      end
+    end.to raise_error(Datagrid::ConfigurationError)
+  end
 
   describe ".column_names attributes" do
     let(:grid) do
-      test_report(column_names: ["id", "name"]) do
+      test_report(column_names: %w[id name]) do
         scope { Entry }
         column(:id)
         column(:name)
@@ -268,10 +263,10 @@ def show?
       end
     end
     let!(:entry) do
-      Entry.create!(name: 'hello')
+      Entry.create!(name: "hello")
     end
     it "should be suppored in header" do
-      expect(grid.header).to eq(["Id", "Name"])
+      expect(grid.header).to eq(%w[Id Name])
     end
     it "should be suppored in rows" do
       expect(grid.rows).to eq([[entry.id, "hello"]])
@@ -282,16 +277,14 @@ def show?
     end
 
     it "should support explicit overwrite" do
-      expect(grid.header(:id, :name, :category)).to eq(%w(Id Name Category))
+      expect(grid.header(:id, :name, :category)).to eq(%w[Id Name Category])
     end
-
   end
 
-
   context "when grid has formatted column" do
     it "should output correct data" do
       report = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:name) do |entry|
           format(entry.name) do |value|
             "<strong>#{value}</strong"
@@ -306,18 +299,18 @@ def show?
   describe ".default_column_options" do
     it "should pass default options to each column definition" do
       report = test_report do
-        scope {Entry}
-        self.default_column_options = {order: false}
+        scope { Entry }
+        self.default_column_options = { order: false }
         column(:id)
         column(:name, order: "name")
       end
-      first = Entry.create(name: '1st')
-      second = Entry.create(name: '2nd')
+      first = Entry.create(name: "1st")
+      second = Entry.create(name: "2nd")
       expect do
-        report.attributes = {order: :id}
+        report.attributes = { order: :id }
         report.assets
       end.to raise_error(Datagrid::OrderUnsupported)
-      report.attributes = {order: :name, descending: true}
+      report.attributes = { order: :name, descending: true }
       expect(report.assets).to eq([second, first])
     end
   end
@@ -353,7 +346,7 @@ def show?
 
     it "should support instance level batch size" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         column :id
         self.batch_size = 25
       end
@@ -370,7 +363,7 @@ def show?
   describe ".data_row" do
     it "should give access to column values via an object" do
       grid = test_report do
-        scope  { Entry }
+        scope { Entry }
         column(:id)
         column(:name) do
           name.capitalize
@@ -379,13 +372,13 @@ def show?
           "some link here"
         end
       end
-      entry = Entry.create!(name: 'hello')
+      entry = Entry.create!(name: "hello")
       row = grid.data_row(entry)
       expect(row.id).to eq(entry.id)
       expect(row.name).to eq("Hello")
-      expect {
+      expect do
         row.actions
-      }.to raise_error(RuntimeError)
+      end.to raise_error(RuntimeError)
     end
   end
 
@@ -419,7 +412,7 @@ def show?
     end
 
     let(:basic_grid) { modified_grid.class.new }
-    let!(:entry) { Entry.create!(name: "Hello", category: 'first') }
+    let!(:entry) { Entry.create!(name: "Hello", category: "first") }
 
     it "should have correct columns" do
       expect(modified_grid.columns.size).to eq(2)
@@ -428,83 +421,83 @@ def show?
     end
 
     it "should give correct header" do
-      expect(modified_grid.header).to eq(["Id", "Name"])
+      expect(modified_grid.header).to eq(%w[Id Name])
       expect(basic_grid.header).to eq(["Id"])
     end
 
     it "should give correct rows" do
-      expect(modified_grid.rows).to eq([[entry.id, 'Hello']])
+      expect(modified_grid.rows).to eq([[entry.id, "Hello"]])
       expect(basic_grid.rows).to eq([[entry.id]])
     end
 
     it "should support :before column name" do
       modified_grid.column(:category, before: :name)
-      expect(modified_grid.header).to eq(["Id", "Category", "Name"])
+      expect(modified_grid.header).to eq(%w[Id Category Name])
     end
 
     it "should support :before all" do
       modified_grid.column(:category, before: true)
-      expect(modified_grid.header).to eq(["Category", "Id", "Name"])
+      expect(modified_grid.header).to eq(%w[Category Id Name])
     end
 
     it "should support columns block" do
       modified_grid.column(:category) do
         category.capitalize
       end
-      expect(modified_grid.rows).to eq([[entry.id, "Hello", 'First']])
+      expect(modified_grid.rows).to eq([[entry.id, "Hello", "First"]])
     end
 
     it "should support column_names accessor" do
       modified_grid.column_names = [:name]
-      expect(modified_grid.rows).to eq([['Hello']])
+      expect(modified_grid.rows).to eq([["Hello"]])
       modified_grid.column_names = [:id]
       expect(modified_grid.rows).to eq([[entry.id]])
     end
     it "should support column_names accessor with mandatory columns" do
       modified_grid.column(:category, mandatory: true)
       modified_grid.column_names = [:name]
-      expect(modified_grid.rows).to eq([['Hello', 'first']])
+      expect(modified_grid.rows).to eq([%w[Hello first]])
       basic_grid.column_names = [:id]
       expect(basic_grid.rows).to eq([[entry.id]])
     end
 
     it "should support available columns" do
       modified_grid.column(:category, mandatory: true)
-      expect(modified_grid.available_columns.map(&:name)).to eq([:id, :name, :category])
+      expect(modified_grid.available_columns.map(&:name)).to eq(%i[id name category])
     end
 
     it "should respect column availability criteria" do
       modified_grid.column(:category, if: proc { false })
-      expect(modified_grid.columns.map(&:name)).to eq([:id, :name])
+      expect(modified_grid.columns.map(&:name)).to eq(%i[id name])
     end
   end
 
   describe ".data_value" do
     it "should return value" do
       grid = test_report do
-        scope {Entry }
+        scope { Entry }
         column(:name)
       end
-      expect(grid.data_value(:name, Entry.create!(name: 'Hello'))).to eq('Hello')
+      expect(grid.data_value(:name, Entry.create!(name: "Hello"))).to eq("Hello")
     end
     it "should raise for disabled columns" do
       grid = test_report do
-        scope {Entry }
+        scope { Entry }
         column(:name, if: proc { false })
       end
-      expect  {
-        grid.data_value(:name, Entry.create!(name: 'Hello'))
-      }.to raise_error(Datagrid::ColumnUnavailableError)
+      expect do
+        grid.data_value(:name, Entry.create!(name: "Hello"))
+      end.to raise_error(Datagrid::ColumnUnavailableError)
     end
   end
 
   describe "caching" do
     it "should work when enabled in class" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         self.cached = true
-        column(:random1) {rand(10**9)}
-        column(:random2) {rand(10**9)}
+        column(:random1) { rand(10**9) }
+        column(:random2) { rand(10**9) }
       end
 
       row = grid.data_row(Entry.create!)
@@ -526,16 +519,18 @@ def show?
   describe "decoration" do
     class EntryDecorator
       attr_reader :model
+
       def initialize(model)
         @model = model
       end
+
       def capitalized_name
         model.name.capitalize
       end
     end
 
     let!(:entry) do
-      Entry.create!(name: 'hello', category: 'first')
+      Entry.create!(name: "hello", category: "first")
     end
 
     it "delegates column values to decorator" do
@@ -551,7 +546,7 @@ def capitalized_name
         end
       end
 
-      expect(grid.rows).to eq([['Hello', 'first', 'Hello']])
+      expect(grid.rows).to eq([%w[Hello first Hello]])
     end
 
     it "allows class decorator" do
@@ -560,7 +555,7 @@ def capitalized_name
         decorate { EntryDecorator }
         column(:capitalized_name)
       end
-      expect(grid.rows).to eq([['Hello']])
+      expect(grid.rows).to eq([["Hello"]])
     end
   end
 
@@ -601,7 +596,7 @@ def capitalized_name
       grid = test_report do
         scope { Entry }
         column(:id1, preload: ->(a) { a.order(:id) })
-        column(:id2, preload: ->(a) { a.order(:id) }, if: ->(a) { false })
+        column(:id2, preload: ->(a) { a.order(:id) }, if: ->(_a) { false })
         column(:name)
       end
       grid.column_names = [:name]
@@ -619,10 +614,10 @@ class DataHashGrid
       end
       grid1 = DataHashGrid.new(order: :name)
       grid2 = DataHashGrid.new(order: :name, descending: true)
-      Entry.create!(name: 'one')
-      Entry.create!(name: 'two')
-      expect(grid1.data_hash).to eq([{name: 'one'}, {name: 'two'}])
-      expect(grid2.data_hash).to eq([{name: 'two'}, {name: 'one'}])
+      Entry.create!(name: "one")
+      Entry.create!(name: "two")
+      expect(grid1.data_hash).to eq([{ name: "one" }, { name: "two" }])
+      expect(grid2.data_hash).to eq([{ name: "two" }, { name: "one" }])
     end
   end
 end
diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index b78cd6f..e5c5d47 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -1,9 +1,9 @@
-require 'spec_helper'
+require "spec_helper"
 require "action_controller/metal/strong_parameters"
 
 describe Datagrid::Core do
-  describe '#original_scope' do
-    it 'does not wrap instance scope' do
+  describe "#original_scope" do
+    it "does not wrap instance scope" do
       grid = test_report do
         scope { Entry }
       end
@@ -11,7 +11,7 @@
       expect(grid.original_scope).to eq(Entry)
     end
 
-    it 'does not wrap class scope' do
+    it "does not wrap class scope" do
       klass = test_report_class do
         scope { Entry }
       end
@@ -20,7 +20,7 @@
     end
   end
 
-  context 'with 2 persisted entries' do
+  context "with 2 persisted entries" do
     before { 2.times { Entry.create } }
 
     let(:report_class) do
@@ -31,8 +31,7 @@ class ScopeTestReport
       ScopeTestReport
     end
 
-    describe '#scope' do
-
+    describe "#scope" do
       it "wraps scope" do
         grid = test_report do
           scope { Entry }
@@ -40,7 +39,7 @@ class ScopeTestReport
         expect(grid.scope).to be_kind_of(ActiveRecord::Relation)
       end
 
-      context 'in the class' do
+      context "in the class" do
         let(:report) { report_class.new }
 
         it { expect(report.scope.to_a.size).to eq(2) }
@@ -61,10 +60,10 @@ class TestGrid < ScopeTestReport
         end
       end
 
-      context 'changes scope on the fly' do
+      context "changes scope on the fly" do
         let(:report) do
           report_class.new.tap do |r|
-            r.scope { Entry.limit(1)}
+            r.scope { Entry.limit(1) }
           end
         end
 
@@ -72,7 +71,7 @@ class TestGrid < ScopeTestReport
         it { expect(report).to be_redefined_scope }
       end
 
-      context 'overriding scope by initializer' do
+      context "overriding scope by initializer" do
         let(:report) { report_class.new { Entry.limit(1) } }
 
         it { expect(report).to be_redefined_scope }
@@ -88,7 +87,7 @@ class TestGrid < ScopeTestReport
       end
 
       context "appending scope by initializer " do
-        let(:report) { report_class.new {|scope| scope.limit(1)} }
+        let(:report) { report_class.new { |scope| scope.limit(1) } }
         it { expect(report.scope.to_a.size).to eq(1) }
         it { expect(report.scope.order_values.size).to eq(1) }
         it { expect(report).to be_redefined_scope }
@@ -100,20 +99,20 @@ class TestGrid < ScopeTestReport
     it "should show all attribute values" do
       class InspectTest
         include Datagrid
-        scope {Entry}
+        scope { Entry }
         filter(:created_at, :date, range: true)
         column(:name)
       end
 
-      grid = InspectTest.new(created_at: ['2014-01-01', '2014-08-05'], descending: true, order: 'name')
-      expect(grid.inspect).to eq('#<InspectTest order: :name, descending: true, created_at: [Wed, 01 Jan 2014, Tue, 05 Aug 2014]>')
+      grid = InspectTest.new(created_at: %w[2014-01-01 2014-08-05], descending: true, order: "name")
+      expect(grid.inspect).to eq("#<InspectTest order: :name, descending: true, created_at: [Wed, 01 Jan 2014, Tue, 05 Aug 2014]>")
     end
   end
 
   describe "#==" do
     class EqualTest
       include Datagrid
-      scope {Entry}
+      scope { Entry }
       filter(:created_at, :date)
       column(:name)
       column(:created_at)
@@ -131,14 +130,14 @@ class EqualTest
       expect(EqualTest.new(order: :created_at)).to eq(EqualTest.new(order: "created_at"))
     end
     it "checks for redefined scope" do
-      expect(EqualTest.new).to_not eq(EqualTest.new {|s| s.reorder(:name)})
+      expect(EqualTest.new).to_not eq(EqualTest.new { |s| s.reorder(:name) })
     end
   end
 
-  describe 'dynamic helper' do
+  describe "dynamic helper" do
     it "should work" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:id)
         dynamic do
           column(:name)
@@ -146,7 +145,7 @@ class EqualTest
         end
       end
 
-      expect(grid.columns.map(&:name)).to eq([:id, :name, :category])
+      expect(grid.columns.map(&:name)).to eq(%i[id name category])
       expect(grid.class.columns.map(&:name)).to eq([:id])
 
       expect(grid.column_by_name(:id)).not_to be_nil
@@ -154,8 +153,8 @@ class EqualTest
     end
 
     it "has access to attributes" do
-      grid = test_report(attribute_name: 'value') do
-        scope {Entry}
+      grid = test_report(attribute_name: "value") do
+        scope { Entry }
         datagrid_attribute :attribute_name
         dynamic do
           value = attribute_name
@@ -163,12 +162,12 @@ class EqualTest
         end
       end
 
-      expect(grid.data_value(:name, Entry.create!)).to eq('value')
+      expect(grid.data_value(:name, Entry.create!)).to eq("value")
     end
 
     it "applies before instance scope" do
       klass = test_report_class do
-        scope {Entry}
+        scope { Entry }
         dynamic do
           scope do |s|
             s.limit(1)
@@ -184,8 +183,8 @@ class EqualTest
     end
 
     it "has access to grid attributes within scope" do
-      grid = test_report(name: 'one') do
-        scope {Entry}
+      grid = test_report(name: "one") do
+        scope { Entry }
         dynamic do
           scope do |s|
             s.where(name: name)
@@ -193,58 +192,56 @@ class EqualTest
         end
         filter(:name, dummy: true)
       end
-      one = Entry.create!(name: 'one')
-      two = Entry.create!(name: 'two')
+      one = Entry.create!(name: "one")
+      two = Entry.create!(name: "two")
       expect(grid.assets).to include(one)
       expect(grid.assets).to_not include(two)
     end
   end
 
   describe "ActionController::Parameters" do
-
     let(:params) do
-      ::ActionController::Parameters.new(name: 'one')
+      ::ActionController::Parameters.new(name: "one")
     end
 
     it "permites all attributes by default" do
-      expect {
+      expect do
         test_report(params) do
           scope { Entry }
           filter(:name)
         end
-      }.to_not raise_error
+      end.to_not raise_error
     end
     it "doesn't permit attributes when forbidden_attributes_protection is set" do
-      expect {
+      expect do
         test_report(params) do
           scope { Entry }
           self.forbidden_attributes_protection = true
           filter(:name)
         end
-      }.to raise_error(ActiveModel::ForbiddenAttributesError)
+      end.to raise_error(ActiveModel::ForbiddenAttributesError)
     end
     it "permits attributes when forbidden_attributes_protection is set and attributes are permitted" do
-      expect {
+      expect do
         test_report(params.permit!) do
           scope { Entry }
           self.forbidden_attributes_protection = true
           filter(:name)
         end
-      }.to_not raise_error
+      end.to_not raise_error
     end
   end
 
-
   describe ".query_param" do
     it "works" do
-      grid = test_report(name: 'value') do
-        scope {Entry}
+      grid = test_report(name: "value") do
+        scope { Entry }
         filter(:name)
         def param_name
-          'grid'
+          "grid"
         end
       end
-      expect(grid.query_params).to eq({grid: {name: 'value'}})
+      expect(grid.query_params).to eq({ grid: { name: "value" } })
     end
   end
 end
diff --git a/spec/datagrid/drivers/active_record_spec.rb b/spec/datagrid/drivers/active_record_spec.rb
index a3c418d..c41a1cf 100644
--- a/spec/datagrid/drivers/active_record_spec.rb
+++ b/spec/datagrid/drivers/active_record_spec.rb
@@ -1,13 +1,12 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Drivers::ActiveRecord do
-
   describe ".match?" do
     subject { described_class }
 
-    it {should be_match(Entry)}
-    it {should be_match(Entry.where(:id => 1))}
-    it {should_not be_match(MongoidEntry)}
+    it { should be_match(Entry) }
+    it { should be_match(Entry.where(id: 1)) }
+    it { should_not be_match(MongoidEntry) }
   end
 
   it "should convert any scope to AR::Relation" do
@@ -17,13 +16,15 @@
   end
 
   it "should support append_column_queries" do
-    scope = subject.append_column_queries(Entry.where({}), [Datagrid::Columns::Column.new(test_report_class, :sum_group_id, 'sum(entries.group_id)')])
+    scope = subject.append_column_queries(Entry.where({}),
+                                          [Datagrid::Columns::Column.new(test_report_class, :sum_group_id,
+                                                                         "sum(entries.group_id)")])
     expect(scope.to_sql.strip).to eq('SELECT "entries".*, sum(entries.group_id) AS sum_group_id FROM "entries"')
   end
 
   describe "Arel" do
     subject do
-      test_report(:order => :test, :descending => true) do
+      test_report(order: :test, descending: true) do
         scope { Entry }
         column(:test, order: Entry.arel_table[:group_id].count)
       end.assets
@@ -35,45 +36,41 @@
   end
 
   describe "gotcha #datagrid_where_by_timestamp" do
-
     subject do
       test_report(created_at: 10.days.ago..5.days.ago) do
-        scope {Entry}
+        scope { Entry }
 
-        filter(:created_at, :date, range: true) do |value, scope, grid|
+        filter(:created_at, :date, range: true) do |value, scope, _grid|
           scope.joins(:group).datagrid_where_by_timestamp("groups.created_at", value)
         end
       end.assets
     end
     it "includes object created in proper range" do
       expect(subject).to include(
-        Entry.create!(group: Group.create!(created_at: 7.days.ago)),
+        Entry.create!(group: Group.create!(created_at: 7.days.ago))
       )
     end
 
     it "excludes object created before the range" do
       expect(subject).to_not include(
-        Entry.create!(created_at: 7.days.ago, group: Group.create!(created_at: 11.days.ago)),
+        Entry.create!(created_at: 7.days.ago, group: Group.create!(created_at: 11.days.ago))
       )
     end
     it "excludes object created after the range" do
       expect(subject).to_not include(
-        Entry.create!(created_at: 7.days.ago, group: Group.create!(created_at: 4.days.ago)),
+        Entry.create!(created_at: 7.days.ago, group: Group.create!(created_at: 4.days.ago))
       )
     end
   end
 
   describe "batches usage" do
-
     it "should be incompatible with scope with limit" do
       report = test_report do
-        scope {Entry.limit(5)}
+        scope { Entry.limit(5) }
         self.batch_size = 20
         column(:id)
       end
       expect { report.data }.to raise_error(Datagrid::ConfigurationError)
     end
   end
-
-
 end
diff --git a/spec/datagrid/drivers/array_spec.rb b/spec/datagrid/drivers/array_spec.rb
index 1a82a27..c0263f9 100644
--- a/spec/datagrid/drivers/array_spec.rb
+++ b/spec/datagrid/drivers/array_spec.rb
@@ -1,17 +1,15 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Drivers::Array do
-
   describe ".match?" do
     subject { described_class }
 
-    it {should be_match(Array.new)}
-    it {should be_match(ActiveRecord::Result.new([], []))}
-    it {should_not be_match({})}
+    it { should be_match([]) }
+    it { should be_match(ActiveRecord::Result.new([], [])) }
+    it { should_not be_match({}) }
   end
 
   describe "api" do
-
     class ArrayGrid
       class User < Struct.new(:name, :age); end
       include Datagrid
@@ -20,7 +18,7 @@ class User < Struct.new(:name, :age); end
       end
 
       filter(:name)
-      filter(:age, :integer, :range => true)
+      filter(:age, :integer, range: true)
 
       column(:name)
       column(:age)
@@ -33,70 +31,66 @@ class User < Struct.new(:name, :age); end
 
     subject do
       ArrayGrid.new(_attributes).scope do
-        [ first, second, third ]
+        [first, second, third]
       end
     end
 
-
-    describe '#assets' do
+    describe "#assets" do
       subject { super().assets }
-      describe '#size' do
+      describe "#size" do
         subject { super().size }
-        it {should == 3}
+        it { should == 3 }
       end
     end
 
-    describe '#rows' do
+    describe "#rows" do
       subject { super().rows }
-      it {should == [["Vasya", 15], ["Petya", 12], ["Vova", 13]]}
+      it { should == [["Vasya", 15], ["Petya", 12], ["Vova", 13]] }
     end
 
-    describe '#header' do
+    describe "#header" do
       subject { super().header }
-      it {should ==[ "Name", "Age"]}
+      it { should == %w[Name Age] }
     end
 
-    describe '#data' do
+    describe "#data" do
       subject { super().data }
-      it {should == [[ "Name", "Age"], ["Vasya", 15], ["Petya", 12], ["Vova", 13]]}
+      it { should == [%w[Name Age], ["Vasya", 15], ["Petya", 12], ["Vova", 13]] }
     end
 
-
     describe "when some filters specified" do
-      let(:_attributes) { {:age => [12,14]} }
+      let(:_attributes) { { age: [12, 14] } }
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets }
-        it {should_not include(first)}
+        it { should_not include(first) }
       end
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets }
-        it {should include(second)}
+        it { should include(second) }
       end
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets }
-        it {should include(third)}
+        it { should include(third) }
       end
     end
 
     describe "when reverse ordering is specified" do
-      let(:_attributes) { {:order => :name, :descending => true} }
+      let(:_attributes) { { order: :name, descending: true } }
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets }
-        it {should == [third, first, second]}
+        it { should == [third, first, second] }
       end
     end
-
   end
 
   describe "when using enumerator scope" do
-
     it "should work fine" do
       grid = test_report(to_enum: true) do
-        scope {[]}
+        scope { [] }
         filter(:to_enum, :boolean) do |_, scope|
           scope.to_enum
         end
@@ -109,11 +103,11 @@ class User < Struct.new(:name, :age); end
     class HashGrid
       include Datagrid
       scope do
-        [{name: 'Bogdan', age: 30}, {name: 'Brad', age: 32}]
+        [{ name: "Bogdan", age: 30 }, { name: "Brad", age: 32 }]
       end
 
       filter(:name)
-      filter(:age, :integer, :range => true)
+      filter(:age, :integer, range: true)
 
       column(:name)
       column(:age)
@@ -126,9 +120,9 @@ class HashGrid
     end
 
     context "ordered" do
-      let(:_attributes) { { order: :name, descending: true }}
+      let(:_attributes) { { order: :name, descending: true } }
 
-      it { subject.assets.should == [ {name: 'Brad', age: 32}, {name: 'Bogdan', age: 30},] }
+      it { subject.assets.should == [{ name: "Brad", age: 32 }, { name: "Bogdan", age: 30 }] }
     end
   end
 end
diff --git a/spec/datagrid/drivers/mongo_mapper_spec.rb b/spec/datagrid/drivers/mongo_mapper_spec.rb
index ad6ee89..6cacf76 100644
--- a/spec/datagrid/drivers/mongo_mapper_spec.rb
+++ b/spec/datagrid/drivers/mongo_mapper_spec.rb
@@ -1,20 +1,16 @@
 require "spec_helper"
 
 describe Datagrid::Drivers::MongoMapper, :mongomapper do
-
   if defined?(MongoMapper)
     describe ".match?" do
-
       subject { described_class }
 
-      it {should be_match(MongoMapperEntry)}
+      it { should be_match(MongoMapperEntry) }
       # MongoMapper doesn't have a scoped method, instead it has a query method which returns a Plucky::Query object
-      it {should be_match(MongoMapperEntry.query)}
-      it {should_not be_match(Entry.where(:id => 1))}
-
+      it { should be_match(MongoMapperEntry.query) }
+      it { should_not be_match(Entry.where(id: 1)) }
     end
     describe "api" do
-
       subject do
         MongoMapperGrid.new(
           defined?(_attributes) ? _attributes : {}
@@ -23,79 +19,77 @@
 
       let!(:first) do
         MongoMapperEntry.create!(
-          :group_id => 2,
-          :name => "Main First",
-          :disabled => false
+          group_id: 2,
+          name: "Main First",
+          disabled: false
         )
       end
       let!(:second) do
         MongoMapperEntry.create!(
-          :group_id => 3,
-          :name => "Main Second",
-          :disabled => true
+          group_id: 3,
+          name: "Main Second",
+          disabled: true
         )
       end
 
-
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets }
-        it {should include(first, second)}
+        it { should include(first, second) }
       end
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets }
-        describe '#size' do
+        describe "#size" do
           subject { super().size }
-          it {should == 2}
+          it { should == 2 }
         end
       end
 
-      describe '#rows' do
+      describe "#rows" do
         subject { super().rows }
-        it {should == [["Main First", 2, false], ["Main Second", 3, true]]}
+        it { should == [["Main First", 2, false], ["Main Second", 3, true]] }
       end
 
-      describe '#header' do
+      describe "#header" do
         subject { super().header }
-        it {should ==[ "Name", "Group", "Disabled"]}
+        it { should == %w[Name Group Disabled] }
       end
 
-      describe '#data' do
+      describe "#data" do
         subject { super().data }
-        it {should == [[ "Name", "Group", "Disabled"], ["Main First", 2, false], ["Main Second", 3, true]]}
+        it { should == [["Name", "Group", "Disabled"], ["Main First", 2, false], ["Main Second", 3, true]] }
       end
 
-
       describe "when some filters specified" do
-        let(:_attributes) { {group_id: [3, nil]} }
+        let(:_attributes) { { group_id: [3, nil] } }
 
-        describe '#assets' do
+        describe "#assets" do
           subject { super().assets }
-          it {should_not include(first)}
+          it { should_not include(first) }
         end
 
-        describe '#assets' do
+        describe "#assets" do
           subject { super().assets }
-          it {should include(second)}
+          it { should include(second) }
         end
       end
 
       describe "when reverse ordering is specified" do
-        let(:_attributes) { {:order => :name, :descending => true} }
+        let(:_attributes) { { order: :name, descending: true } }
 
-        describe '#rows' do
+        describe "#rows" do
           subject { super().rows }
-          it {should == [["Main Second", 3, true], ["Main First", 2, false]]}
+          it { should == [["Main Second", 3, true], ["Main First", 2, false]] }
         end
       end
       it "should not provide default order for non declared fields" do
-        expect {
-          test_report(:order => :test) do
+        expect do
+          test_report(order: :test) do
             scope { MongoMapperEntry }
             column(:test)
-            end.assets
-        }.to raise_error(Datagrid::OrderUnsupported)
-          end
+          end.assets
+        end.to raise_error(Datagrid::OrderUnsupported)
       end
     end
   end
+end
diff --git a/spec/datagrid/drivers/mongoid_spec.rb b/spec/datagrid/drivers/mongoid_spec.rb
index 32108f5..b119951 100644
--- a/spec/datagrid/drivers/mongoid_spec.rb
+++ b/spec/datagrid/drivers/mongoid_spec.rb
@@ -1,18 +1,14 @@
 require "spec_helper"
 
 describe Datagrid::Drivers::Mongoid, :mongoid do
-
   describe ".match?" do
-
     subject { described_class }
 
-    it {should be_match(MongoidEntry)}
-    it {should be_match(MongoidEntry.scoped)}
-    it {should_not be_match(Entry.where(:id => 1))}
-
+    it { should be_match(MongoidEntry) }
+    it { should be_match(MongoidEntry.scoped) }
+    it { should_not be_match(Entry.where(id: 1)) }
   end
   describe "api" do
-
     subject do
       MongoidGrid.new(
         defined?(_attributes) ? _attributes : {}
@@ -21,79 +17,77 @@
 
     let!(:first) do
       MongoidEntry.create!(
-        :group_id => 2,
-        :name => "Main First",
-        :disabled => false
+        group_id: 2,
+        name: "Main First",
+        disabled: false
       )
     end
     let!(:second) do
       MongoidEntry.create!(
-        :group_id => 3,
-        :name => "Main Second",
-        :disabled => true
+        group_id: 3,
+        name: "Main Second",
+        disabled: true
       )
     end
 
-
-    describe '#assets' do
+    describe "#assets" do
       subject { super().assets }
-      it {should include(first, second)}
+      it { should include(first, second) }
     end
 
-    describe '#assets' do
+    describe "#assets" do
       subject { super().assets }
-      describe '#size' do
+      describe "#size" do
         subject { super().size }
-        it {should == 2}
+        it { should == 2 }
       end
     end
 
-    describe '#rows' do
+    describe "#rows" do
       subject { super().rows }
-      it {should == [["Main First", 2, false], ["Main Second", 3, true]]}
+      it { should == [["Main First", 2, false], ["Main Second", 3, true]] }
     end
 
-    describe '#header' do
+    describe "#header" do
       subject { super().header }
-      it {should ==[ "Name", "Group", "Disabled"]}
+      it { should == %w[Name Group Disabled] }
     end
 
-    describe '#data' do
+    describe "#data" do
       subject { super().data }
-      it {should == [[ "Name", "Group", "Disabled"], ["Main First", 2, false], ["Main Second", 3, true]]}
+      it { should == [["Name", "Group", "Disabled"], ["Main First", 2, false], ["Main Second", 3, true]] }
     end
 
-
     describe "when some filters specified" do
-      let(:_attributes) { {group_id: [3, nil]} }
+      let(:_attributes) { { group_id: [3, nil] } }
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets.map(&:_id) }
-        it {should_not include(first.id)}
+        it { should_not include(first.id) }
       end
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets }
-        it {should include(second)}
+        it { should include(second) }
       end
     end
 
     describe "when reverse ordering is specified" do
-      let(:_attributes) { {:order => :name, :descending => true} }
+      let(:_attributes) { { order: :name, descending: true } }
 
-      describe '#rows' do
+      describe "#rows" do
         subject { super().rows }
-        it {should == [["Main Second", 3, true], ["Main First", 2, false]]}
+        it { should == [["Main Second", 3, true], ["Main First", 2, false]] }
       end
     end
 
     it "should not provide default order for non declared fields" do
-      expect {
-        test_report(:order => :test) do
+      expect do
+        test_report(order: :test) do
           scope { MongoidEntry }
           column(:test)
         end.assets
-      }.to raise_error(Datagrid::OrderUnsupported)
+      end.to raise_error(Datagrid::OrderUnsupported)
     end
 
     it "should support batch_size" do
diff --git a/spec/datagrid/drivers/sequel_spec.rb b/spec/datagrid/drivers/sequel_spec.rb
index 7e462b7..731b19d 100644
--- a/spec/datagrid/drivers/sequel_spec.rb
+++ b/spec/datagrid/drivers/sequel_spec.rb
@@ -1,18 +1,14 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Drivers::Sequel do
-
   describe ".match?" do
-
     subject { described_class }
 
-    it {should be_match(SequelEntry)}
-    it {should be_match(SequelEntry.where(:id => 1))}
-    it {should_not be_match(Entry.where(:id => 1))}
-
+    it { should be_match(SequelEntry) }
+    it { should be_match(SequelEntry.where(id: 1)) }
+    it { should_not be_match(Entry.where(id: 1)) }
   end
   describe "api" do
-
     subject do
       SequelGrid.new(
         defined?(_attributes) ? _attributes : {}
@@ -21,16 +17,16 @@
 
     let!(:first) do
       SequelEntry.create(
-        :group_id => 2,
-        :name => "Main First",
-        :disabled => false
+        group_id: 2,
+        name: "Main First",
+        disabled: false
       )
     end
     let!(:second) do
       SequelEntry.create(
-        :group_id => 3,
-        :name => "Main Second",
-        :disabled => true
+        group_id: 3,
+        name: "Main Second",
+        disabled: true
       )
     end
 
@@ -40,73 +36,72 @@ class PaginationTest
         scope { SequelEntry }
       end
       grid = PaginationTest.new do |scope|
-        scope.paginate(1,25)
+        scope.paginate(1, 25)
       end
       expect(grid.rows.to_a).to be_kind_of(Array)
       expect(grid.assets.to_a).to be_kind_of(Array)
     end
 
-    describe '#assets' do
+    describe "#assets" do
       subject { super().assets }
-      it {should include(first, second)}
+      it { should include(first, second) }
     end
 
-    describe '#assets' do
+    describe "#assets" do
       subject { super().assets }
-      describe '#size' do
+      describe "#size" do
         subject { super().count }
-        it {should == 2}
+        it { should == 2 }
       end
     end
 
-    describe '#rows' do
+    describe "#rows" do
       subject { super().rows }
-      it {should == [["Main First", 2, false], ["Main Second", 3, true]]}
+      it { should == [["Main First", 2, false], ["Main Second", 3, true]] }
     end
 
-    describe '#header' do
+    describe "#header" do
       subject { super().header }
-      it {should ==[ "Name", "Group", "Disabled"]}
+      it { should == %w[Name Group Disabled] }
     end
 
-    describe '#data' do
+    describe "#data" do
       subject { super().data }
-      it {should == [[ "Name", "Group", "Disabled"], ["Main First", 2, false], ["Main Second", 3, true]]}
+      it { should == [["Name", "Group", "Disabled"], ["Main First", 2, false], ["Main Second", 3, true]] }
     end
 
-
     describe "when some filters specified" do
-      let(:_attributes) { {group_id: 3..100} }
+      let(:_attributes) { { group_id: 3..100 } }
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets.map(&:id) }
-        it {should_not include(first.id)}
+        it { should_not include(first.id) }
       end
 
-      describe '#assets' do
+      describe "#assets" do
         subject { super().assets }
-        it {should include(second)}
+        it { should include(second) }
       end
     end
 
     describe "when reverse ordering is specified" do
-      let(:_attributes) { {:order => :name, :descending => true} }
+      let(:_attributes) { { order: :name, descending: true } }
 
-      describe '#rows' do
+      describe "#rows" do
         subject { super().rows }
-        it {should == [["Main Second", 3, true], ["Main First", 2, false]]}
+        it { should == [["Main Second", 3, true], ["Main First", 2, false]] }
       end
     end
 
     it "should provide default order for non declared fields" do
-      expect {
-        test_report(:order => :test) do
+      expect do
+        test_report(order: :test) do
           scope { SequelEntry }
           column(:test) do
-            'test'
+            "test"
           end
         end.assets
-      }.to raise_error(Datagrid::OrderUnsupported)
+      end.to raise_error(Datagrid::OrderUnsupported)
     end
 
     it "should support batch_size" do
diff --git a/spec/datagrid/filters/base_filter_spec.rb b/spec/datagrid/filters/base_filter_spec.rb
index 10acc2c..44160da 100644
--- a/spec/datagrid/filters/base_filter_spec.rb
+++ b/spec/datagrid/filters/base_filter_spec.rb
@@ -1,19 +1,16 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Filters::BaseFilter do
-  
-
   it "should support default option as block" do
     report = test_report do
-      scope {Entry}
-      filter(:name, :string, :default => :name_default)
+      scope { Entry }
+      filter(:name, :string, default: :name_default)
       def name_default
-        'hello'
+        "hello"
       end
     end
-    expect(report.assets).to include(Entry.create!(:name => "hello"))
-    expect(report.assets).not_to include(Entry.create!(:name => "world"))
-    expect(report.assets).not_to include(Entry.create!(:name => ""))
+    expect(report.assets).to include(Entry.create!(name: "hello"))
+    expect(report.assets).not_to include(Entry.create!(name: "world"))
+    expect(report.assets).not_to include(Entry.create!(name: ""))
   end
-
 end
diff --git a/spec/datagrid/filters/composite_filters_spec.rb b/spec/datagrid/filters/composite_filters_spec.rb
index 8bb10ae..b40e2a4 100644
--- a/spec/datagrid/filters/composite_filters_spec.rb
+++ b/spec/datagrid/filters/composite_filters_spec.rb
@@ -1,15 +1,13 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Filters::CompositeFilters do
-
   describe ".date_range_filters" do
-
     it "should generate from date and to date filters" do
-      e1 = Entry.create!(:shipping_date => 6.days.ago)
-      e2 = Entry.create!(:shipping_date => 4.days.ago)
-      e3 = Entry.create!(:shipping_date => 1.days.ago)
-      assets = test_report(:from_shipping_date => 5.days.ago, :to_shipping_date => 2.day.ago) do
-        scope {Entry}
+      e1 = Entry.create!(shipping_date: 6.days.ago)
+      e2 = Entry.create!(shipping_date: 4.days.ago)
+      e3 = Entry.create!(shipping_date: 1.days.ago)
+      assets = test_report(from_shipping_date: 5.days.ago, to_shipping_date: 2.day.ago) do
+        scope { Entry }
         silence_warnings do
           date_range_filters(:shipping_date)
         end
@@ -22,7 +20,7 @@
     it "should support options" do
       report = test_report do
         silence_warnings do
-          date_range_filters(:shipping_date, {:default => 10.days.ago.to_date}, {:default => Date.today})
+          date_range_filters(:shipping_date, { default: 10.days.ago.to_date }, { default: Date.today })
         end
       end
       expect(report.from_shipping_date).to eq(10.days.ago.to_date)
@@ -31,7 +29,7 @@
     it "should support table name in field" do
       report = test_report do
         silence_warnings do
-          date_range_filters("entries.shipping_date", {:default => 10.days.ago.to_date}, {:default => Date.today})
+          date_range_filters("entries.shipping_date", { default: 10.days.ago.to_date }, { default: Date.today })
         end
       end
       expect(report.from_entries_shipping_date).to eq(10.days.ago.to_date)
@@ -40,13 +38,12 @@
   end
 
   describe ".integer_range_filters" do
-
     it "should generate from integer and to integer filters" do
-      e1 = Entry.create!(:group_id => 1)
-      e2 = Entry.create!(:group_id => 3)
-      e3 = Entry.create!(:group_id => 5)
-      assets = test_report(:from_group_id => 2, :to_group_id => 4) do
-        scope {Entry}
+      e1 = Entry.create!(group_id: 1)
+      e2 = Entry.create!(group_id: 3)
+      e3 = Entry.create!(group_id: 5)
+      assets = test_report(from_group_id: 2, to_group_id: 4) do
+        scope { Entry }
         silence_warnings do
           integer_range_filters(:group_id)
         end
@@ -58,7 +55,7 @@
     it "should support options" do
       report = test_report do
         silence_warnings do
-          integer_range_filters(:group_id, {:default => 0}, {:default => 100})
+          integer_range_filters(:group_id, { default: 0 }, { default: 100 })
         end
       end
       expect(report.from_group_id).to eq(0)
@@ -67,7 +64,7 @@
     it "should table name in field name" do
       report = test_report do
         silence_warnings do
-          integer_range_filters("entries.group_id", {:default => 0}, {:default => 100})
+          integer_range_filters("entries.group_id", { default: 0 }, { default: 100 })
         end
       end
       expect(report.from_entries_group_id).to eq(0)
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index e459283..56fd5ce 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -1,8 +1,7 @@
-require 'spec_helper'
+require "spec_helper"
 require "active_support/testing/time_helpers"
 
 describe Datagrid::Filters::DateFilter do
-
   it "supports date range argument" do
     e1 = Entry.create!(created_at: 7.days.ago)
     e2 = Entry.create!(created_at: 4.days.ago)
@@ -30,7 +29,7 @@
     expect(report.assets).not_to include(e2)
   end
 
-  {active_record: Entry, mongoid: MongoidEntry, sequel: SequelEntry}.each do |orm, klass|
+  { active_record: Entry, mongoid: MongoidEntry, sequel: SequelEntry }.each do |orm, klass|
     describe "with orm #{orm}", orm => true do
       describe "date to timestamp conversion" do
         let(:klass) { klass }
@@ -47,20 +46,20 @@ def entry_dated(date)
 
         context "when single date paramter given" do
           let(:_created_at) { Date.today }
-          it { should include(entry_dated(1.second.ago))}
-          it { should include(entry_dated(Date.today.end_of_day))}
-          it { should_not include(entry_dated(Date.today.beginning_of_day - 1.second))}
-          it { should_not include(entry_dated(Date.today.end_of_day + 1.second))}
+          it { should include(entry_dated(1.second.ago)) }
+          it { should include(entry_dated(Date.today.end_of_day)) }
+          it { should_not include(entry_dated(Date.today.beginning_of_day - 1.second)) }
+          it { should_not include(entry_dated(Date.today.end_of_day + 1.second)) }
         end
 
         context "when range date range given" do
           let(:_created_at) { [Date.yesterday, Date.today] }
-          it { should include(entry_dated(1.second.ago))}
-          it { should include(entry_dated(1.day.ago))}
-          it { should include(entry_dated(Date.today.end_of_day))}
-          it { should include(entry_dated(Date.yesterday.beginning_of_day))}
-          it { should_not include(entry_dated(Date.yesterday.beginning_of_day - 1.second))}
-          it { should_not include(entry_dated(Date.today.end_of_day + 1.second))}
+          it { should include(entry_dated(1.second.ago)) }
+          it { should include(entry_dated(1.day.ago)) }
+          it { should include(entry_dated(Date.today.end_of_day)) }
+          it { should include(entry_dated(Date.yesterday.beginning_of_day)) }
+          it { should_not include(entry_dated(Date.yesterday.beginning_of_day - 1.second)) }
+          it { should_not include(entry_dated(Date.today.end_of_day + 1.second)) }
         end
       end
     end
@@ -106,7 +105,6 @@ def entry_dated(date)
   end
 
   it "should find something in one day interval" do
-
     e1 = Entry.create!(created_at: 7.days.ago)
     e2 = Entry.create!(created_at: 4.days.ago)
     e3 = Entry.create!(created_at: 1.day.ago)
@@ -134,10 +132,9 @@ def entry_dated(date)
     expect(report.assets).to include(e3)
   end
 
-
   it "should support block" do
-    date = Date.new(2018, 01, 07)
-    time = Time.utc(2018, 01, 07, 2, 2)
+    date = Date.new(2018, 0o1, 0o7)
+    time = Time.utc(2018, 0o1, 0o7, 2, 2)
     report = test_report(created_at: date) do
       scope { Entry }
       filter(:created_at, :date, range: true) do |value|
@@ -148,7 +145,6 @@ def entry_dated(date)
     expect(report.assets).to include(Entry.create!(created_at: time))
   end
 
-
   context "when date format is configured" do
     around(:each) do |example|
       with_date_format do
@@ -158,40 +154,39 @@ def entry_dated(date)
 
     it "should have configurable date format" do
       report = test_report(created_at: "10/01/2013") do
-        scope  {Entry}
+        scope  { Entry }
         filter(:created_at, :date)
       end
-      expect(report.created_at).to eq(Date.new(2013,10,01))
+      expect(report.created_at).to eq(Date.new(2013, 10, 0o1))
     end
 
     it "should support default explicit date" do
       report = test_report(created_at: Date.parse("2013-10-01")) do
-        scope  {Entry}
+        scope  { Entry }
         filter(:created_at, :date)
       end
-      expect(report.created_at).to eq(Date.new(2013,10,01))
+      expect(report.created_at).to eq(Date.new(2013, 10, 0o1))
     end
   end
 
-
   it "should automatically reverse Array if first more than last" do
-    report = test_report(created_at: ["2013-01-01", "2012-01-01"]) do
-      scope  {Entry}
+    report = test_report(created_at: %w[2013-01-01 2012-01-01]) do
+      scope  { Entry }
       filter(:created_at, :date, range: true)
     end
-    expect(report.created_at).to eq([Date.new(2012, 01, 01), Date.new(2013, 01, 01)])
+    expect(report.created_at).to eq([Date.new(2012, 0o1, 0o1), Date.new(2013, 0o1, 0o1)])
   end
   it "should automatically reverse Array if first more than last" do
-    report = test_report(created_at: ["2013-01-01", "2012-01-01"]) do
-      scope  {Entry}
+    report = test_report(created_at: %w[2013-01-01 2012-01-01]) do
+      scope  { Entry }
       filter(:created_at, :date, range: true)
     end
-    expect(report.created_at).to eq([Date.new(2012, 01, 01), Date.new(2013, 01, 01)])
+    expect(report.created_at).to eq([Date.new(2012, 0o1, 0o1), Date.new(2013, 0o1, 0o1)])
   end
 
   it "should nullify blank range" do
     report = test_report(created_at: [nil, nil]) do
-      scope  {Entry}
+      scope  { Entry }
       filter(:created_at, :date, range: true)
     end
 
@@ -201,11 +196,10 @@ def entry_dated(date)
   it "should properly format date in filter_value_as_string" do
     with_date_format do
       report = test_report(created_at: "2012-01-02") do
-        scope  {Entry}
+        scope  { Entry }
         filter(:created_at, :date)
       end
       expect(report.filter_value_as_string(:created_at)).to eq("01/02/2012")
     end
   end
-
 end
diff --git a/spec/datagrid/filters/date_time_filter_spec.rb b/spec/datagrid/filters/date_time_filter_spec.rb
index 41c833c..425b442 100644
--- a/spec/datagrid/filters/date_time_filter_spec.rb
+++ b/spec/datagrid/filters/date_time_filter_spec.rb
@@ -1,7 +1,7 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Filters::DateTimeFilter do
-  {:active_record => Entry, :mongoid => MongoidEntry}.each do |orm, klass|
+  { active_record: Entry, mongoid: MongoidEntry }.each do |orm, klass|
     describe "with orm #{orm}", orm => true do
       describe "timestamp to timestamp conversion" do
         let(:klass) { klass }
@@ -24,38 +24,37 @@ def entry_dated(date)
         context "with single datetime paramter given" do
           let(:_created_at) { Time.now.change(sec: 0) }
           it { should include(entry_dated(_created_at)) }
-          it { should_not include(entry_dated(_created_at - 1.second))}
-          it { should_not include(entry_dated(_created_at + 1.second))}
+          it { should_not include(entry_dated(_created_at - 1.second)) }
+          it { should_not include(entry_dated(_created_at + 1.second)) }
         end
 
         context "with range datetime range given" do
           let(:_created_at) { [Time.now.beginning_of_day, Time.now.end_of_day] }
-          it { should include(entry_dated(1.second.ago))}
-          it { should include(entry_dated(Date.today.to_time))}
-          it { should include(entry_dated(Time.now.end_of_day.to_time))}
-          it { should_not include(entry_dated(Date.yesterday.end_of_day))}
-          it { should_not include(entry_dated(Date.tomorrow.beginning_of_day))}
+          it { should include(entry_dated(1.second.ago)) }
+          it { should include(entry_dated(Date.today.to_time)) }
+          it { should include(entry_dated(Time.now.end_of_day.to_time)) }
+          it { should_not include(entry_dated(Date.yesterday.end_of_day)) }
+          it { should_not include(entry_dated(Date.tomorrow.beginning_of_day)) }
         end
 
         context "with right open range" do
           let(:_created_at) { Time.now.beginning_of_day..nil }
-          it { should include(entry_dated(1.second.ago))}
-          it { should include(entry_dated(Date.today.to_time))}
-          it { should include(entry_dated(Time.now.end_of_day.to_time))}
-          it { should include(entry_dated(Date.tomorrow.beginning_of_day))}
-          it { should_not include(entry_dated(Date.yesterday.end_of_day))}
+          it { should include(entry_dated(1.second.ago)) }
+          it { should include(entry_dated(Date.today.to_time)) }
+          it { should include(entry_dated(Time.now.end_of_day.to_time)) }
+          it { should include(entry_dated(Date.tomorrow.beginning_of_day)) }
+          it { should_not include(entry_dated(Date.yesterday.end_of_day)) }
         end
 
         context "with left open range" do
           let(:_created_at) { nil..Time.now.end_of_day }
-          it { should include(entry_dated(1.second.ago))}
-          it { should include(entry_dated(Date.today.to_time))}
-          it { should include(entry_dated(Time.now.end_of_day.to_time))}
-          it { should include(entry_dated(Date.yesterday.end_of_day))}
-          it { should_not include(entry_dated(Date.tomorrow.beginning_of_day))}
+          it { should include(entry_dated(1.second.ago)) }
+          it { should include(entry_dated(Date.today.to_time)) }
+          it { should include(entry_dated(Time.now.end_of_day.to_time)) }
+          it { should include(entry_dated(Date.yesterday.end_of_day)) }
+          it { should_not include(entry_dated(Date.tomorrow.beginning_of_day)) }
         end
       end
-
     end
   end
 
@@ -126,7 +125,6 @@ def entry_dated(date)
     expect(report.assets).to include(e3)
   end
 
-
   it "should support block" do
     report = test_report(created_at: Time.now) do
       scope { Entry }
@@ -135,10 +133,9 @@ def entry_dated(date)
       end
     end
     expect(report.assets).not_to include(Entry.create!(created_at: 1.day.ago))
-    expect(report.assets).to include(Entry.create!(created_at: Time.now+1.day))
+    expect(report.assets).to include(Entry.create!(created_at: Time.now + 1.day))
   end
 
-
   context "when datetime format is configured" do
     around(:each) do |example|
       with_datetime_format("%m/%d/%Y %H:%M") do
@@ -148,27 +145,26 @@ def entry_dated(date)
 
     it "should have configurable datetime format" do
       report = test_report(created_at: "10/01/2013 01:00") do
-        scope  {Entry}
+        scope  { Entry }
         filter(:created_at, :datetime)
       end
-      expect(report.created_at).to eq(Time.new(2013,10,01,1,0))
+      expect(report.created_at).to eq(Time.new(2013, 10, 0o1, 1, 0))
     end
 
     it "should support default explicit datetime" do
       report = test_report(created_at: Time.parse("2013-10-01 01:00")) do
-        scope  {Entry}
+        scope  { Entry }
         filter(:created_at, :datetime)
       end
-      expect(report.created_at).to eq(Time.new(2013,10,01,1,0))
+      expect(report.created_at).to eq(Time.new(2013, 10, 0o1, 1, 0))
     end
   end
 
-
   it "should automatically reverse Array if first more than last" do
     report = test_report(created_at: ["2013-01-01 01:00", "2012-01-01 01:00"]) do
-      scope  {Entry}
+      scope  { Entry }
       filter(:created_at, :datetime, range: true)
     end
-    expect(report.created_at).to eq([Time.new(2012, 01, 01, 1, 0), Time.new(2013, 01, 01, 1, 0)])
+    expect(report.created_at).to eq([Time.new(2012, 0o1, 0o1, 1, 0), Time.new(2013, 0o1, 0o1, 1, 0)])
   end
 end
diff --git a/spec/datagrid/filters/dynamic_filter_spec.rb b/spec/datagrid/filters/dynamic_filter_spec.rb
index da6a052..6bbfa7c 100644
--- a/spec/datagrid/filters/dynamic_filter_spec.rb
+++ b/spec/datagrid/filters/dynamic_filter_spec.rb
@@ -1,128 +1,128 @@
 require "spec_helper"
 
-
 describe Datagrid::Filters::DynamicFilter do
   let(:report) do
     test_report do
-      scope  {Entry}
+      scope { Entry }
       filter(:condition, :dynamic)
     end
   end
 
   it "should support = operation" do
     report.condition = [:name, "=", "hello"]
-    expect(report.assets).to include(Entry.create!(:name => 'hello'))
-    expect(report.assets).not_to include(Entry.create!(:name => 'bye'))
+    expect(report.assets).to include(Entry.create!(name: "hello"))
+    expect(report.assets).not_to include(Entry.create!(name: "bye"))
   end
 
   it "should support >= operation" do
     report.condition = [:name, ">=", "d"]
-    expect(report.assets).to include(Entry.create!(:name => 'x'))
-    expect(report.assets).to include(Entry.create!(:name => 'd'))
-    expect(report.assets).not_to include(Entry.create!(:name => 'a'))
+    expect(report.assets).to include(Entry.create!(name: "x"))
+    expect(report.assets).to include(Entry.create!(name: "d"))
+    expect(report.assets).not_to include(Entry.create!(name: "a"))
   end
 
   it "should blank value" do
     report.condition = [:name, "=", ""]
-    expect(report.assets).to include(Entry.create!(:name => 'hello'))
+    expect(report.assets).to include(Entry.create!(name: "hello"))
   end
 
   it "should support =~ operation on strings" do
     report.condition = [:name, "=~", "ell"]
-    expect(report.assets).to include(Entry.create!(:name => 'hello'))
-    expect(report.assets).not_to include(Entry.create!(:name => 'bye'))
+    expect(report.assets).to include(Entry.create!(name: "hello"))
+    expect(report.assets).not_to include(Entry.create!(name: "bye"))
   end
 
   it "should support =~ operation integers" do
     report.condition = [:group_id, "=~", 2]
-    expect(report.assets).to include(Entry.create!(:group_id => 2))
-    expect(report.assets).not_to include(Entry.create!(:group_id => 1))
-    expect(report.assets).not_to include(Entry.create!(:group_id => 3))
+    expect(report.assets).to include(Entry.create!(group_id: 2))
+    expect(report.assets).not_to include(Entry.create!(group_id: 1))
+    expect(report.assets).not_to include(Entry.create!(group_id: 3))
   end
 
   it "should support >= operation on integer" do
     report.condition = [:group_id, ">=", 2]
-    expect(report.assets).to include(Entry.create!(:group_id => 3))
-    expect(report.assets).not_to include(Entry.create!(:group_id => 1))
+    expect(report.assets).to include(Entry.create!(group_id: 3))
+    expect(report.assets).not_to include(Entry.create!(group_id: 1))
   end
 
   it "should support <= operation on integer" do
     report.condition = [:group_id, "<=", 2]
-    expect(report.assets).to include(Entry.create!(:group_id => 1))
-    expect(report.assets).not_to include(Entry.create!(:group_id => 3))
+    expect(report.assets).to include(Entry.create!(group_id: 1))
+    expect(report.assets).not_to include(Entry.create!(group_id: 3))
   end
 
   it "should support <= operation on integer with string value" do
-    report.condition = [:group_id, "<=", '2']
-    expect(report.assets).to include(Entry.create!(:group_id => 1))
-    expect(report.assets).to include(Entry.create!(:group_id => 2))
-    expect(report.assets).not_to include(Entry.create!(:group_id => 3))
+    report.condition = [:group_id, "<=", "2"]
+    expect(report.assets).to include(Entry.create!(group_id: 1))
+    expect(report.assets).to include(Entry.create!(group_id: 2))
+    expect(report.assets).not_to include(Entry.create!(group_id: 3))
   end
 
   it "should nullify incorrect value for integer" do
-    report.condition = [:group_id, "<=", 'aa']
+    report.condition = [:group_id, "<=", "aa"]
     expect(report.condition).to eq([:group_id, "<=", nil])
   end
 
   it "should nullify incorrect value for date" do
-    report.condition = [:shipping_date, "<=", 'aa']
+    report.condition = [:shipping_date, "<=", "aa"]
     expect(report.condition).to eq([:shipping_date, "<=", nil])
   end
 
   it "should nullify incorrect value for datetime" do
-    report.condition = [:created_at, "<=", 'aa']
+    report.condition = [:created_at, "<=", "aa"]
     expect(report.condition).to eq([:created_at, "<=", nil])
   end
 
   it "should support date comparation operation by timestamp column" do
-    report.condition = [:created_at, "<=", '1986-08-05']
-    expect(report.condition).to eq([:created_at, "<=", Date.parse('1986-08-05')])
-    expect(report.assets).to include(Entry.create!(:created_at => Time.parse('1986-08-04 01:01:01')))
-    expect(report.assets).to include(Entry.create!(:created_at => Time.parse('1986-08-05 23:59:59')))
-    expect(report.assets).to include(Entry.create!(:created_at => Time.parse('1986-08-05 00:00:00')))
-    expect(report.assets).not_to include(Entry.create!(:created_at => Time.parse('1986-08-06 00:00:00')))
-    expect(report.assets).not_to include(Entry.create!(:created_at => Time.parse('1986-08-06 23:59:59')))
+    report.condition = [:created_at, "<=", "1986-08-05"]
+    expect(report.condition).to eq([:created_at, "<=", Date.parse("1986-08-05")])
+    expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-04 01:01:01")))
+    expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
+    expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:00")))
+    expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-06 00:00:00")))
+    expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-06 23:59:59")))
   end
 
   it "should support date = operation by timestamp column" do
-    report.condition = [:created_at, "=", '1986-08-05']
-    expect(report.condition).to eq([:created_at, "=", Date.parse('1986-08-05')])
-    expect(report.assets).not_to include(Entry.create!(:created_at => Time.parse('1986-08-04 23:59:59')))
-    expect(report.assets).to include(Entry.create!(:created_at => Time.parse('1986-08-05 23:59:59')))
-    expect(report.assets).to include(Entry.create!(:created_at => Time.parse('1986-08-05 00:00:01')))
-    #TODO: investigate SQLite issue and uncomment this line
-    #report.assets.should include(Entry.create!(:created_at => Time.parse('1986-08-05 00:00:00')))
-    expect(report.assets).not_to include(Entry.create!(:created_at => Time.parse('1986-08-06 23:59:59')))
+    report.condition = [:created_at, "=", "1986-08-05"]
+    expect(report.condition).to eq([:created_at, "=", Date.parse("1986-08-05")])
+    expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-04 23:59:59")))
+    expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
+    expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:01")))
+    # TODO: investigate SQLite issue and uncomment this line
+    # report.assets.should include(Entry.create!(:created_at => Time.parse('1986-08-05 00:00:00')))
+    expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-06 23:59:59")))
   end
 
   it "should support date =~ operation by timestamp column" do
-    report.condition = [:created_at, "=~", '1986-08-05']
-    expect(report.condition).to eq([:created_at, "=~", Date.parse('1986-08-05')])
-    expect(report.assets).not_to include(Entry.create!(:created_at => Time.parse('1986-08-04 23:59:59')))
-    expect(report.assets).to include(Entry.create!(:created_at => Time.parse('1986-08-05 23:59:59')))
-    expect(report.assets).to include(Entry.create!(:created_at => Time.parse('1986-08-05 00:00:01')))
-    #TODO: investigate SQLite issue and uncomment this line
-    #report.assets.should include(Entry.create!(:created_at => Time.parse('1986-08-05 00:00:00')))
-    expect(report.assets).not_to include(Entry.create!(:created_at => Time.parse('1986-08-06 23:59:59')))
+    report.condition = [:created_at, "=~", "1986-08-05"]
+    expect(report.condition).to eq([:created_at, "=~", Date.parse("1986-08-05")])
+    expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-04 23:59:59")))
+    expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
+    expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:01")))
+    # TODO: investigate SQLite issue and uncomment this line
+    # report.assets.should include(Entry.create!(:created_at => Time.parse('1986-08-05 00:00:00')))
+    expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-06 23:59:59")))
   end
 
   it "should support operations for invalid date" do
-    report.condition = [:shipping_date, "<=", '1986-08-05']
-    expect(report.assets).to include(Entry.create!(:shipping_date => '1986-08-04'))
-    expect(report.assets).to include(Entry.create!(:shipping_date => '1986-08-05'))
-    expect(report.assets).not_to include(Entry.create!(:shipping_date => '1986-08-06'))
+    report.condition = [:shipping_date, "<=", "1986-08-05"]
+    expect(report.assets).to include(Entry.create!(shipping_date: "1986-08-04"))
+    expect(report.assets).to include(Entry.create!(shipping_date: "1986-08-05"))
+    expect(report.assets).not_to include(Entry.create!(shipping_date: "1986-08-06"))
   end
   it "should support operations for invalid date" do
-    report.condition = [:shipping_date, "<=", Date.parse('1986-08-05')]
-    expect(report.assets).to include(Entry.create!(:shipping_date => '1986-08-04'))
-    expect(report.assets).to include(Entry.create!(:shipping_date => '1986-08-05'))
-    expect(report.assets).not_to include(Entry.create!(:shipping_date => '1986-08-06'))
+    report.condition = [:shipping_date, "<=", Date.parse("1986-08-05")]
+    expect(report.assets).to include(Entry.create!(shipping_date: "1986-08-04"))
+    expect(report.assets).to include(Entry.create!(shipping_date: "1986-08-05"))
+    expect(report.assets).not_to include(Entry.create!(shipping_date: "1986-08-06"))
   end
 
   it "should support allow_nil and allow_blank options" do
     grid = test_report do
-      scope {Entry}
-      filter(:condition, :dynamic, :allow_nil => true, :allow_blank => true, operations: ['>=', '<=']) do |(field, operation, value), scope|
+      scope { Entry }
+      filter(:condition, :dynamic, allow_nil: true, allow_blank: true,
+                                   operations: [">=", "<="]) do |(field, operation, value), scope|
         if value.blank?
           scope.where(disabled: false)
         else
@@ -134,16 +134,16 @@
     expect(grid.assets).to_not include(Entry.create!(disabled: true))
     expect(grid.assets).to include(Entry.create!(disabled: false))
 
-    grid.condition = [:group_id, '>=', 3]
+    grid.condition = [:group_id, ">=", 3]
     expect(grid.assets).to include(Entry.create!(disabled: true, group_id: 4))
     expect(grid.assets).to_not include(Entry.create!(disabled: false, group_id: 2))
   end
 
   it "should support custom operations" do
-    entry = Entry.create!(name: 'hello')
+    entry = Entry.create!(name: "hello")
 
     grid = test_report do
-      scope {Entry}
+      scope { Entry }
       filter(
         :condition, :dynamic, operations: ["=", "!="]
       ) do |(field, operation, value), scope|
@@ -166,10 +166,9 @@
   end
 
   it "should raise if unknown operation" do
-    report.condition = [:shipping_date, "<>", '1996-08-05']
-    expect{
+    report.condition = [:shipping_date, "<>", "1996-08-05"]
+    expect do
       report.assets
-    }.to raise_error(Datagrid::FilteringError)
+    end.to raise_error(Datagrid::FilteringError)
   end
-
 end
diff --git a/spec/datagrid/filters/enum_filter_spec.rb b/spec/datagrid/filters/enum_filter_spec.rb
index 5b60348..7192614 100644
--- a/spec/datagrid/filters/enum_filter_spec.rb
+++ b/spec/datagrid/filters/enum_filter_spec.rb
@@ -1,28 +1,27 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Filters::EnumFilter do
-
   it "should support select option" do
     report = test_report do
-      scope {Entry}
-      filter(:group_id, :enum, :select =>  [1,2] )
+      scope { Entry }
+      filter(:group_id, :enum, select: [1, 2])
     end
-    expect(report.filter_by_name(:group_id).select(report)).to eq([1,2])
+    expect(report.filter_by_name(:group_id).select(report)).to eq([1, 2])
   end
 
   it "should support select option as proc" do
     grid = test_report do
-      scope {Entry}
-      filter(:group_id, :enum, :select => proc { [1,2] })
+      scope { Entry }
+      filter(:group_id, :enum, select: proc { [1, 2] })
     end
-    expect(grid.filter_by_name(:group_id).select(grid)).to eq([1,2])
+    expect(grid.filter_by_name(:group_id).select(grid)).to eq([1, 2])
   end
 
   it "should support select option as proc with instace input" do
     klass = test_report do
-              scope {Entry}
-              filter(:group_id, :enum, :select => proc { |obj| obj.object_id })
-            end.class
+      scope { Entry }
+      filter(:group_id, :enum, select: proc { |obj| obj.object_id })
+    end.class
     instance = klass.new
     expect(klass.filter_by_name(:group_id).select(instance)).to eq(instance.object_id)
   end
@@ -30,22 +29,20 @@
   it "should initialize select option only on instanciation" do
     class ReportWithLazySelect
       include Datagrid
-      scope {Entry}
-      filter(:group_id, :enum, :select => proc { raise 'hello' })
+      scope { Entry }
+      filter(:group_id, :enum, select: proc { raise "hello" })
     end
   end
 
-
   it "should support select given as symbol" do
     report = test_report do
-      scope {Entry}
-      filter(:group_id, :enum, :select => :selectable_group_ids)
+      scope { Entry }
+      filter(:group_id, :enum, select: :selectable_group_ids)
       def selectable_group_ids
-        [1,3,5]
+        [1, 3, 5]
       end
     end
 
-    expect(report.filter_by_name(:group_id).select(report)).to eq([1,3,5])
+    expect(report.filter_by_name(:group_id).select(report)).to eq([1, 3, 5])
   end
-
 end
diff --git a/spec/datagrid/filters/extended_boolean_filter_spec.rb b/spec/datagrid/filters/extended_boolean_filter_spec.rb
index 22e36e5..ebff604 100644
--- a/spec/datagrid/filters/extended_boolean_filter_spec.rb
+++ b/spec/datagrid/filters/extended_boolean_filter_spec.rb
@@ -1,23 +1,22 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Filters::ExtendedBooleanFilter do
-
   it "should support select option" do
     grid = test_report do
-      scope {Entry}
+      scope { Entry }
       filter(:disabled, :xboolean)
     end
-    expect(grid.filter_by_name(:disabled).select(grid)).to eq([["Yes", "YES"], ["No", "NO"]])
+    expect(grid.filter_by_name(:disabled).select(grid)).to eq([%w[Yes YES], %w[No NO]])
   end
 
   it "should generate pass boolean value to filter block" do
     grid = test_report do
-      scope {Entry}
+      scope { Entry }
       filter(:disabled, :xboolean)
     end
 
-    disabled_entry = Entry.create!(:disabled => true)
-    enabled_entry = Entry.create!(:disabled => false)
+    disabled_entry = Entry.create!(disabled: true)
+    enabled_entry = Entry.create!(disabled: false)
 
     expect(grid.disabled).to be_nil
     expect(grid.assets).to include(disabled_entry, enabled_entry)
@@ -34,7 +33,7 @@
 
   it "should normalize true/false as YES/NO" do
     grid = test_report do
-      scope {Entry}
+      scope { Entry }
       filter(:disabled, :xboolean)
     end
     grid.disabled = true
@@ -46,5 +45,4 @@
     grid.disabled = "false"
     expect(grid.disabled).to eq("NO")
   end
-
 end
diff --git a/spec/datagrid/filters/float_filter_spec.rb b/spec/datagrid/filters/float_filter_spec.rb
index a745355..131cdd0 100644
--- a/spec/datagrid/filters/float_filter_spec.rb
+++ b/spec/datagrid/filters/float_filter_spec.rb
@@ -1,11 +1,10 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Filters::FloatFilter do
-  
   it "should support float values" do
-    g1 = Group.create!(:rating => 1.5)
-    g2 = Group.create!(:rating => 1.6)
-    report = test_report(:rating => 1.5) do
+    g1 = Group.create!(rating: 1.5)
+    g2 = Group.create!(rating: 1.6)
+    report = test_report(rating: 1.5) do
       scope { Group }
       filter(:rating, :float)
     end
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index 5d5a9f2..a7f1855 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -1,7 +1,6 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Filters::IntegerFilter do
-
   let(:entry1) { Entry.create!(group_id: 1) }
   let(:entry2) { Entry.create!(group_id: 2) }
   let(:entry3) { Entry.create!(group_id: 3) }
@@ -58,7 +57,6 @@
   end
 
   it "should find something in one integer interval" do
-
     report = test_report(group_id: (4..4)) do
       scope { Entry }
       filter(:group_id, :integer, range: true)
@@ -69,18 +67,16 @@
   end
 
   it "should support invalid range" do
-
     report = test_report(group_id: (7..1)) do
       scope { Entry }
       filter(:group_id, :integer, range: true)
     end
-    expect(report.group_id).to eq([1,7])
+    expect(report.group_id).to eq([1, 7])
     expect(report.assets).to include(entry7)
     expect(report.assets).to include(entry4)
     expect(report.assets).to include(entry1)
   end
 
-
   it "should support block" do
     report = test_report(group_id: 5) do
       scope { Entry }
@@ -92,42 +88,41 @@
     expect(report.assets).to include(entry5)
   end
 
-
   it "should not prefix table name if column is joined" do
-    report = test_report(rating: [4,nil]) do
+    report = test_report(rating: [4, nil]) do
       scope { Entry.joins(:group) }
       filter(:rating, :integer, range: true)
     end
-    expect(report.rating).to eq([4,nil])
+    expect(report.rating).to eq([4, nil])
     expect(report.assets).not_to include(Entry.create!(group: Group.create!(rating: 3)))
     expect(report.assets).to include(Entry.create!(group: Group.create!(rating: 5)))
   end
 
   it "should support multiple values" do
     report = test_report(group_id: "1,2") do
-      scope {Entry}
+      scope { Entry }
       filter(:group_id, :integer, multiple: true)
     end
-    expect(report.group_id).to eq([1,2])
+    expect(report.group_id).to eq([1, 2])
     expect(report.assets).to include(entry1)
     expect(report.assets).to include(entry2)
     expect(report.assets).not_to include(entry3)
   end
   it "should support custom separator multiple values" do
     report = test_report(group_id: "1|2") do
-      scope {Entry}
-      filter(:group_id, :integer, multiple: '|')
+      scope { Entry }
+      filter(:group_id, :integer, multiple: "|")
     end
-    expect(report.group_id).to eq([1,2])
+    expect(report.group_id).to eq([1, 2])
     expect(report.assets).to include(entry1)
     expect(report.assets).to include(entry2)
     expect(report.assets).not_to include(entry3)
   end
 
   it "should support multiple with allow_blank allow_nil options" do
-    report  = test_report do
-      scope {Entry}
-      filter(:group_id, :integer, multiple: true, allow_nil: false, allow_blank: true )
+    report = test_report do
+      scope { Entry }
+      filter(:group_id, :integer, multiple: true, allow_nil: false, allow_blank: true)
     end
     report.group_id = []
     expect(report.assets).to_not include(entry1)
@@ -143,7 +138,7 @@
   it "normalizes AR object to ID" do
     group = Group.create!
     report  = test_report(group_id: group) do
-      scope {Entry}
+      scope { Entry }
       filter(:group_id, :integer)
     end
 
diff --git a/spec/datagrid/filters/string_filter_spec.rb b/spec/datagrid/filters/string_filter_spec.rb
index 63b702f..8e84de3 100644
--- a/spec/datagrid/filters/string_filter_spec.rb
+++ b/spec/datagrid/filters/string_filter_spec.rb
@@ -1,35 +1,34 @@
 require "spec_helper"
 
 describe Datagrid::Filters::StringFilter do
-
   it "should support multiple values" do
-    report = test_report(:name => "one,two") do
-      scope {Entry}
-      filter(:name, :string, :multiple => true)
+    report = test_report(name: "one,two") do
+      scope { Entry }
+      filter(:name, :string, multiple: true)
     end
-    expect(report.assets).to include(Entry.create!( :name => "one"))
-    expect(report.assets).to include(Entry.create!( :name => "two"))
-    expect(report.assets).not_to include(Entry.create!( :name => "three"))
+    expect(report.assets).to include(Entry.create!(name: "one"))
+    expect(report.assets).to include(Entry.create!(name: "two"))
+    expect(report.assets).not_to include(Entry.create!(name: "three"))
   end
   it "should support custom separator multiple values" do
-    report = test_report(:name => "one,1|two,2") do
-      scope {Entry}
-      filter(:name, :string, :multiple => '|')
+    report = test_report(name: "one,1|two,2") do
+      scope { Entry }
+      filter(:name, :string, multiple: "|")
     end
-    expect(report.assets).to include(Entry.create!( :name => "one,1"))
-    expect(report.assets).to include(Entry.create!( :name => "two,2"))
-    expect(report.assets).not_to include(Entry.create!( :name => "one"))
-    expect(report.assets).not_to include(Entry.create!( :name => "two"))
+    expect(report.assets).to include(Entry.create!(name: "one,1"))
+    expect(report.assets).to include(Entry.create!(name: "two,2"))
+    expect(report.assets).not_to include(Entry.create!(name: "one"))
+    expect(report.assets).not_to include(Entry.create!(name: "two"))
   end
 
   it "supports range" do
-    report = test_report(:name => ['ab', 'lm']) do
-      scope {Entry}
+    report = test_report(name: %w[ab lm]) do
+      scope { Entry }
       filter(:name, :string, range: true)
     end
-    expect(report.assets).to include(Entry.create!( :name => "ac"))
-    expect(report.assets).to include(Entry.create!( :name => "kl"))
-    expect(report.assets).not_to include(Entry.create!( :name => "aa"))
-    expect(report.assets).not_to include(Entry.create!( :name => "mn"))
+    expect(report.assets).to include(Entry.create!(name: "ac"))
+    expect(report.assets).to include(Entry.create!(name: "kl"))
+    expect(report.assets).not_to include(Entry.create!(name: "aa"))
+    expect(report.assets).not_to include(Entry.create!(name: "mn"))
   end
 end
diff --git a/spec/datagrid/filters_spec.rb b/spec/datagrid/filters_spec.rb
index 9f9325a..91614dc 100644
--- a/spec/datagrid/filters_spec.rb
+++ b/spec/datagrid/filters_spec.rb
@@ -1,65 +1,63 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Filters do
-
   it "should support default option as proc" do
     expect(test_report do
-      scope {Entry}
-      filter(:created_at, :date, :default => proc { Date.today } )
+      scope { Entry }
+      filter(:created_at, :date, default: proc { Date.today })
     end.created_at).to eq(Date.today)
   end
 
   it "should stack with other filters" do
-    Entry.create(:name => "ZZ", :category => "first")
-    report = test_report(:name => "Pop", :category => "first") do
+    Entry.create(name: "ZZ", category: "first")
+    report = test_report(name: "Pop", category: "first") do
       scope  { Entry }
       filter(:name)
-      filter(:category, :enum, :select => ["first", "second"])
+      filter(:category, :enum, select: %w[first second])
     end
     expect(report.assets).to be_empty
   end
 
   it "should not support array argument for not multiple filter" do
     report = test_report do
-      scope {Entry}
+      scope { Entry }
       filter(:group_id, :integer)
     end
-    expect {
-      report.group_id = [1,2]
-    }.to raise_error(Datagrid::ArgumentError)
+    expect do
+      report.group_id = [1, 2]
+    end.to raise_error(Datagrid::ArgumentError)
   end
 
   it "should filter block with 2 arguments" do
     report = test_report do
-      scope {Entry}
+      scope { Entry }
       filter(:group_id, :integer) do |value, scope|
-        scope.where(:group_id => value)
+        scope.where(group_id: value)
       end
     end
-    expect {
-      report.group_id = [1,2]
-    }.to raise_error(Datagrid::ArgumentError)
+    expect do
+      report.group_id = [1, 2]
+    end.to raise_error(Datagrid::ArgumentError)
   end
 
-
   it "should initialize when report Scope table not exists" do
     class ModelWithoutTable < ActiveRecord::Base; end
     expect(ModelWithoutTable).not_to be_table_exists
     class TheReport
       include Datagrid
 
-      scope {ModelWithoutTable}
+      scope { ModelWithoutTable }
 
       filter(:name)
       filter(:limit)
     end
-    expect(TheReport.new(:name => 'hello')).not_to be_nil
+    expect(TheReport.new(name: "hello")).not_to be_nil
   end
 
   it "should support inheritence" do
     parent = Class.new do
       include Datagrid
-      scope {Entry}
+      scope { Entry }
       filter(:name)
     end
     child = Class.new(parent) do
@@ -70,11 +68,10 @@ class TheReport
   end
 
   describe "allow_blank and allow_nil options" do
-
     def check_performed(value, result, **options)
       $FILTER_PERFORMED = false
-      report = test_report(:name => value) do
-        scope {Entry}
+      report = test_report(name: value) do
+        scope { Entry }
         filter(:name, **options) do |_|
           $FILTER_PERFORMED = true
           self
@@ -87,18 +84,18 @@ def check_performed(value, result, **options)
 
     it "should support allow_blank argument" do
       [nil, "", " "].each do |value|
-        check_performed(value, true, :allow_blank => true)
+        check_performed(value, true, allow_blank: true)
       end
     end
 
     it "should support allow_nil argument" do
-      check_performed(nil, true, :allow_nil => true)
+      check_performed(nil, true, allow_nil: true)
     end
 
     it "should support combination on allow_nil and allow_blank" do
-      check_performed(nil, false, :allow_nil => false, :allow_blank => true)
-      check_performed("", true, :allow_nil => false, :allow_blank => true)
-      check_performed(nil, true, :allow_nil => true, :allow_blank => false)
+      check_performed(nil, false, allow_nil: false, allow_blank: true)
+      check_performed("", true, allow_nil: false, allow_blank: true)
+      check_performed(nil, true, allow_nil: true, allow_blank: false)
     end
   end
 
@@ -106,71 +103,67 @@ def check_performed(value, result, **options)
     it "should create default filter if scope respond to filter name method" do
       Entry.create!
       Entry.create!
-      grid = test_report(:limit => 1) do
-        scope {Entry}
+      grid = test_report(limit: 1) do
+        scope { Entry }
         filter(:limit)
       end
       expect(grid.assets.to_a.size).to eq(1)
     end
-
   end
   describe "default filter as scope" do
     it "should create default filter if scope respond to filter name method" do
       Entry.create!
-      grid = test_report(:custom => 'skip') do
-        scope {Entry}
+      grid = test_report(custom: "skip") do
+        scope { Entry }
         filter(:custom) do |value|
-          if value != 'skip'
-            where(:custom => value)
-          end
+          where(custom: value) if value != "skip"
         end
       end
       expect(grid.assets).not_to be_empty
     end
-
   end
 
   describe "positioning filter before another" do
     it "should insert the filter before the specified element" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         filter(:limit)
-        filter(:name, :before => :limit)
+        filter(:name, before: :limit)
       end
-      expect(grid.filters.index {|f| f.name == :name}).to eq(0)
+      expect(grid.filters.index { |f| f.name == :name }).to eq(0)
     end
   end
 
   describe "positioning filter after another" do
     it "should insert the filter before the specified element" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         filter(:limit)
         filter(:name)
-        filter(:group_id, :after => :limit)
+        filter(:group_id, after: :limit)
       end
-      expect(grid.filters.index {|f| f.name == :group_id}).to eq(1)
+      expect(grid.filters.index { |f| f.name == :group_id }).to eq(1)
     end
   end
 
   it "should support dummy filter" do
     grid = test_report do
       scope { Entry }
-      filter(:period, :date, :dummy => true, :default => proc { Date.today })
+      filter(:period, :date, dummy: true, default: proc { Date.today })
     end
-    Entry.create!(:created_at => 3.days.ago)
+    Entry.create!(created_at: 3.days.ago)
     expect(grid.assets).not_to be_empty
   end
 
   describe "#filter_by" do
     it "should allow partial filtering" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         filter(:id)
         filter(:name)
       end
-      Entry.create!(:name => 'hello')
-      grid.attributes = {:id => -1, :name => 'hello'}
+      Entry.create!(name: "hello")
+      grid.attributes = { id: -1, name: "hello" }
       expect(grid.assets).to be_empty
       expect(grid.filter_by(:name)).not_to be_empty
     end
@@ -178,7 +171,7 @@ def check_performed(value, result, **options)
 
   it "supports dynamic header" do
     grid = test_report do
-      scope {Entry}
+      scope { Entry }
       filter(:id, :integer, header: proc { rand(10**9) })
     end
 
@@ -186,11 +179,10 @@ def check_performed(value, result, **options)
     expect(filter.header).to_not eq(filter.header)
   end
 
-
   describe "#filter_by_name" do
     it "should return filter object" do
       r = test_report do
-        scope {Entry}
+        scope { Entry }
         filter(:id, :integer)
       end
 
@@ -200,7 +192,6 @@ def check_performed(value, result, **options)
   end
 
   describe "tranlations" do
-
     module ::Ns46
       class TranslatedReport
         include Datagrid
@@ -214,66 +205,64 @@ class InheritedReport < TranslatedReport
 
     it "translates filter with namespace" do
       grid = Ns46::TranslatedReport.new
-      store_translations(:en, datagrid: {:"ns46/translated_report" => {filters: {name: "Navn"}}}) do
+      store_translations(:en, datagrid: { "ns46/translated_report": { filters: { name: "Navn" } } }) do
         expect(grid.filters.map(&:header)).to eq(["Navn"])
       end
     end
 
     it "translates filter using defaults namespace" do
       grid = Ns46::TranslatedReport.new
-      store_translations(:en, datagrid: {defaults: {filters: {name: "Navn"}}}) do
+      store_translations(:en, datagrid: { defaults: { filters: { name: "Navn" } } }) do
         expect(grid.filters.map(&:header)).to eq(["Navn"])
       end
     end
 
     it "translates filter using parent report" do
       grid = Ns46::InheritedReport.new
-      store_translations(:en, datagrid: {:"ns46/translated_report" => {filters: {name: "Navn"}}}) do
+      store_translations(:en, datagrid: { "ns46/translated_report": { filters: { name: "Navn" } } }) do
         expect(grid.filters.map(&:header)).to eq(["Navn"])
       end
     end
   end
 
-
   describe "#select_options" do
     it "should return select options" do
       filters = {
-        id: [1,2],
-        name: [['a', 1], ['b', 2]],
-        category: {a: 1, b: 2},
+        id: [1, 2],
+        name: [["a", 1], ["b", 2]],
+        category: { a: 1, b: 2 }
       }
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         filters.each do |name, options|
           filter(name, :enum, select: options, multiple: true)
         end
       end
       filters.each do |name, options|
         expect(grid.select_options(name)).to eq(options)
-        expect(grid.select_values(name)).to eq([1,2])
+        expect(grid.select_values(name)).to eq([1, 2])
         grid.select_all(name)
-        expect(grid.public_send(name)).to eq([1,2])
+        expect(grid.public_send(name)).to eq([1, 2])
       end
     end
 
     it "should raise ArgumentError for filter without options" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         filter(:id, :integer)
       end
-      expect {
+      expect do
         grid.select_options(:id)
-      }.to raise_error(Datagrid::ArgumentError)
+      end.to raise_error(Datagrid::ArgumentError)
     end
   end
 
   describe "#inspect" do
     it "should list all fitlers with types" do
-
       module ::NsInspect
         class TestGrid
           include Datagrid
-          scope {Entry}
+          scope { Entry }
           filter(:id, :integer)
           filter(:name, :string)
           filter(:current_user)
@@ -288,18 +277,17 @@ class TestGrid
     it "dislays no filters" do
       class TestGrid8728
         include Datagrid
-        scope {Entry}
+        scope { Entry }
       end
 
       expect(TestGrid8728.inspect).to eq("TestGrid8728(no filters)")
     end
   end
 
-
   describe ":if :unless options" do
     it "supports :if option" do
       klass = test_report_class do
-        scope {Entry}
+        scope { Entry }
         filter(:admin_mode, :boolean, dummy: true)
         filter(:id, :integer, if: :admin_mode)
         filter(:name, :integer, unless: :admin_mode)
@@ -313,11 +301,12 @@ class TestGrid8728
       expect(non_admin_filters).to include(:name)
     end
 
-    context 'with delegation to attribute' do
-      let(:role) { OpenStruct.new('admin?' => admin) }
+    context "with delegation to attribute" do
+      let(:role) { OpenStruct.new("admin?" => admin) }
       let(:klass) do
         test_report_class do
           attr_accessor :role
+
           delegate :admin?, to: :role
 
           scope { Entry }
@@ -328,13 +317,13 @@ class TestGrid8728
 
       subject { klass.new(role: role).filters.map(&:name) }
 
-      context 'when condition is true' do
+      context "when condition is true" do
         let(:admin) { true }
 
         it { is_expected.to include(:id) }
       end
 
-      context 'when condition is false' do
+      context "when condition is false" do
         let(:admin) { false }
 
         it { is_expected.to_not include(:id) }
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 6aa5826..dac013b 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -1,5 +1,4 @@
-# encoding: UTF-8
-require 'spec_helper'
+require "spec_helper"
 require "action_controller"
 
 class MyFormBuilder
@@ -10,18 +9,17 @@ class MyTemplate
   include ActionView::Helpers::FormHelper
 end
 
-
 describe Datagrid::FormBuilder do
   let(:template) do
     action_view_template
   end
 
-  let(:view) { ActionView::Helpers::FormBuilder.new(:report, _grid, template, view_options)}
+  let(:view) { ActionView::Helpers::FormBuilder.new(:report, _grid, template, view_options) }
   let(:view_options) { {} }
 
   describe ".datagrid_filter" do
     it "should work for every filter type" do
-      Datagrid::Filters::FILTER_TYPES.each do |type, klass|
+      Datagrid::Filters::FILTER_TYPES.each do |_type, klass|
         expect(Datagrid::FormBuilder.instance_methods.map(&:to_sym)).to include(klass.form_builder_helper_name)
       end
     end
@@ -33,218 +31,251 @@ class MyTemplate
     let(:_filter_options) { {} }
     let(:_filter_block) { nil }
     context "with default filter type" do
-      let(:_grid) {
+      let(:_grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(:name)
         end
-      }
+      end
       let(:_filter) { :name }
-      it { should equal_to_dom(
-        '<input class="name default_filter" type="text" name="report[name]" id="report_name"/>'
-      )}
+      it {
+        should equal_to_dom(
+          '<input class="name default_filter" type="text" name="report[name]" id="report_name"/>'
+        )
+      }
     end
     context "with integer filter type" do
       let(:_filter) { :group_id }
-      let(:_grid) {
+      let(:_grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(:group_id, :integer)
         end
+      end
+      it {
+        should equal_to_dom(
+          '<input class="group_id integer_filter" type="text" name="report[group_id]" id="report_group_id"/>'
+        )
       }
-      it { should equal_to_dom(
-        '<input class="group_id integer_filter" type="text" name="report[group_id]" id="report_group_id"/>'
-      )}
 
       context "when partials option is passed for filter that don't support range" do
-        let(:view_options) { {partials: 'anything' } }
-        it { should equal_to_dom(
-          '<input class="group_id integer_filter" type="text" name="report[group_id]" id="report_group_id"/>'
-        )}
+        let(:view_options) { { partials: "anything" } }
+        it {
+          should equal_to_dom(
+            '<input class="group_id integer_filter" type="text" name="report[group_id]" id="report_group_id"/>'
+          )
+        }
       end
     end
 
     context "with date filter type" do
       let(:_filter) { :created_at }
-      let(:_grid) {
+      let(:_grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(:created_at, :date)
         end
+      end
+      it {
+        should equal_to_dom(
+          '<input class="created_at date_filter" type="text" name="report[created_at]" id="report_created_at"/>'
+        )
       }
-      it { should equal_to_dom(
-        '<input class="created_at date_filter" type="text" name="report[created_at]" id="report_created_at"/>'
-      )}
       context "when special date format specified" do
         around(:each) do |example|
-          _grid.created_at = Date.parse('2012-01-02')
+          _grid.created_at = Date.parse("2012-01-02")
           with_date_format do
             example.run
           end
         end
-        it { should equal_to_dom(
-          '<input value="01/02/2012" class="created_at date_filter" type="text" name="report[created_at]" id="report_created_at"/>'
-        )}
+        it {
+          should equal_to_dom(
+            '<input value="01/02/2012" class="created_at date_filter" type="text" name="report[created_at]" id="report_created_at"/>'
+          )
+        }
       end
     end
     context "with input_options" do
       context "type is date" do
         let(:_filter) { :created_at }
-        let(:_grid) {
+        let(:_grid) do
           test_report do
-            scope {Entry}
-            filter(:created_at, :date, input_options: {type: :date})
+            scope { Entry }
+            filter(:created_at, :date, input_options: { type: :date })
           end
+        end
+        it {
+          should equal_to_dom(
+            '<input type="date" class="created_at date_filter" name="report[created_at]" id="report_created_at"/>'
+          )
         }
-        it { should equal_to_dom(
-          '<input type="date" class="created_at date_filter" name="report[created_at]" id="report_created_at"/>'
-        )}
       end
       context "type is textarea" do
         let(:_filter) { :name }
-        let(:_grid) {
+        let(:_grid) do
           test_report do
-            scope {Entry}
-            filter(:name, :string, input_options: {type: :textarea})
+            scope { Entry }
+            filter(:name, :string, input_options: { type: :textarea })
           end
+        end
+        it {
+          should equal_to_dom(
+            '<textarea class="name string_filter" name="report[name]" id="report_name"/>'
+          )
         }
-        it { should equal_to_dom(
-          '<textarea class="name string_filter" name="report[name]" id="report_name"/>'
-        )}
       end
 
       context "type is datetime-local" do
         let(:_filter) { :created_at }
-        let(:_grid) {
+        let(:_grid) do
           test_report(created_at: Time.new(2024, 1, 1, 9, 25, 15)) do
-            scope {Entry}
-            filter(:created_at, :datetime, input_options: {type: "datetime-local"})
+            scope { Entry }
+            filter(:created_at, :datetime, input_options: { type: "datetime-local" })
           end
+        end
+        it {
+          should equal_to_dom(
+            '<input type="datetime-local" class="created_at date_time_filter" value="2024-01-01T09:25:15" name="report[created_at]" id="report_created_at"/>'
+          )
         }
-        it { should equal_to_dom(
-          '<input type="datetime-local" class="created_at date_time_filter" value="2024-01-01T09:25:15" name="report[created_at]" id="report_created_at"/>'
-        )}
 
         context "nil value option" do
           let(:_filter_options) do
             { value: nil }
           end
-          it { should equal_to_dom(
-            '<input type="datetime-local" value="" class="created_at date_time_filter" name="report[created_at]" id="report_created_at"/>'
-          )}
+          it {
+            should equal_to_dom(
+              '<input type="datetime-local" value="" class="created_at date_time_filter" name="report[created_at]" id="report_created_at"/>'
+            )
+          }
         end
       end
 
       context "type is date" do
         let(:_filter) { :created_at }
-        let(:_grid) {
+        let(:_grid) do
           test_report(created_at: Date.new(2024, 1, 1)) do
-            scope {Entry}
-            filter(:created_at, :datetime, input_options: {type: :date})
+            scope { Entry }
+            filter(:created_at, :datetime, input_options: { type: :date })
           end
+        end
+        it {
+          should equal_to_dom(
+            '<input type="date" class="created_at date_time_filter" value="2024-01-01" name="report[created_at]" id="report_created_at"/>'
+          )
         }
-        it { should equal_to_dom(
-          '<input type="date" class="created_at date_time_filter" value="2024-01-01" name="report[created_at]" id="report_created_at"/>'
-        )}
       end
     end
 
     context "with integer filter type and range option" do
       let(:_filter) { :group_id }
-      let(:_grid) {
-        test_report(:group_id => _range) do
-          scope {Entry}
-          filter(:group_id, :integer, :range => true)
+      let(:_grid) do
+        test_report(group_id: _range) do
+          scope { Entry }
+          filter(:group_id, :integer, range: true)
         end
-      }
+      end
       context "when datagrid_filter options has id" do
-        let(:_filter_options) { {:id => "hello"} }
-        let(:_range) { [1,2]}
-        it { should equal_to_dom(
-          '<input value="1" id="from_hello" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
-          '<span class="separator integer"> - </span>' +
-          '<input value="2" id="to_hello" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
-        )}
+        let(:_filter_options) { { id: "hello" } }
+        let(:_range) { [1, 2] }
+        it {
+          should equal_to_dom(
+            '<input value="1" id="from_hello" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
+            '<span class="separator integer"> - </span>' +
+            '<input value="2" id="to_hello" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
+          )
+        }
       end
       context "with only left bound" do
-
-        let(:_range) { [10, nil]}
-        it { should equal_to_dom(
-          '<input value="10" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
-          '<span class="separator integer"> - </span>' +
-          '<input class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
-        )}
+        let(:_range) { [10, nil] }
+        it {
+          should equal_to_dom(
+            '<input value="10" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
+            '<span class="separator integer"> - </span>' +
+            '<input class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
+          )
+        }
         it { should be_html_safe }
       end
       context "with only right bound" do
-        let(:_range) { [nil, 10]}
-        it { should equal_to_dom(
-          '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
-          '<span class="separator integer"> - </span>' +
-          '<input value="10" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
-        )}
+        let(:_range) { [nil, 10] }
+        it {
+          should equal_to_dom(
+            '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
+            '<span class="separator integer"> - </span>' +
+            '<input value="10" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
+          )
+        }
         it { should be_html_safe }
       end
 
       context "with invalid range value" do
         let(:_range) { 2..1 }
-        it { should equal_to_dom(
-          '<input value="1" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
-          '<span class="separator integer"> - </span>' +
-          '<input value="2" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
-        )}
+        it {
+          should equal_to_dom(
+            '<input value="1" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
+            '<span class="separator integer"> - </span>' +
+            '<input value="2" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
+          )
+        }
       end
 
       context "with custom partials option and template exists" do
-        let(:view_options) { { :partials => 'custom_range' } }
+        let(:view_options) { { partials: "custom_range" } }
         let(:_range) { nil }
-        it { should equal_to_dom(
-          "custom_range_partial"
-        ) }
+        it {
+          should equal_to_dom(
+            "custom_range_partial"
+          )
+        }
       end
 
       context "when custom partial doesn't exist" do
-        let(:view_options) { { :partials => 'not_existed' } }
+        let(:view_options) { { partials: "not_existed" } }
         let(:_range) { nil }
-        it { should equal_to_dom(
-          '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"><span class="separator integer"> - </span><input class="to group_id integer_filter" multiple type="text" name="report[group_id][]">'
-        ) }
-
+        it {
+          should equal_to_dom(
+            '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"><span class="separator integer"> - </span><input class="to group_id integer_filter" multiple type="text" name="report[group_id][]">'
+          )
+        }
       end
     end
 
     context "with float filter type and range option" do
       let(:_filter) { :rating }
-      let(:_grid) {
-        test_report(:rating => _range) do
-          scope {Group}
-          filter(:rating, :float, :range => true)
-        end
+      let(:_grid) do
+        test_report(rating: _range) do
+          scope { Group }
+          filter(:rating, :float, range: true)
+        end
+      end
+      let(:_range) { [1.5, 2.5] }
+      it {
+        should equal_to_dom(
+          '<input value="1.5" class="from rating float_filter" multiple type="text" name="report[rating][]"/>' +
+          '<span class="separator float"> - </span>' +
+          '<input value="2.5" class="to rating float_filter" multiple type="text" name="report[rating][]"/>'
+        )
       }
-      let(:_range) { [1.5,2.5]}
-      it { should equal_to_dom(
-        '<input value="1.5" class="from rating float_filter" multiple type="text" name="report[rating][]"/>' +
-        '<span class="separator float"> - </span>' +
-        '<input value="2.5" class="to rating float_filter" multiple type="text" name="report[rating][]"/>'
-      )}
     end
 
     context "with date filter type and range option" do
       let(:_filter) { :created_at }
-      let(:_grid) {
-        test_report(:created_at => _range) do
-          scope {Entry}
-          filter(:created_at, :date, :range => true)
+      let(:_grid) do
+        test_report(created_at: _range) do
+          scope { Entry }
+          filter(:created_at, :date, range: true)
         end
-      }
+      end
       context "with only left bound" do
-
-        let(:_range) { ["2012-01-03", nil]}
-        it { should equal_to_dom(
-          '<input value="2012-01-03" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-          '<span class="separator date"> - </span>' +
-          '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
-        )}
+        let(:_range) { ["2012-01-03", nil] }
+        it {
+          should equal_to_dom(
+            '<input value="2012-01-03" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
+            '<span class="separator date"> - </span>' +
+            '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
+          )
+        }
         it { should be_html_safe }
       end
       context "when special date format specified" do
@@ -253,31 +284,36 @@ class MyTemplate
             example.run
           end
         end
-        let(:_range) { ["2013/01/01", '2013/02/02']}
-        it { should equal_to_dom(
-          '<input value="01/01/2013" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-          '<span class="separator date"> - </span>' +
-          '<input value="02/02/2013" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
-        )}
+        let(:_range) { ["2013/01/01", "2013/02/02"] }
+        it {
+          should equal_to_dom(
+            '<input value="01/01/2013" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
+            '<span class="separator date"> - </span>' +
+            '<input value="02/02/2013" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
+          )
+        }
       end
       context "with only right bound" do
-
-        let(:_range) { [nil, "2012-01-03"]}
-        it { should equal_to_dom(
-          '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-          '<span class="separator date"> - </span>' +
-          '<input value="2012-01-03" class="to created_at date_filter" multiple type="text"  name="report[created_at][]"/>'
-        )}
+        let(:_range) { [nil, "2012-01-03"] }
+        it {
+          should equal_to_dom(
+            '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
+            '<span class="separator date"> - </span>' +
+            '<input value="2012-01-03" class="to created_at date_filter" multiple type="text"  name="report[created_at][]"/>'
+          )
+        }
         it { should be_html_safe }
       end
 
       context "with invalid range value" do
-        let(:_range) { Date.parse('2012-01-02')..Date.parse('2012-01-01') }
-        it { should equal_to_dom(
-          '<input value="2012-01-01" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-          '<span class="separator date"> - </span>' +
-          '<input value="2012-01-02" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
-        )}
+        let(:_range) { Date.parse("2012-01-02")..Date.parse("2012-01-01") }
+        it {
+          should equal_to_dom(
+            '<input value="2012-01-01" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
+            '<span class="separator date"> - </span>' +
+            '<input value="2012-01-02" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
+          )
+        }
       end
       context "with blank range value" do
         around(:each) do |example|
@@ -286,100 +322,118 @@ class MyTemplate
           end
         end
         let(:_range) { [nil, nil] }
-        it { should equal_to_dom(
-          '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-          '<span class="separator date"> - </span>' +
-          '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
-        )}
+        it {
+          should equal_to_dom(
+            '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
+            '<span class="separator date"> - </span>' +
+            '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
+          )
+        }
       end
     end
     context "with enum filter type" do
       let(:_filter) { :category }
       let(:_category_filter_options) { {} }
-      let(:_grid) {
+      let(:_grid) do
         filter_options = _category_filter_options
         test_report do
-          scope {Entry}
-          filter(:category, :enum, select: ["first", "second"], **filter_options)
+          scope { Entry }
+          filter(:category, :enum, select: %w[first second], **filter_options)
         end
-      }
-      it { should equal_to_dom(
-        %(<select class="category enum_filter" name="report[category]" id="report_category">
+      end
+      it {
+        should equal_to_dom(
+          %(<select class="category enum_filter" name="report[category]" id="report_category">
        <option value="" label=" "></option>
        <option value="first">first</option>
        <option value="second">second</option></select>)
-      )}
+        )
+      }
 
       context "when block is given" do
-        let(:_filter_block ) do
+        let(:_filter_block) do
           proc do
-            template.content_tag(:option, 'block option', :value => 'block_value')
+            template.content_tag(:option, "block option", value: "block_value")
           end
         end
-        it { should equal_to_dom(
-          %(<select class="category enum_filter" name="report[category]" id="report_category">
+        it {
+          should equal_to_dom(
+            %(<select class="category enum_filter" name="report[category]" id="report_category">
           <option value="" label=" "></option>
           <option value="block_value">block option</option></select>)
-        )}
+          )
+        }
       end
       context "when first option is selected" do
         before(:each) do
           _grid.category = "first"
         end
-        it { should equal_to_dom(
-          %(<select class="category enum_filter" name="report[category]" id="report_category">
+        it {
+          should equal_to_dom(
+            %(<select class="category enum_filter" name="report[category]" id="report_category">
        <option value="" label=" "></option>
        <option selected value="first">first</option>
        <option value="second">second</option></select>)
-        )}
+          )
+        }
       end
       context "with include_blank option set to false" do
         let(:_category_filter_options) { { include_blank: false } }
-        it { should equal_to_dom(
-          '<select class="category enum_filter" name="report[category]" id="report_category">
+        it {
+          should equal_to_dom(
+            '<select class="category enum_filter" name="report[category]" id="report_category">
          <option value="first">first</option>
          <option value="second">second</option></select>'
-        )}
+          )
+        }
       end
       context "with dynamic include_blank option" do
-        let(:_category_filter_options) { {include_blank: proc { "Choose plz" }} }
-        it { should equal_to_dom(
-          '<select class="category enum_filter" name="report[category]" id="report_category">
+        let(:_category_filter_options) { { include_blank: proc { "Choose plz" } } }
+        it {
+          should equal_to_dom(
+            '<select class="category enum_filter" name="report[category]" id="report_category">
          <option value="">Choose plz</option>
          <option value="first">first</option>
          <option value="second">second</option></select>'
-        )}
+          )
+        }
       end
 
       context "with prompt option" do
-        let(:_category_filter_options) { {prompt: 'My Prompt'} }
-        it { should equal_to_dom(
-          '<select class="category enum_filter" name="report[category]" id="report_category"><option value="">My Prompt</option>
+        let(:_category_filter_options) { { prompt: "My Prompt" } }
+        it {
+          should equal_to_dom(
+            '<select class="category enum_filter" name="report[category]" id="report_category"><option value="">My Prompt</option>
          <option value="first">first</option>
          <option value="second">second</option></select>'
-        )}
+          )
+        }
       end
 
       context "with input_options class" do
-        let(:_category_filter_options) { {input_options: {class: 'custom-class'}} }
-        it { should equal_to_dom(
-          '<select class="custom-class category enum_filter" name="report[category]" id="report_category"><option value="" label=" "></option>
+        let(:_category_filter_options) { { input_options: { class: "custom-class" } } }
+        it {
+          should equal_to_dom(
+            '<select class="custom-class category enum_filter" name="report[category]" id="report_category"><option value="" label=" "></option>
          <option value="first">first</option>
          <option value="second">second</option></select>'
-        )}
+          )
+        }
       end
       context "with checkboxes option" do
-        let(:_category_filter_options) { {checkboxes: true} }
-        it { should equal_to_dom(
-          '
+        let(:_category_filter_options) { { checkboxes: true } }
+        it {
+          should equal_to_dom(
+            '
 <label class="category enum_filter checkboxes" for="report_category_first"><input class="category enum_filter checkboxes" type="checkbox" id="report_category_first" value="first" name="report[category][]" />first</label>
 <label class="category enum_filter checkboxes" for="report_category_second"><input class="category enum_filter checkboxes" type="checkbox" id="report_category_second" value="second" name="report[category][]" />second</label>
           '
-        )}
+          )
+        }
 
         context "when partials option passed and partial exists" do
-          let(:view_options) { {partials: 'custom_checkboxes'} }
-          it { should equal_to_dom('custom_enum_checkboxes') }
+          let(:view_options) { { partials: "custom_checkboxes" } }
+          it { should equal_to_dom("custom_enum_checkboxes") }
         end
       end
     end
@@ -388,96 +442,104 @@ class MyTemplate
       let(:_filter) { :disabled }
       let(:_grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(:disabled, :boolean, default: true)
         end
       end
-      it { should equal_to_dom(
-        # hidden is important when default is set to true
-        %{<input name="report[disabled]" type="hidden" value="0" autocomplete="off"><input class="disabled boolean_filter" type="checkbox" value="1" checked name="report[disabled]" id="report_disabled">}
-      )}
+      it {
+        should equal_to_dom(
+          # hidden is important when default is set to true
+          %(<input name="report[disabled]" type="hidden" value="0" autocomplete="off"><input class="disabled boolean_filter" type="checkbox" value="1" checked name="report[disabled]" id="report_disabled">)
+        )
+      }
     end
     context "with xboolean filter type" do
       let(:_filter) { :disabled }
       let(:_grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(:disabled, :xboolean)
         end
       end
-      it { should equal_to_dom(
-        %(<select class="disabled extended_boolean_filter" name="report[disabled]" id="report_disabled">
+      it {
+        should equal_to_dom(
+          %(<select class="disabled extended_boolean_filter" name="report[disabled]" id="report_disabled">
           <option value="" label=" "></option>
           <option value="YES">Yes</option>
           <option value="NO">No</option></select>)
-      )}
+        )
+      }
     end
     context "with string filter" do
       let(:_grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(:name, :string)
         end
       end
 
       let(:_filter) { :name }
 
-      it {should equal_to_dom('<input class="name string_filter" type="text" name="report[name]" id="report_name">')}
+      it { should equal_to_dom('<input class="name string_filter" type="text" name="report[name]" id="report_name">') }
 
       context "when multiple option is set" do
         let(:_grid) do
-          test_report(:name => "one,two") do
-            scope {Entry}
-            filter(:name, :string, :multiple => true)
+          test_report(name: "one,two") do
+            scope { Entry }
+            filter(:name, :string, multiple: true)
           end
         end
 
         let(:_filter) { :name }
 
-        it {should equal_to_dom('<input value="one,two" class="name string_filter" type="text" name="report[name]" id="report_name">')}
+        it {
+          should equal_to_dom('<input value="one,two" class="name string_filter" type="text" name="report[name]" id="report_name">')
+        }
       end
     end
 
     context "with non multiple filter" do
       let(:_grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(
             :name, :enum,
-            :include_blank => false,
-            :multiple => false,
-            :select => []
+            include_blank: false,
+            multiple: false,
+            select: []
           )
         end
       end
       let(:_filter) { :name }
-      it {should equal_to_dom('<select class="name enum_filter" name="report[name]" id="report_name"></select>')}
+      it { should equal_to_dom('<select class="name enum_filter" name="report[name]" id="report_name"></select>') }
     end
     context "with float filter type" do
-      let(:_grid) {
+      let(:_grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(:group_id, :float)
         end
-      }
+      end
       let(:_filter) { :group_id }
-      it { should equal_to_dom(
-        '<input class="group_id float_filter" type="text" name="report[group_id]" id="report_group_id"/>'
-      )}
+      it {
+        should equal_to_dom(
+          '<input class="group_id float_filter" type="text" name="report[group_id]" id="report_group_id"/>'
+        )
+      }
     end
 
     context "with enum multiple filter" do
       let(:_grid) do
         test_report do
-          scope {Entry}
-          filter(:group_id, :enum, :select => ['hello'], :multiple => true)
+          scope { Entry }
+          filter(:group_id, :enum, select: ["hello"], multiple: true)
         end
       end
       let(:_filter) { :group_id }
       let(:expected_html) do
-        <<-HTML
-<select multiple class="group_id enum_filter" name="report[group_id][]" id="report_group_id">
-<option value="hello">hello</option></select>
+        <<~HTML
+          <select multiple class="group_id enum_filter" name="report[group_id][]" id="report_group_id">
+          <option value="hello">hello</option></select>
         HTML
       end
 
@@ -486,8 +548,8 @@ class MyTemplate
 
     context "with column names filter" do
       let(:_grid) do
-        test_report(:column_names => [:id, :name]) do
-          scope {Entry}
+        test_report(column_names: %i[id name]) do
+          scope { Entry }
 
           column_names_filter
 
@@ -498,10 +560,10 @@ class MyTemplate
       end
       let(:_filter) { :column_names }
       let(:expected_html) do
-        <<-HTML
-<select multiple class="column_names enum_filter" name="report[column_names][]" id="report_column_names"><option selected value="id">Id</option>
-<option selected value="name">Name</option>
-<option value="category">Category</option></select>
+        <<~HTML
+          <select multiple class="column_names enum_filter" name="report[column_names][]" id="report_column_names"><option selected value="id">Id</option>
+          <option selected value="name">Name</option>
+          <option value="category">Category</option></select>
         HTML
       end
 
@@ -509,10 +571,10 @@ class MyTemplate
     end
     context "with column_names_filter default given as symbols" do
       let(:_grid) do
-        test_report() do
-          scope {Entry}
+        test_report do
+          scope { Entry }
 
-          column_names_filter(:default => [:id, :name], :checkboxes => true)
+          column_names_filter(default: %i[id name], checkboxes: true)
 
           column(:id)
           column(:name)
@@ -521,11 +583,11 @@ class MyTemplate
       end
       let(:_filter) { :column_names }
       let(:expected_html) do
-        <<DOM
-<label class="column_names enum_filter checkboxes" for="report_column_names_id"><input class="column_names enum_filter checkboxes" id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">Id</label>
-<label class="column_names enum_filter checkboxes" for="report_column_names_name"><input class="column_names enum_filter checkboxes" id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]">Name</label>
-<label class="column_names enum_filter checkboxes" for="report_column_names_category"><input class="column_names enum_filter checkboxes" id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">Category</label>
-DOM
+        <<~DOM
+          <label class="column_names enum_filter checkboxes" for="report_column_names_id"><input class="column_names enum_filter checkboxes" id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">Id</label>
+          <label class="column_names enum_filter checkboxes" for="report_column_names_name"><input class="column_names enum_filter checkboxes" id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]">Name</label>
+          <label class="column_names enum_filter checkboxes" for="report_column_names_category"><input class="column_names enum_filter checkboxes" id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">Category</label>
+        DOM
       end
 
       it do
@@ -541,7 +603,7 @@ class MyTemplate
       let(:_grid) do
         options = filter_options
         test_report do
-          scope {Entry}
+          scope { Entry }
           filter(:condition, :dynamic, **options)
         end
       end
@@ -565,12 +627,11 @@ class MyTemplate
          <option value="&lt;=">&le;</option></select><input class="condition dynamic_filter value"  name="report[condition][]" type="text" id="report_condition">
           HTML
         end
-        it {should equal_to_dom(expected_html)}
-
+        it { should equal_to_dom(expected_html) }
       end
       context "when default option passed" do
         let(:filter_options) do
-          {:select => [:id, :name], :default => [:id, '>=', 1]}
+          { select: %i[id name], default: [:id, ">=", 1] }
         end
         let(:expected_html) do
           <<-HTML
@@ -581,13 +642,12 @@ class MyTemplate
        <option value="&lt;=">&le;</option></select><input class="condition dynamic_filter value" name="report[condition][]" value="1" type="text"  id="report_condition">
           HTML
         end
-        it {should equal_to_dom(expected_html)}
-
+        it { should equal_to_dom(expected_html) }
       end
 
       context "when operations and options are defined" do
         let(:filter_options) do
-          {:operations => %w(>= <=), :select => [:id, :name]}
+          { operations: %w[>= <=], select: %i[id name] }
         end
         let(:expected_html) do
           <<-HTML
@@ -595,12 +655,12 @@ class MyTemplate
        <option value="&lt;=">≤</option></select><input class="condition dynamic_filter value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
-        it {should equal_to_dom(expected_html)}
+        it { should equal_to_dom(expected_html) }
       end
 
       context "when the field is predefined" do
         let(:filter_options) do
-          {:operations => %w(>= <=), :select => [:id]}
+          { operations: %w[>= <=], select: [:id] }
         end
         let(:expected_html) do
           <<-HTML
@@ -608,30 +668,28 @@ class MyTemplate
        <option value="&lt;=">≤</option></select><input class="condition dynamic_filter value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
-        it {should equal_to_dom(expected_html)}
+        it { should equal_to_dom(expected_html) }
       end
       context "when operation is predefined" do
         let(:filter_options) do
-          {:operations => %w(=), :select => [:id, :name]}
+          { operations: %w[=], select: %i[id name] }
         end
         let(:expected_html) do
           <<-HTML
           <select class="condition dynamic_filter field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><input class="condition dynamic_filter operation" name="report[condition][]" value="=" autocomplete="off" type="hidden" id="report_condition"><input class="condition dynamic_filter value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
-        it {should equal_to_dom(expected_html)}
+        it { should equal_to_dom(expected_html) }
       end
-
     end
   end
 
-
   describe ".datagrid_label" do
     let(:_grid) do
       test_report do
-        scope {Entry}
+        scope { Entry }
         filter(:name, :string)
-        filter(:created_at, :date, label_options: {class: 'js-date-selector'})
+        filter(:created_at, :date, label_options: { class: "js-date-selector" })
       end
     end
     it "should generate label for filter" do
@@ -645,12 +703,12 @@ class MyTemplate
       )
     end
     it "should pass options through to the helper" do
-      expect(view.datagrid_label(:name, class: 'foo')).to equal_to_dom(
+      expect(view.datagrid_label(:name, class: "foo")).to equal_to_dom(
         '<label class="foo name string_filter" for="report_name">Name</label>'
       )
     end
     it "should support block" do
-      expect(view.datagrid_label(:name, class: 'foo') { 'The Name' }).to equal_to_dom(
+      expect(view.datagrid_label(:name, class: "foo") { "The Name" }).to equal_to_dom(
         '<label class="foo name string_filter" for="report_name">The Name</label>'
       )
     end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 2ec195f..adc8efa 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -1,9 +1,9 @@
-require 'spec_helper'
+require "spec_helper"
 require "active_support/core_ext/hash"
 require "active_support/core_ext/object"
 require "action_controller"
 
-require 'datagrid/renderer'
+require "datagrid/renderer"
 
 describe Datagrid::Helper do
   subject do
@@ -16,15 +16,16 @@
       Struct.new(:path, :query_parameters).new("/location", {})
     end
     allow(subject).to receive(:url_for) do |options|
-      options.is_a?(String) ? options : ["/location", options.to_param.presence].compact.join('?')
+      options.is_a?(String) ? options : ["/location", options.to_param.presence].compact.join("?")
     end
-
   end
 
-  let(:group) { Group.create!(:name => "Pop") }
-  let!(:entry) {  Entry.create!(
-    group: group, name: "Star", disabled: false, confirmed: false, category: "first"
-  ) }
+  let(:group) { Group.create!(name: "Pop") }
+  let!(:entry) do
+    Entry.create!(
+      group: group, name: "Star", disabled: false, confirmed: false, category: "first"
+    )
+  end
   let(:grid) { SimpleReport.new }
 
   context "when grid has no records" do
@@ -67,13 +68,13 @@ class TestGrid
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern({
-        "table.datagrid tr th.group div.order" => 1,
-        "table.datagrid tr th.group" => /Group.*/,
-        "table.datagrid tr th.name div.order" => 1,
-        "table.datagrid tr th.name" => /Name.*/,
-        "table.datagrid tr td.group" => "Pop",
-        "table.datagrid tr td.name" => "Star"
-      })
+                                                    "table.datagrid tr th.group div.order" => 1,
+                                                    "table.datagrid tr th.group" => /Group.*/,
+                                                    "table.datagrid tr th.name div.order" => 1,
+                                                    "table.datagrid tr th.name" => /Name.*/,
+                                                    "table.datagrid tr td.group" => "Pop",
+                                                    "table.datagrid tr td.name" => "Star"
+                                                  })
     end
 
     it "should support giving assets explicitly" do
@@ -81,13 +82,13 @@ class TestGrid
       datagrid_table = subject.datagrid_table(grid, [entry])
 
       expect(datagrid_table).to match_css_pattern({
-        "table.datagrid tr th.group div.order" => 1,
-        "table.datagrid tr th.group" => /Group.*/,
-        "table.datagrid tr th.name div.order" => 1,
-        "table.datagrid tr th.name" => /Name.*/,
-        "table.datagrid tr td.group" => "Pop",
-        "table.datagrid tr td.name" => "Star"
-      })
+                                                    "table.datagrid tr th.group div.order" => 1,
+                                                    "table.datagrid tr th.group" => /Group.*/,
+                                                    "table.datagrid tr th.name div.order" => 1,
+                                                    "table.datagrid tr th.name" => /Name.*/,
+                                                    "table.datagrid tr td.group" => "Pop",
+                                                    "table.datagrid tr td.name" => "Star"
+                                                  })
     end
 
     it "should support no order given" do
@@ -95,7 +96,7 @@ class TestGrid
     end
 
     it "should support columns option" do
-      expect(subject.datagrid_table(grid, [entry], :columns => [:name])).to match_css_pattern(
+      expect(subject.datagrid_table(grid, [entry], columns: [:name])).to match_css_pattern(
         "table.datagrid th.name" => 1,
         "table.datagrid td.name" => 1,
         "table.datagrid th.group" => 0,
@@ -125,7 +126,7 @@ class TestGrid
     context "when grid has no columns" do
       let(:grid) do
         test_report do
-          scope {Entry}
+          scope { Entry }
         end
       end
 
@@ -134,7 +135,7 @@ class TestGrid
       end
     end
 
-    context 'with partials attribute' do
+    context "with partials attribute" do
       let(:grid) do
         test_report do
           scope { Entry }
@@ -143,21 +144,21 @@ class TestGrid
         end
       end
 
-      it 'renders namespaced table partial' do
+      it "renders namespaced table partial" do
         rendered_partial = subject.datagrid_table(
-          grid, [entry], partials: 'client/datagrid'
+          grid, [entry], partials: "client/datagrid"
         )
-        expect(rendered_partial).to include 'Namespaced table partial.'
-        expect(rendered_partial).to include 'Namespaced row partial.'
-        expect(rendered_partial).to include 'Namespaced head partial.'
-        expect(rendered_partial).to include 'Namespaced order_for partial.'
+        expect(rendered_partial).to include "Namespaced table partial."
+        expect(rendered_partial).to include "Namespaced row partial."
+        expect(rendered_partial).to include "Namespaced head partial."
+        expect(rendered_partial).to include "Namespaced order_for partial."
       end
     end
 
     context "when scope is enumerator" do
       let(:grid) do
         test_report do
-          scope { ['a', 'b'].to_enum }
+          scope { %w[a b].to_enum }
           column(:name) do |value|
             value
           end
@@ -166,14 +167,14 @@ class TestGrid
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
           "table.datagrid th.name" => 1,
-          "table.datagrid td.name" => 2,
+          "table.datagrid td.name" => 2
         )
       end
     end
     context "when scope is lazy enumerator" do
       let(:grid) do
         test_report do
-          scope { ['a', 'b'].lazy }
+          scope { %w[a b].lazy }
           column(:name) do |value|
             value
           end
@@ -182,7 +183,7 @@ class TestGrid
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
           "table.datagrid th.name" => 1,
-          "table.datagrid td.name" => 2,
+          "table.datagrid td.name" => 2
         )
       end
     end
@@ -192,7 +193,7 @@ class TestGrid
     it "should support urls" do
       rp = test_report do
         scope { Entry }
-        column(:name, url: lambda {|model| model.name})
+        column(:name, url: ->(model) { model.name })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
         "tr td.name a[href=Star]" => "Star"
@@ -202,7 +203,7 @@ class TestGrid
     it "should support conditional urls" do
       rp = test_report do
         scope { Entry }
-        column(:name, url: lambda {|model| false})
+        column(:name, url: ->(_model) { false })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
         "tr td.name" => "Star"
@@ -292,7 +293,7 @@ class TestGrid
     it "should render :html columns with &:symbol block with a data attribute" do
       rp = test_report do
         scope { Entry }
-        column(:name, html: true, data: 'DATA', &:name)
+        column(:name, html: true, data: "DATA", &:name)
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
@@ -303,7 +304,7 @@ class TestGrid
     it "should render argument-based html columns" do
       rp = test_report do
         scope { Entry }
-        column(:name, html: lambda {|data| content_tag :h1, data})
+        column(:name, html: ->(data) { content_tag :h1, data })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
         "tr td.name h1" => "Star"
@@ -313,8 +314,8 @@ class TestGrid
     it "should render argument-based html columns with custom data" do
       rp = test_report do
         scope { Entry }
-        column(:name, html: lambda {|data| content_tag :em, data}) do
-          self.name.upcase
+        column(:name, html: ->(data) { content_tag :em, data }) do
+          name.upcase
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
@@ -326,7 +327,7 @@ class TestGrid
       rp = test_report do
         scope { Entry }
         column(:name, html: true) do |model, grid|
-          content_tag(:span, "#{model.name}-#{grid.assets.klass}" )
+          content_tag(:span, "#{model.name}-#{grid.assets.klass}")
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
@@ -364,7 +365,7 @@ class TestGrid
         column(:name, html: lambda { |data, model|
           content_tag :h1, "#{data}-#{model.name}"
         }) do
-          self.name.upcase
+          name.upcase
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
@@ -378,7 +379,7 @@ class TestGrid
         column(:name, html: lambda { |data, model, grid|
           content_tag :h1, "#{data}-#{model.name}-#{grid.assets.klass}"
         }) do
-          self.name.upcase
+          name.upcase
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
@@ -392,10 +393,10 @@ class TestGrid
         column(:name)
         column(:category)
       end
-      expect(subject.datagrid_rows(rp, [entry], :columns => [:name])).to match_css_pattern(
+      expect(subject.datagrid_rows(rp, [entry], columns: [:name])).to match_css_pattern(
         "tr td.name" => "Star"
       )
-      expect(subject.datagrid_rows(rp, [entry], :columns => [:name])).to match_css_pattern(
+      expect(subject.datagrid_rows(rp, [entry], columns: [:name])).to match_css_pattern(
         "tr td.category" => 0
       )
     end
@@ -403,7 +404,7 @@ class TestGrid
     it "should allow CSS classes to be specified for a column" do
       rp = test_report do
         scope { Entry }
-        column(:name, :class => 'my_class')
+        column(:name, class: "my_class")
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
@@ -413,8 +414,8 @@ class TestGrid
 
     context "when grid has complicated columns" do
       let(:grid) do
-        test_report(:name => 'Hello') do
-          scope {Entry}
+        test_report(name: "Hello") do
+          scope { Entry }
           filter(:name)
           column(:name) do |model, grid|
             "'#{model.name}' filtered by '#{grid.name}'"
@@ -429,23 +430,21 @@ class TestGrid
     end
 
     it "should escape html" do
-      entry.update!(:name => "<div>hello</div>")
-      expect(subject.datagrid_rows(grid, [entry], :columns => [:name])).to equal_to_dom(<<-HTML)
+      entry.update!(name: "<div>hello</div>")
+      expect(subject.datagrid_rows(grid, [entry], columns: [:name])).to equal_to_dom(<<-HTML)
        <tr><td class="name">&lt;div&gt;hello&lt;/div&gt;</td></tr>
-        HTML
+      HTML
     end
 
     it "should not escape safe html" do
-      entry.update!(:name => "<div>hello</div>")
+      entry.update!(name: "<div>hello</div>")
       grid.column(:safe_name) do |model|
         model.name.html_safe
       end
-      expect(subject.datagrid_rows(grid, [entry], :columns => [:safe_name])).to equal_to_dom(<<-HTML)
+      expect(subject.datagrid_rows(grid, [entry], columns: [:safe_name])).to equal_to_dom(<<-HTML)
        <tr><td class="safe_name"><div>hello</div></td></tr>
-        HTML
-
+      HTML
     end
-
   end
 
   describe ".datagrid_order_for" do
@@ -456,22 +455,21 @@ class OrderedGrid
         column(:category)
       end
       object = OrderedGrid.new(descending: true, order: :category)
-      expect(subject.datagrid_order_for(object, object.column_by_name(:category))).to equal_to_dom(<<-HTML)
-<div class="order">
-<a class="asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
-<a class="desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">&darr;</a>
-</div>
+      expect(subject.datagrid_order_for(object, object.column_by_name(:category))).to equal_to_dom(<<~HTML)
+        <div class="order">
+        <a class="asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
+        <a class="desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">&darr;</a>
+        </div>
       HTML
     end
-
   end
   describe ".datagrid_form_for" do
-    it 'returns namespaced partial if partials options is passed' do
+    it "returns namespaced partial if partials options is passed" do
       rendered_form = subject.datagrid_form_for(grid, {
-        url: '',
-        :partials => 'client/datagrid'
-      })
-      expect(rendered_form).to include 'Namespaced form partial.'
+                                                  url: "",
+                                                  partials: "client/datagrid"
+                                                })
+      expect(rendered_form).to include "Namespaced form partial."
     end
     it "should render form and filter inputs" do
       class FormForGrid
@@ -479,7 +477,7 @@ class FormForGrid
         scope { Entry }
         filter(:category)
       end
-      object = FormForGrid.new(:category => "hello")
+      object = FormForGrid.new(category: "hello")
       expect(subject.datagrid_form_for(object, url: "/grid")).to match_css_pattern(
         "form.datagrid-form.form_for_grid[action='/grid']" => 1,
         "form input[name=utf8]" => 1,
@@ -500,7 +498,7 @@ class TestGrid
       expect(subject.datagrid_form_for(::Ns22::TestGrid.new, url: "grid")).to match_css_pattern(
         "form.datagrid-form.ns22_test_grid" => 1,
         "form.datagrid-form label[for=ns22_test_grid_id]" => 1,
-        "form.datagrid-form input#ns22_test_grid_id[name='ns22_test_grid[id]']" => 1,
+        "form.datagrid-form input#ns22_test_grid_id[name='ns22_test_grid[id]']" => 1
       )
     end
 
@@ -510,39 +508,38 @@ class ParamNameGrid81
         scope { Entry }
         filter(:id)
         def param_name
-          'g'
+          "g"
         end
       end
       expect(subject.datagrid_form_for(::ParamNameGrid81.new, url: "/grid")).to match_css_pattern(
-        "form.datagrid-form input[name='g[id]']" => 1,
+        "form.datagrid-form input[name='g[id]']" => 1
       )
     end
 
     it "takes default partials if custom doesn't exist" do
       class PartialDefaultGrid
         include Datagrid
-        scope {Entry}
-        filter(:id, :integer, :range => true)
-        filter(:group_id, :enum, :multiple => true, :checkboxes => true, :select => [1,2])
+        scope { Entry }
+        filter(:id, :integer, range: true)
+        filter(:group_id, :enum, multiple: true, checkboxes: true, select: [1, 2])
         def param_name
-          'g'
+          "g"
         end
       end
       rendered_form = subject.datagrid_form_for(PartialDefaultGrid.new, {
-        url: '',
-        :partials => 'custom_form'
-      })
-      expect(rendered_form).to include 'form_partial_test'
+                                                  url: "",
+                                                  partials: "custom_form"
+                                                })
+      expect(rendered_form).to include "form_partial_test"
       expect(rendered_form).to match_css_pattern([
-        'input.integer_filter.from',
-        'input.integer_filter.to',
-        ".enum_filter input[value='1']",
-        ".enum_filter input[value='2']",
-      ])
+                                                   "input.integer_filter.from",
+                                                   "input.integer_filter.to",
+                                                   ".enum_filter input[value='1']",
+                                                   ".enum_filter input[value='2']"
+                                                 ])
     end
   end
 
-
   describe ".datagrid_row" do
     let(:grid) do
       test_report do
@@ -553,7 +550,7 @@ def param_name
     end
 
     let(:entry) do
-      Entry.create!(:name => "Hello", :category => "greetings")
+      Entry.create!(name: "Hello", category: "greetings")
     end
 
     it "should provide access to row data" do
@@ -563,7 +560,7 @@ def param_name
     end
     it "should provide an interator" do
       r = subject.datagrid_row(grid, entry)
-      expect(r.map {|z| z.upcase}).to eq(["HELLO", "GREETINGS"])
+      expect(r.map { |z| z.upcase }).to eq(%w[HELLO GREETINGS])
       expect(r.name).to eq("Hello")
       expect(r.category).to eq("greetings")
     end
@@ -592,10 +589,10 @@ def param_name
 
     it "should use cache" do
       grid = test_report do
-        scope {Entry}
+        scope { Entry }
         self.cached = true
-        column(:random1, html: true) {rand(10**9)}
-        column(:random2) {|model| format(rand(10**9)) {|value| value}}
+        column(:random1, html: true) { rand(10**9) }
+        column(:random2) { |_model| format(rand(10**9)) { |value| value } }
       end
 
       entry = Entry.create!
@@ -615,15 +612,15 @@ def param_name
 
     it "converts to string using columns option" do
       r = subject.datagrid_row(grid, entry, columns: [:name]).to_s
-      expect(r).to match_css_pattern('tr td.name')
-      expect(r).to_not match_css_pattern('tr td.category')
+      expect(r).to match_css_pattern("tr td.name")
+      expect(r).to_not match_css_pattern("tr td.category")
     end
   end
 
   describe ".datagrid_value" do
     it "should format value by column name" do
       report = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:name) do |e|
           "<b>#{e.name}</b>"
         end
@@ -633,21 +630,21 @@ def param_name
     end
     it "should support format in column" do
       report = test_report do
-        scope {Entry}
+        scope { Entry }
         column(:name) do |e|
           format(e.name) do |value|
-             link_to value, "/profile"
+            link_to value, "/profile"
           end
         end
       end
       expect(subject.datagrid_value(report, :name, entry)).to be_html_safe
-      expect(subject.datagrid_value(report, :name, entry)).to eq("<a href=\"/profile\">Star</a>")
+      expect(subject.datagrid_value(report, :name, entry)).to eq('<a href="/profile">Star</a>')
     end
 
     it "applies decorator" do
       report = test_report do
-        scope {Entry}
-        decorate do |model|
+        scope { Entry }
+        decorate do |_model|
           Class.new(Struct.new(:model)) do
             def name
               model.name.upcase
@@ -656,7 +653,7 @@ def name
         end
         column(:name, html: true)
       end
-      entry = Entry.create!(name: 'hello')
+      entry = Entry.create!(name: "hello")
       expect(subject.datagrid_value(report, :name, entry)).to eq("HELLO")
     end
   end
@@ -668,17 +665,15 @@ def name
         column(:category, order: false, order_by_value: true)
 
         def param_name
-          'grid'
+          "grid"
         end
       end
-      expect(subject.datagrid_header(grid)).to equal_to_dom(<<HTML)
-<tr><th class="category ordered asc">Category<div class="order">
-<a class="asc" href="/location?grid%5Bdescending%5D=false&amp;grid%5Border%5D=category">&uarr;</a><a class="desc" href="/location?grid%5Bdescending%5D=true&amp;grid%5Border%5D=category">&darr;</a>
-</div>
-</th></tr>
-HTML
+      expect(subject.datagrid_header(grid)).to equal_to_dom(<<~HTML)
+        <tr><th class="category ordered asc">Category<div class="order">
+        <a class="asc" href="/location?grid%5Bdescending%5D=false&amp;grid%5Border%5D=category">&uarr;</a><a class="desc" href="/location?grid%5Bdescending%5D=true&amp;grid%5Border%5D=category">&darr;</a>
+        </div>
+        </th></tr>
+      HTML
     end
   end
-
 end
-
diff --git a/spec/datagrid/ordering_spec.rb b/spec/datagrid/ordering_spec.rb
index e688f42..701e6b0 100644
--- a/spec/datagrid/ordering_spec.rb
+++ b/spec/datagrid/ordering_spec.rb
@@ -1,12 +1,9 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Ordering do
-
-
-  let!(:third) { Entry.create!(name: "cc")}
-  let!(:second) { Entry.create!(name: "bb")}
-  let!(:first) { Entry.create!(name: "aa")}
-
+  let!(:third) { Entry.create!(name: "cc") }
+  let!(:second) { Entry.create!(name: "bb") }
+  let!(:first) { Entry.create!(name: "aa") }
 
   it "should support order" do
     expect(test_report(order: "name") do
@@ -15,7 +12,6 @@
       end
       column :name
     end.assets).to eq([first, second, third])
-
   end
 
   it "should support desc order" do
@@ -27,17 +23,16 @@
     end.assets).to eq([third, second, first])
   end
 
-
   it "should raise error if ordered by not existing column" do
-    expect {
+    expect do
       test_report(order: :hello).assets
-    }.to raise_error(Datagrid::OrderUnsupported)
+    end.to raise_error(Datagrid::OrderUnsupported)
   end
 
   it "should raise error if ordered by column without order" do
     expect do
       test_report(order: :category) do
-        filter(:category, :default, order: false) do |value|
+        filter(:category, :default, order: false) do |_value|
           self
         end
       end.assets
@@ -46,7 +41,7 @@
 
   it "should override default order" do
     expect(test_report(order: :name) do
-      scope { Entry.order("name desc")}
+      scope { Entry.order("name desc") }
       column(:name, order: "name asc")
     end.assets).to eq([first, second, third])
   end
@@ -68,20 +63,20 @@
   it "should support order desc given as block" do
     expect(test_report(order: :name, descending: true) do
       scope { Entry }
-      column(:name,  order_desc: proc { order("name desc")})
+      column(:name, order_desc: proc { order("name desc") })
     end.assets).to eq([third, second, first])
   end
 
   it "should treat true order as default" do
     expect(test_report(order: :name) do
       scope { Entry }
-      column(:name,  order: true)
+      column(:name, order: true)
     end.assets).to eq([first, second, third])
   end
 
   it "should support order_by_value" do
     report = test_report(order: :the_name) do
-      scope {Entry}
+      scope { Entry }
       column(:the_name, order_by_value: true) do
         name
       end
@@ -92,12 +87,10 @@
   end
 
   it "should support order_by_value as block" do
-
-    order = { aa: 2, bb: 3, cc: 1}
+    order = { aa: 2, bb: 3, cc: 1 }
     report = test_report(order: :the_name) do
-
-      scope {Entry}
-      column(:the_name, order_by_value: proc{|model| order[model.name.to_sym]}) do
+      scope { Entry }
+      column(:the_name, order_by_value: proc { |model| order[model.name.to_sym] }) do
         name
       end
     end
@@ -116,27 +109,25 @@ class OrderInheritenceChild < OrderInheritenceBase
       column(:name)
     end
 
-    grid = OrderInheritenceChild.new(order: 'name')
+    grid = OrderInheritenceChild.new(order: "name")
     expect(grid.assets).to eq([first, second, third])
     grid.descending = true
     expect(grid.assets).to eq([third, second, first])
   end
   it "should support ordering by dynamic columns" do
-
     report = test_report(order: "name") do
-      scope {Entry}
+      scope { Entry }
       dynamic do
         column(:name)
       end
     end
 
     expect(report.assets).to eq([first, second, third])
-
   end
 
   it "should support #ordered_by? method" do
     report = test_report(order: "name") do
-      scope  {Entry}
+      scope  { Entry }
       column(:id)
       column(:name)
     end
diff --git a/spec/datagrid/scaffold_spec.rb b/spec/datagrid/scaffold_spec.rb
index 7ccc455..9bab5fc 100644
--- a/spec/datagrid/scaffold_spec.rb
+++ b/spec/datagrid/scaffold_spec.rb
@@ -1,20 +1,19 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Scaffold do
   subject { Datagrid::Scaffold.new(["user"]) }
 
-
-  describe '.pagination_helper_code' do
-    it 'uses kaminari by default' do
-      expect(subject.pagination_helper_code).to eql('paginate(@grid.assets)')
+  describe ".pagination_helper_code" do
+    it "uses kaminari by default" do
+      expect(subject.pagination_helper_code).to eql("paginate(@grid.assets)")
     end
 
     context "when WillPaginate exists" do
       before(:each) do
         Object.const_set("WillPaginate", 1)
       end
-      it 'uses willpaginate' do
-        expect(subject.pagination_helper_code).to eql('will_paginate(@grid.assets)')
+      it "uses willpaginate" do
+        expect(subject.pagination_helper_code).to eql("will_paginate(@grid.assets)")
       end
 
       after(:each) do
@@ -24,7 +23,6 @@
   end
 
   describe ".index_action" do
-
     it "works" do
       expect(subject.index_action).to eq(<<-RUBY)
   def index
@@ -38,8 +36,7 @@ def index
   def grid_params
     params.fetch(:users_grid, {}).permit!
   end
-RUBY
+      RUBY
     end
-
   end
 end
diff --git a/spec/datagrid/stylesheet_spec.rb b/spec/datagrid/stylesheet_spec.rb
index 0404561..0fc65ce 100644
--- a/spec/datagrid/stylesheet_spec.rb
+++ b/spec/datagrid/stylesheet_spec.rb
@@ -1,9 +1,7 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe "Datagrid stylesheet" do
-
   it "should work correctly" do
-
     if Rails.application.assets.respond_to?(:find_asset)
       asset = Rails.application.assets.find_asset("datagrid")
       asset
diff --git a/spec/datagrid/utils_spec.rb b/spec/datagrid/utils_spec.rb
index 89775cd..f66a495 100644
--- a/spec/datagrid/utils_spec.rb
+++ b/spec/datagrid/utils_spec.rb
@@ -1,8 +1,6 @@
-require 'spec_helper'
+require "spec_helper"
 
 describe Datagrid::Utils do
-  
-
   describe ".warn_once" do
     it "should work" do
       silence_warnings do
diff --git a/spec/datagrid_spec.rb b/spec/datagrid_spec.rb
index 6b3ecc5..3b04da1 100644
--- a/spec/datagrid_spec.rb
+++ b/spec/datagrid_spec.rb
@@ -1,11 +1,9 @@
-require 'spec_helper'
+require "spec_helper"
 require "datagrid/rspec"
 
-
 describe Datagrid do
-
   describe SimpleReport do
-    it_should_behave_like 'Datagrid'
+    it_should_behave_like "Datagrid"
   end
 
   let(:group) { Group.create!(name: "Pop") }
@@ -20,22 +18,23 @@
     )
   end
 
-  let!(:entry) {  Entry.create!(
-    group: group, name: "Star", disabled: false, confirmed: false, category: "first"
-  ) }
+  let!(:entry) do
+    Entry.create!(
+      group: group, name: "Star", disabled: false, confirmed: false, category: "first"
+    )
+  end
 
-  describe '#assets' do
+  describe "#assets" do
     subject { super().assets }
     it { should include(entry) }
   end
 
   describe ".attributes" do
     it "should return report attributes" do
-      (subject.filters.map(&:name) + [:order, :descending]).each do |attribute|
+      (subject.filters.map(&:name) + %i[order descending]).each do |attribute|
         expect(subject.attributes).to have_key(attribute)
       end
     end
-
   end
 
   describe ".scope" do
@@ -43,17 +42,15 @@
       expect(subject.scope).to respond_to(:each)
     end
 
-
     context "when not defined on class level" do
       subject do
         test_report {}
       end
 
       it "should raise ConfigurationError" do
-        expect {
+        expect do
           subject.scope
-        }.to raise_error(Datagrid::ConfigurationError)
-
+        end.to raise_error(Datagrid::ConfigurationError)
       end
     end
   end
@@ -89,6 +86,4 @@
       end
     end
   end
-
-
 end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index a555bb8..995e21f 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,77 +1,71 @@
-require 'rubygems'
-require 'bundler'
+require "rubygems"
+require "bundler"
 
 begin
   Bundler.setup(:default, :development)
 rescue Bundler::BundlerError => e
-  $stderr.puts e.message
-  $stderr.puts "Run `bundle install` to install missing gems"
+  warn e.message
+  warn "Run `bundle install` to install missing gems"
   exit e.status_code
 end
 
-$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
 $LOAD_PATH.unshift(File.dirname(__FILE__))
 
 require "active_record"
-require 'action_view'
+require "action_view"
 require "rails"
 require "mongoid"
 begin
-  require 'mongo_mapper'
+  require "mongo_mapper"
 rescue LoadError
 end
 
-
-
-require 'datagrid'
+require "datagrid"
 begin
-  require 'ruby-debug'
+  require "ruby-debug"
 rescue LoadError
 end
-require 'rspec'
+require "rspec"
 require "logger"
 
 class DatagridTest < Rails::Application
-
   config.eager_load = false
-
 end
 
-if I18n.respond_to?(:enforce_available_locales)
-  I18n.enforce_available_locales = true
-end
+I18n.enforce_available_locales = true if I18n.respond_to?(:enforce_available_locales)
 
-File.open('spec.log', "w").close
-TEST_LOGGER = Logger.new('spec.log')
-NO_MONGO = ENV['NO_MONGO']
+File.open("spec.log", "w").close
+TEST_LOGGER = Logger.new("spec.log")
+NO_MONGO = ENV["NO_MONGO"]
 
 if NO_MONGO
   warn("MONGODB WARNING: Skipping Mongoid and Mongomapper tests.")
 else
   begin
     Mongoid.load_configuration({
-      "clients" =>
-      {
-        "default" =>
-        {
-          "hosts" => ["localhost:27017"],
-          "database" =>"datagrid_mongoid",
-          options: {
-            max_read_retries: 0,
-            retry_reads: false,
-            connect_timeout: 2,
-            wait_queue_timeout: 2,
-            server_selection_timeout: 2,
-            socket_timeout: 1
-          }
-        }
-      }
-    })
+                                 "clients" =>
+                                 {
+                                   "default" =>
+                                   {
+                                     "hosts" => ["localhost:27017"],
+                                     "database" => "datagrid_mongoid",
+                                     options: {
+                                       max_read_retries: 0,
+                                       retry_reads: false,
+                                       connect_timeout: 2,
+                                       wait_queue_timeout: 2,
+                                       server_selection_timeout: 2,
+                                       socket_timeout: 1
+                                     }
+                                   }
+                                 }
+                               })
 
     Mongoid.client(:default).collections # check mongo connection
 
     if defined?(MongoMapper)
-      MongoMapper.connection = Mongo::Connection.new('localhost', 27017)
+      MongoMapper.connection = Mongo::Connection.new("localhost", 27_017)
       MongoMapper.database = "datagrid_mongo_mapper"
     end
   rescue Mongo::Error::NoServerAvailable => e
@@ -82,7 +76,7 @@ class DatagridTest < Rails::Application
 
 RSpec.configure do |config|
   config.after(:each) do
-    #TODO better database truncation
+    # TODO: better database truncation
     Group.delete_all
     Entry.delete_all
     SequelEntry.where({}).delete
@@ -90,7 +84,6 @@ class DatagridTest < Rails::Application
       MongoidEntry.delete_all
       MongoMapperEntry.delete_all if defined?(MongoMapperEntry)
     end
-
   end
 
   if NO_MONGO
@@ -99,24 +92,22 @@ class DatagridTest < Rails::Application
   end
 
   config.expect_with :rspec do |c|
-    #c.syntax = :expect
-    c.syntax = [:should, :expect]
+    # c.syntax = :expect
+    c.syntax = %i[should expect]
   end
 end
 
 def action_view_template
   context = ActionView::LookupContext.new([
-    File.expand_path("../../app/views", __FILE__),
-    File.expand_path("../support/test_partials", __FILE__),
-  ], {})
+                                            File.expand_path("../app/views", __dir__),
+                                            File.expand_path("support/test_partials", __dir__)
+                                          ], {})
   Datagrid::Engine.extend_modules
   template = ActionView::Base.with_empty_template_cache.new(context, {}, ::ActionController::Base.new)
   allow(template).to receive(:protect_against_forgery?).and_return(false)
   template
 end
 
-
-
 # Requires supporting files with custom matchers and macros, etc,
 # in ./support/ and its subdirectories.
-Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
+Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
diff --git a/spec/support/active_record.rb b/spec/support/active_record.rb
index ed1f32e..3a9a2c4 100644
--- a/spec/support/active_record.rb
+++ b/spec/support/active_record.rb
@@ -1,16 +1,12 @@
 require "sqlite3"
 
 ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
-#ActiveRecord::Base.configurations = true
+# ActiveRecord::Base.configurations = true
 
 ActiveRecord::Base.logger = TEST_LOGGER
 
-
-
-
 ActiveRecord::Schema.verbose = false
 ActiveRecord::Schema.define(version: 1) do
-
   create_table :entries do |t|
     t.integer :group_id
     t.string :name
@@ -32,6 +28,7 @@
   class ::Entry < ActiveRecord::Base
     belongs_to :group
   end
+
   class ::Group < ActiveRecord::Base
     has_many :entries
   end
diff --git a/spec/support/configuration.rb b/spec/support/configuration.rb
index 014c295..07e39a1 100644
--- a/spec/support/configuration.rb
+++ b/spec/support/configuration.rb
@@ -1,28 +1,23 @@
 def with_date_format(format = "%m/%d/%Y")
-  begin
-    old_format = Datagrid.configuration.date_formats
-    Datagrid.configure do |config|
-      config.date_formats = format
-    end
-    yield
-  ensure
-    Datagrid.configure do |config|
-      config.date_formats = old_format
-    end
+  old_format = Datagrid.configuration.date_formats
+  Datagrid.configure do |config|
+    config.date_formats = format
+  end
+  yield
+ensure
+  Datagrid.configure do |config|
+    config.date_formats = old_format
   end
 end
 
 def with_datetime_format(format = "%m/%d/%Y")
-  begin
-    old_format = Datagrid.configuration.datetime_formats
-    Datagrid.configure do |config|
-      config.datetime_formats = format
-    end
-    yield
-  ensure
-    Datagrid.configure do |config|
-      config.datetime_formats = old_format
-    end
+  old_format = Datagrid.configuration.datetime_formats
+  Datagrid.configure do |config|
+    config.datetime_formats = format
+  end
+  yield
+ensure
+  Datagrid.configure do |config|
+    config.datetime_formats = old_format
   end
 end
-
diff --git a/spec/support/i18n_helpers.rb b/spec/support/i18n_helpers.rb
index 280220d..9a52846 100644
--- a/spec/support/i18n_helpers.rb
+++ b/spec/support/i18n_helpers.rb
@@ -1,4 +1,4 @@
-def store_translations(locale, translations, &block)
+def store_translations(locale, translations)
   I18n.backend.store_translations locale, translations
   yield
 ensure
diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb
index 76dd2f1..3a84ce6 100644
--- a/spec/support/matchers.rb
+++ b/spec/support/matchers.rb
@@ -1,4 +1,4 @@
-require 'rails/dom/testing'
+require "rails/dom/testing"
 
 def equal_to_dom(text)
   EqualToDom.new(text)
@@ -8,7 +8,6 @@ def match_css_pattern(pattern)
   CssPattern.new(pattern)
 end
 
-
 class EqualToDom
   include Rails::Dom::Testing::Assertions::DomAssertions
 
@@ -34,15 +33,14 @@ def description
   end
 end
 
-
 class CssPattern
   def initialize(pattern)
     @css_pattern = pattern
     @error_message = nil
-    unless @css_pattern.is_a?(Hash)
-      @css_pattern = Array(@css_pattern).map do |key|
-        [key, 1]
-      end
+    return if @css_pattern.is_a?(Hash)
+
+    @css_pattern = Array(@css_pattern).map do |key|
+      [key, 1]
     end
   end
 
@@ -60,7 +58,7 @@ def matches?(text)
       if amount_or_pattern_or_string_or_proc.is_a?(String) or amount_or_pattern_or_string_or_proc.is_a?(Regexp)
         pattern_or_string = amount_or_pattern_or_string_or_proc
         html = path.inner_html
-        if !html.match(pattern_or_string)
+        unless html.match(pattern_or_string)
           return error!("#{css.inspect} did not match #{pattern_or_string.inspect}. It was \n:#{html.inspect}")
         end
       elsif amount_or_pattern_or_string_or_proc.is_a? Numeric
@@ -70,13 +68,12 @@ def matches?(text)
           return error!("did not find #{css.inspect} #{expected_amount.inspect} times. It was #{amount.inspect}")
         end
       elsif amount_or_pattern_or_string_or_proc.is_a? Proc
-        if !amount_or_pattern_or_string_or_proc.call(path)
+        unless amount_or_pattern_or_string_or_proc.call(path)
           return error!("#{css.inspect} did not validate (proc must not return a falsy value)")
         end
       else
         raise "Instance of String, Rexexp, Proc or Fixnum required"
       end
-      true
     end
   end
 
diff --git a/spec/support/mongo_mapper.rb b/spec/support/mongo_mapper.rb
index 4f37240..5876028 100644
--- a/spec/support/mongo_mapper.rb
+++ b/spec/support/mongo_mapper.rb
@@ -10,7 +10,6 @@ class MongoMapperEntry
     key :confirmed, Boolean, default: false
     key :shipping_date, Time
     timestamps!
-
   end
 
   class MongoMapperGrid
@@ -27,6 +26,5 @@ class MongoMapperGrid
     column :name
     column :group_id
     column :disabled
-
   end
 end
diff --git a/spec/support/mongoid.rb b/spec/support/mongoid.rb
index de617b7..014c008 100644
--- a/spec/support/mongoid.rb
+++ b/spec/support/mongoid.rb
@@ -1,10 +1,8 @@
 require "rubygems"
 
-#Mongoid.logger = TEST_LOGGER #TODO: understand why still output to STDOUT
-
+# Mongoid.logger = TEST_LOGGER #TODO: understand why still output to STDOUT
 
 class MongoidEntry
-
   include Mongoid::Document
   include Mongoid::Timestamps
 
@@ -14,7 +12,6 @@ class MongoidEntry
   field :disabled, default: false, type: Boolean
   field :confirmed, default: false, type: Boolean
   field :shipping_date, type: Time
-
 end
 
 class MongoidGrid
@@ -31,6 +28,4 @@ class MongoidGrid
   column :name
   column :group_id
   column :disabled
-
 end
-
diff --git a/spec/support/sequel.rb b/spec/support/sequel.rb
index 48fe2d4..5130fc9 100644
--- a/spec/support/sequel.rb
+++ b/spec/support/sequel.rb
@@ -1,6 +1,5 @@
 require "sequel"
 
-
 DB = Sequel.sqlite # memory database
 DB.extension(:pagination)
 
@@ -17,10 +16,8 @@
 end
 
 class SequelEntry < Sequel::Model
-
 end
 
-
 class SequelGrid
   include ::Datagrid
 
@@ -35,6 +32,4 @@ class SequelGrid
   column :name
   column :group_id
   column :disabled
-
 end
-
diff --git a/spec/support/simple_report.rb b/spec/support/simple_report.rb
index 02591f9..57e553a 100644
--- a/spec/support/simple_report.rb
+++ b/spec/support/simple_report.rb
@@ -1,5 +1,3 @@
-
-
 def test_report(attributes = {}, &block)
   klass = test_report_class(&block)
   klass.new(attributes)
@@ -13,14 +11,11 @@ def self.name
         "TestGrid"
       end
     end
-    if block
-      klass.class_eval(&block)
-    end
+    klass.class_eval(&block) if block
   end
 end
 
 class SimpleReport
-
   include Datagrid
 
   scope do
@@ -28,16 +23,16 @@ class SimpleReport
   end
 
   filter(:group_id, :integer, multiple: true)
-  filter(:category, :enum, select: ["first", "second"])
+  filter(:category, :enum, select: %w[first second])
   filter(:disabled, :xboolean)
   filter(:confirmed, :boolean)
 
   filter(:name) do |value|
-    self.where(name: value)
+    where(name: value)
   end
 
   column(:group, order: "groups.name") do
-    self.group.name
+    group.name
   end
 
   column(:name) do |user|
@@ -45,20 +40,18 @@ class SimpleReport
   end
 
   column(:actions, html: true) do |model|
-    render partial: "/actions", locals: {model: model}
+    render partial: "/actions", locals: { model: model }
   end
 
-  column(:pet, html: lambda {|data| content_tag :em, data}) do
-    self.pet&.upcase
+  column(:pet, html: ->(data) { content_tag :em, data }) do
+    pet&.upcase
   end
 
   column(:shipping_date, before: :group)
 
-  column(:access_level, html: lambda {|data| content_tag :h1, data}, after: :actions)
+  column(:access_level, html: ->(data) { content_tag :h1, data }, after: :actions)
 
   def param_name
     :report
   end
-
 end
-

From fb7002fb7ab700e29b9324ce489492dacd4cf0ca Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 19:38:26 +0100
Subject: [PATCH 009/157] Rubocop fixes

---
 Appraisals                                    |   2 +
 Gemfile                                       |   2 +
 gemfiles/rails_6.1.gemfile                    |   2 +
 gemfiles/rails_7.0.gemfile                    |   2 +
 gemfiles/rails_7.1.gemfile                    |   2 +
 gemfiles/rails_7.2.gemfile                    |   2 +
 lib/datagrid.rb                               |   2 +
 lib/datagrid/active_model.rb                  |   2 +
 lib/datagrid/column_names_attribute.rb        |  10 +-
 lib/datagrid/columns.rb                       |   8 +-
 lib/datagrid/columns/column.rb                | 264 ++++++++--------
 lib/datagrid/configuration.rb                 |   2 +
 lib/datagrid/core.rb                          |   5 +-
 lib/datagrid/drivers.rb                       |   2 +
 lib/datagrid/drivers/abstract_driver.rb       |   4 +-
 lib/datagrid/drivers/active_record.rb         |   2 +
 lib/datagrid/drivers/array.rb                 |   2 +
 lib/datagrid/drivers/mongo_mapper.rb          |   2 +
 lib/datagrid/drivers/mongoid.rb               |   2 +
 lib/datagrid/drivers/sequel.rb                |  10 +-
 lib/datagrid/engine.rb                        |   2 +
 lib/datagrid/filters.rb                       |   4 +-
 lib/datagrid/filters/base_filter.rb           | 295 +++++++++---------
 lib/datagrid/filters/boolean_filter.rb        |  12 +-
 lib/datagrid/filters/composite_filters.rb     |   2 +
 lib/datagrid/filters/date_filter.rb           |  52 +--
 lib/datagrid/filters/date_time_filter.rb      |  36 ++-
 lib/datagrid/filters/default_filter.rb        |  12 +-
 lib/datagrid/filters/dynamic_filter.rb        | 214 +++++++------
 lib/datagrid/filters/enum_filter.rb           |  48 +--
 .../filters/extended_boolean_filter.rb        |  66 ++--
 lib/datagrid/filters/float_filter.rb          |  16 +-
 lib/datagrid/filters/integer_filter.rb        |  26 +-
 lib/datagrid/filters/ranged_filter.rb         |  84 ++---
 lib/datagrid/filters/select_options.rb        | 108 ++++---
 lib/datagrid/filters/string_filter.rb         |  14 +-
 lib/datagrid/form_builder.rb                  |  19 +-
 lib/datagrid/helper.rb                        |   2 +
 lib/datagrid/ordering.rb                      |   2 +
 lib/datagrid/renderer.rb                      |   8 +-
 lib/datagrid/rspec.rb                         |   2 +
 lib/datagrid/scaffold.rb                      | 206 ++++++------
 lib/datagrid/utils.rb                         |   6 +-
 lib/datagrid/version.rb                       |   2 +
 lib/tasks/datagrid_tasks.rake                 |   4 +-
 spec/datagrid/active_model_spec.rb            |   2 +
 spec/datagrid/column_names_attribute_spec.rb  |   2 +
 spec/datagrid/columns/column_spec.rb          |   2 +
 spec/datagrid/columns_spec.rb                 |   2 +
 spec/datagrid/core_spec.rb                    |   2 +
 spec/datagrid/drivers/active_record_spec.rb   |   2 +
 spec/datagrid/drivers/array_spec.rb           |   4 +-
 spec/datagrid/drivers/mongo_mapper_spec.rb    |   2 +
 spec/datagrid/drivers/mongoid_spec.rb         |   2 +
 spec/datagrid/drivers/sequel_spec.rb          |   2 +
 spec/datagrid/filters/base_filter_spec.rb     |   2 +
 .../filters/composite_filters_spec.rb         |   2 +
 spec/datagrid/filters/date_filter_spec.rb     |   2 +
 .../datagrid/filters/date_time_filter_spec.rb |   2 +
 spec/datagrid/filters/dynamic_filter_spec.rb  |   2 +
 spec/datagrid/filters/enum_filter_spec.rb     |   4 +-
 .../filters/extended_boolean_filter_spec.rb   |   2 +
 spec/datagrid/filters/float_filter_spec.rb    |   2 +
 spec/datagrid/filters/integer_filter_spec.rb  |   2 +
 spec/datagrid/filters/string_filter_spec.rb   |   2 +
 spec/datagrid/filters_spec.rb                 |   2 +
 spec/datagrid/form_builder_spec.rb            |  44 +--
 spec/datagrid/helper_spec.rb                  |   6 +-
 spec/datagrid/ordering_spec.rb                |   2 +
 spec/datagrid/scaffold_spec.rb                |   2 +
 spec/datagrid/stylesheet_spec.rb              |   2 +
 spec/datagrid/utils_spec.rb                   |   2 +
 spec/datagrid_spec.rb                         |   2 +
 spec/spec_helper.rb                           |   4 +-
 spec/support/active_record.rb                 |   2 +
 spec/support/configuration.rb                 |   2 +
 spec/support/i18n_helpers.rb                  |   2 +
 spec/support/matchers.rb                      |   9 +-
 spec/support/mongo_mapper.rb                  |   2 +
 spec/support/mongoid.rb                       |   2 +
 spec/support/sequel.rb                        |   2 +
 spec/support/simple_report.rb                 |   6 +-
 82 files changed, 960 insertions(+), 748 deletions(-)

diff --git a/Appraisals b/Appraisals
index fe1e44a..8e0cfff 100644
--- a/Appraisals
+++ b/Appraisals
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 appraise "rails-6.1" do
   group :development do
     gem "rails", "~> 6.1.0"
diff --git a/Gemfile b/Gemfile
index 909dbe7..c0c942e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 source "https://rubygems.org"
 
 gemspec
diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile
index 512aa8a..1fd476f 100644
--- a/gemfiles/rails_6.1.gemfile
+++ b/gemfiles/rails_6.1.gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This file was generated by Appraisal
 
 source "https://rubygems.org"
diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile
index 61566c8..44cf0d6 100644
--- a/gemfiles/rails_7.0.gemfile
+++ b/gemfiles/rails_7.0.gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This file was generated by Appraisal
 
 source "https://rubygems.org"
diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile
index bdd7ff4..2bf4d9c 100644
--- a/gemfiles/rails_7.1.gemfile
+++ b/gemfiles/rails_7.1.gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This file was generated by Appraisal
 
 source "https://rubygems.org"
diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile
index 753268c..a21ea73 100644
--- a/gemfiles/rails_7.2.gemfile
+++ b/gemfiles/rails_7.2.gemfile
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This file was generated by Appraisal
 
 source "https://rubygems.org"
diff --git a/lib/datagrid.rb b/lib/datagrid.rb
index 82abe93..4bf2bdf 100644
--- a/lib/datagrid.rb
+++ b/lib/datagrid.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "action_view"
 require "datagrid/configuration"
 require "datagrid/engine"
diff --git a/lib/datagrid/active_model.rb b/lib/datagrid/active_model.rb
index 5284711..22860e0 100644
--- a/lib/datagrid/active_model.rb
+++ b/lib/datagrid/active_model.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   # Required to be ActiveModel compatible
   module ActiveModel
diff --git a/lib/datagrid/column_names_attribute.rb b/lib/datagrid/column_names_attribute.rb
index c04027c..48041ae 100644
--- a/lib/datagrid/column_names_attribute.rb
+++ b/lib/datagrid/column_names_attribute.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   module ColumnNamesAttribute
     extend ActiveSupport::Concern
@@ -45,7 +47,7 @@ def columns(*args, **options)
     # If no mandatory columns specified than all of them considered mandatory
     # @return [Array<Datagrid::Columns::Column>]
     def mandatory_columns
-      available_columns.select { |c| c.mandatory? }
+      available_columns.select(&:mandatory?)
     end
 
     # Returns a list of enabled columns without <tt>mandatory: true</tt> option
@@ -68,7 +70,7 @@ def selected_column_names(*args)
           column.is_a?(Datagrid::Columns::Column) ? column.name : column.to_sym
         end
         args
-      elsif column_names && column_names.any?
+      elsif column_names&.any?
         column_names + mandatory_columns.map(&:name)
       else
         columns_enabled_by_default.map(&:name)
@@ -76,9 +78,7 @@ def selected_column_names(*args)
     end
 
     def columns_visibility_enabled?
-      columns_array.any? do |column|
-        column.mandatory_explicitly_set?
-      end
+      columns_array.any?(&:mandatory_explicitly_set?)
     end
 
     def columns_enabled_by_default
diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index d2fd850..a388ab5 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "datagrid/utils"
 require "active_support/core_ext/class/attribute"
 
@@ -488,7 +490,7 @@ def map_with_batches(&block)
     end
 
     def each_with_batches(&block)
-      if batch_size && batch_size > 0
+      if batch_size&.positive?
         driver.batch_each(assets, batch_size, &block)
       else
         assets.each(&block)
@@ -498,7 +500,7 @@ def each_with_batches(&block)
     def value_from_html_block(context, asset, column)
       args = []
       remaining_arity = column.html_block.arity
-      remaining_arity = 1 if remaining_arity < 0
+      remaining_arity = 1 if remaining_arity.negative?
 
       asset = decorate(asset)
 
@@ -507,7 +509,7 @@ def value_from_html_block(context, asset, column)
         remaining_arity -= 1
       end
 
-      args << asset if remaining_arity > 0
+      args << asset if remaining_arity.positive?
       args << self if remaining_arity > 1
 
       context.instance_exec(*args, &column.html_block)
diff --git a/lib/datagrid/columns/column.rb b/lib/datagrid/columns/column.rb
index 1d50c40..2f15359 100644
--- a/lib/datagrid/columns/column.rb
+++ b/lib/datagrid/columns/column.rb
@@ -1,160 +1,166 @@
-class Datagrid::Columns::Column
-  # Datagrid class holding an information of
-  # how a column should be rendered in data/console/csv format and HTML format
-  class ResponseFormat
-    attr_accessor :data_block, :html_block
-
-    def initialize
-      yield(self)
-    end
-
-    def data(&block)
-      self.data_block = block
-    end
-
-    def html(&block)
-      self.html_block = block
-    end
+# frozen_string_literal: true
+
+module Datagrid
+  module Columns
+    class Column
+      # Datagrid class holding an information of
+      # how a column should be rendered in data/console/csv format and HTML format
+      class ResponseFormat
+        attr_accessor :data_block, :html_block
+
+        def initialize
+          yield(self)
+        end
+
+        def data(&block)
+          self.data_block = block
+        end
+
+        def html(&block)
+          self.html_block = block
+        end
+
+        def call_data
+          data_block.call
+        end
+
+        def to_s
+          call_data.to_s
+        end
+
+        def call_html(context)
+          context.instance_eval(&html_block)
+        end
+      end
 
-    def call_data
-      data_block.call
-    end
+      attr_accessor :grid_class, :options, :data_block, :name, :html_block, :query
 
-    def to_s
-      call_data.to_s
-    end
+      def initialize(grid_class, name, query, options = {}, &block)
+        self.grid_class = grid_class
+        self.name = name.to_sym
+        self.options = options
+        if options[:html] == true
+          self.html_block = block
+        else
+          self.data_block = block
 
-    def call_html(context)
-      context.instance_eval(&html_block)
-    end
-  end
-
-  attr_accessor :grid_class, :options, :data_block, :name, :html_block, :query
+          self.html_block = options[:html] if options[:html].is_a? Proc
+        end
+        self.query = query
+      end
 
-  def initialize(grid_class, name, query, options = {}, &block)
-    self.grid_class = grid_class
-    self.name = name.to_sym
-    self.options = options
-    if options[:html] == true
-      self.html_block = block
-    else
-      self.data_block = block
+      def data_value(model, grid)
+        # backward compatibility method
+        grid.data_value(name, model)
+      end
 
-      self.html_block = options[:html] if options[:html].is_a? Proc
-    end
-    self.query = query
-  end
+      def label
+        options[:label]
+      end
 
-  def data_value(model, grid)
-    # backward compatibility method
-    grid.data_value(name, model)
-  end
+      def header
+        if (header = options[:header])
+          Datagrid::Utils.callable(header)
+        else
+          Datagrid::Utils.translate_from_namespace(:columns, grid_class, name)
+        end
+      end
 
-  def label
-    options[:label]
-  end
+      def order
+        if options.key?(:order) && options[:order] != true
+          options[:order]
+        else
+          driver.default_order(grid_class.scope, name)
+        end
+      end
 
-  def header
-    if header = options[:header]
-      Datagrid::Utils.callable(header)
-    else
-      Datagrid::Utils.translate_from_namespace(:columns, grid_class, name)
-    end
-  end
+      def supports_order?
+        order || order_by_value?
+      end
 
-  def order
-    if options.has_key?(:order) && options[:order] != true
-      options[:order]
-    else
-      driver.default_order(grid_class.scope, name)
-    end
-  end
+      def order_by_value(model, grid)
+        if options[:order_by_value] == true
+          grid.data_value(self, model)
+        else
+          Datagrid::Utils.apply_args(model, grid, &options[:order_by_value])
+        end
+      end
 
-  def supports_order?
-    order || order_by_value?
-  end
+      def order_by_value?
+        !!options[:order_by_value]
+      end
 
-  def order_by_value(model, grid)
-    if options[:order_by_value] == true
-      grid.data_value(self, model)
-    else
-      Datagrid::Utils.apply_args(model, grid, &options[:order_by_value])
-    end
-  end
+      def order_desc
+        return nil unless order
 
-  def order_by_value?
-    !!options[:order_by_value]
-  end
+        options[:order_desc]
+      end
 
-  def order_desc
-    return nil unless order
+      def html?
+        options[:html] != false
+      end
 
-    options[:order_desc]
-  end
+      def data?
+        data_block != nil
+      end
 
-  def html?
-    options[:html] != false
-  end
+      def mandatory?
+        !!options[:mandatory]
+      end
 
-  def data?
-    data_block != nil
-  end
+      def mandatory_explicitly_set?
+        options.key?(:mandatory)
+      end
 
-  def mandatory?
-    !!options[:mandatory]
-  end
+      def enabled?(grid)
+        ::Datagrid::Utils.process_availability(grid, options[:if], options[:unless])
+      end
 
-  def mandatory_explicitly_set?
-    options.key?(:mandatory)
-  end
+      def inspect
+        "#<#{self.class} #{grid_class}##{name} #{options.inspect}>"
+      end
 
-  def enabled?(grid)
-    ::Datagrid::Utils.process_availability(grid, options[:if], options[:unless])
-  end
+      def to_s
+        header
+      end
 
-  def inspect
-    "#<#{self.class} #{grid_class}##{name} #{options.inspect}>"
-  end
+      def html_value(context, asset, grid)
+        grid.html_value(name, context, asset)
+      end
 
-  def to_s
-    header
-  end
+      def generic_value(model, grid)
+        grid.generic_value(self, model)
+      end
 
-  def html_value(context, asset, grid)
-    grid.html_value(name, context, asset)
-  end
+      def append_preload(relation)
+        return relation unless preload
 
-  def generic_value(model, grid)
-    grid.generic_value(self, model)
-  end
+        if preload.respond_to?(:call)
+          return relation unless preload
 
-  def append_preload(relation)
-    return relation unless preload
+          if preload.arity == 1
+            preload.call(relation)
+          else
+            relation.instance_exec(&preload)
+          end
+        else
+          driver.default_preload(relation, preload)
+        end
+      end
 
-    if preload.respond_to?(:call)
-      return relation unless preload
+      def preload
+        preload = options[:preload]
 
-      if preload.arity == 1
-        preload.call(relation)
-      else
-        relation.instance_exec(&preload)
+        if preload == true && driver.can_preload?(grid_class.scope, name)
+          name
+        else
+          preload
+        end
       end
-    else
-      driver.default_preload(relation, preload)
-    end
-  end
-
-  def preload
-    preload = options[:preload]
 
-    if preload == true && driver.can_preload?(grid_class.scope, name)
-      name
-    else
-      preload
+      def driver
+        grid_class.driver
+      end
     end
   end
-
-  def driver
-    grid_class.driver
-  end
 end
diff --git a/lib/datagrid/configuration.rb b/lib/datagrid/configuration.rb
index 65a1653..2f956e2 100644
--- a/lib/datagrid/configuration.rb
+++ b/lib/datagrid/configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   def self.configuration
     @configuration ||= Configuration.new
diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index 0f560a4..4fc1166 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "datagrid/drivers"
 require "active_support/core_ext/class/attribute"
 require "active_model/attribute_assignment"
@@ -150,9 +152,6 @@ def attributes
     #   grid.attributes = {first_name: 'John', last_name: 'Smith'}
     #   grid.first_name # => 'John'
     #   grid.last_name # => 'Smith'
-    def attributes=(attributes)
-      super(attributes)
-    end
 
     # @return [Object] Any datagrid attribute value
     def [](attribute)
diff --git a/lib/datagrid/drivers.rb b/lib/datagrid/drivers.rb
index 3e72d3f..03953af 100644
--- a/lib/datagrid/drivers.rb
+++ b/lib/datagrid/drivers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "datagrid/drivers/abstract_driver"
 require "datagrid/drivers/active_record"
 require "datagrid/drivers/array"
diff --git a/lib/datagrid/drivers/abstract_driver.rb b/lib/datagrid/drivers/abstract_driver.rb
index 99530a8..8807c1b 100644
--- a/lib/datagrid/drivers/abstract_driver.rb
+++ b/lib/datagrid/drivers/abstract_driver.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
 module Datagrid
   module Drivers
     # @!visibility private
     class AbstractDriver
-      TIMESTAMP_CLASSES = [DateTime, Time, ActiveSupport::TimeWithZone]
+      TIMESTAMP_CLASSES = [DateTime, Time, ActiveSupport::TimeWithZone].freeze
 
       class_attribute :subclasses, default: []
 
diff --git a/lib/datagrid/drivers/active_record.rb b/lib/datagrid/drivers/active_record.rb
index 3135df8..5be39b1 100644
--- a/lib/datagrid/drivers/active_record.rb
+++ b/lib/datagrid/drivers/active_record.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   module Drivers
     # @!visibility private
diff --git a/lib/datagrid/drivers/array.rb b/lib/datagrid/drivers/array.rb
index 3d2da1d..bfa36ef 100644
--- a/lib/datagrid/drivers/array.rb
+++ b/lib/datagrid/drivers/array.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   module Drivers
     # @!visibility private
diff --git a/lib/datagrid/drivers/mongo_mapper.rb b/lib/datagrid/drivers/mongo_mapper.rb
index e36fa51..df1c178 100644
--- a/lib/datagrid/drivers/mongo_mapper.rb
+++ b/lib/datagrid/drivers/mongo_mapper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   module Drivers
     # @!visibility private
diff --git a/lib/datagrid/drivers/mongoid.rb b/lib/datagrid/drivers/mongoid.rb
index c737ff4..aa166a8 100644
--- a/lib/datagrid/drivers/mongoid.rb
+++ b/lib/datagrid/drivers/mongoid.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   module Drivers
     # @!visibility private
diff --git a/lib/datagrid/drivers/sequel.rb b/lib/datagrid/drivers/sequel.rb
index ce603b8..a450642 100644
--- a/lib/datagrid/drivers/sequel.rb
+++ b/lib/datagrid/drivers/sequel.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   module Drivers
     # @!visibility private
@@ -18,10 +20,6 @@ def to_scope(scope)
         scope.where({})
       end
 
-      def append_column_queries(assets, columns)
-        super
-      end
-
       def where(scope, attribute, value)
         scope.where(attribute => value)
       end
@@ -34,10 +32,6 @@ def desc(scope, order)
         scope.order(::Sequel.desc(::Sequel.lit(order)))
       end
 
-      def reverse_order(scope)
-        super
-      end
-
       def default_order(scope, column_name)
         has_column?(scope, column_name) ? ::Sequel.lit(prefix_table_name(scope, column_name)) : nil
       end
diff --git a/lib/datagrid/engine.rb b/lib/datagrid/engine.rb
index b37dd4e..3a52dd6 100644
--- a/lib/datagrid/engine.rb
+++ b/lib/datagrid/engine.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "rails/engine"
 require "datagrid/helper"
 require "datagrid/form_builder"
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 3283c81..ecca158 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "active_support/core_ext/class/attribute"
 
 module Datagrid
@@ -27,7 +29,7 @@ module Filters
       enum: Filters::EnumFilter,
       float: Filters::FloatFilter,
       dynamic: Filters::DynamicFilter
-    }
+    }.freeze
 
     # @!visibility private
     DEFAULT_FILTER_BLOCK = Object.new
diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index 608e8d9..ab43023 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -1,185 +1,194 @@
+# frozen_string_literal: true
+
 # An error raise when datagrid filter is defined incorrectly and
 # causes filtering chain to be broken
-class Datagrid::FilteringError < StandardError
+module Datagrid
+  class FilteringError < StandardError
+  end
 end
 
 # @!visibility private
-class Datagrid::Filters::BaseFilter
-  class_attribute :input_helper_name, instance_writer: false
-  attr_accessor :grid_class, :options, :block, :name
-
-  def initialize(grid_class, name, options = {}, &block)
-    self.grid_class = grid_class
-    self.name = name.to_sym
-    self.options = options
-    self.block = block || default_filter_block
-  end
+module Datagrid
+  module Filters
+    class BaseFilter
+      class_attribute :input_helper_name, instance_writer: false
+      attr_accessor :grid_class, :options, :block, :name
+
+      def initialize(grid_class, name, options = {}, &block)
+        self.grid_class = grid_class
+        self.name = name.to_sym
+        self.options = options
+        self.block = block || default_filter_block
+      end
 
-  def parse(value)
-    raise NotImplementedError, "#parse(value) suppose to be overwritten"
-  end
+      def parse(value)
+        raise NotImplementedError, "#parse(value) suppose to be overwritten"
+      end
 
-  def unapplicable_value?(value)
-    value.nil? ? !allow_nil? : value.blank? && !allow_blank?
-  end
+      def unapplicable_value?(value)
+        value.nil? ? !allow_nil? : value.blank? && !allow_blank?
+      end
 
-  def apply(grid_object, scope, value)
-    return scope if unapplicable_value?(value)
+      def apply(grid_object, scope, value)
+        return scope if unapplicable_value?(value)
 
-    result = execute(value, scope, grid_object)
+        result = execute(value, scope, grid_object)
 
-    return scope unless result
+        return scope unless result
 
-    result = default_filter(value, scope, grid_object) if result == Datagrid::Filters::DEFAULT_FILTER_BLOCK
-    unless grid_object.driver.match?(result)
-      raise Datagrid::FilteringError,
-            "Can not apply #{name.inspect} filter: result #{result.inspect} no longer match #{grid_object.driver.class}."
-    end
+        result = default_filter(value, scope, grid_object) if result == Datagrid::Filters::DEFAULT_FILTER_BLOCK
+        unless grid_object.driver.match?(result)
+          raise Datagrid::FilteringError,
+                "Can not apply #{name.inspect} filter: result #{result.inspect} no longer match #{grid_object.driver.class}."
+        end
 
-    result
-  end
-
-  def parse_values(value)
-    if multiple?
-      return nil if value.nil?
+        result
+      end
 
-      normalize_multiple_value(value).map do |v|
-        parse(v)
+      def parse_values(value)
+        if multiple?
+          return nil if value.nil?
+
+          normalize_multiple_value(value).map do |v|
+            parse(v)
+          end
+        elsif value.is_a?(Array)
+          raise Datagrid::ArgumentError,
+                "#{grid_class}##{name} filter can not accept Array argument. Use :multiple option."
+        else
+          parse(value)
+        end
       end
-    elsif value.is_a?(Array)
-      raise Datagrid::ArgumentError, "#{grid_class}##{name} filter can not accept Array argument. Use :multiple option."
-    else
-      parse(value)
-    end
-  end
 
-  def separator
-    options[:multiple].is_a?(String) ? options[:multiple] : default_separator
-  end
+      def separator
+        options[:multiple].is_a?(String) ? options[:multiple] : default_separator
+      end
 
-  def header
-    if header = options[:header]
-      Datagrid::Utils.callable(header)
-    else
-      Datagrid::Utils.translate_from_namespace(:filters, grid_class, name)
-    end
-  end
+      def header
+        if (header = options[:header])
+          Datagrid::Utils.callable(header)
+        else
+          Datagrid::Utils.translate_from_namespace(:filters, grid_class, name)
+        end
+      end
 
-  def default(object)
-    default = options[:default]
-    if default.is_a?(Symbol)
-      object.send(default)
-    elsif default.respond_to?(:call)
-      Datagrid::Utils.apply_args(object, &default)
-    else
-      default
-    end
-  end
+      def default(object)
+        default = options[:default]
+        if default.is_a?(Symbol)
+          object.send(default)
+        elsif default.respond_to?(:call)
+          Datagrid::Utils.apply_args(object, &default)
+        else
+          default
+        end
+      end
 
-  def multiple?
-    options[:multiple]
-  end
+      def multiple?
+        options[:multiple]
+      end
 
-  def allow_nil?
-    options.has_key?(:allow_nil) ? options[:allow_nil] : options[:allow_blank]
-  end
+      def allow_nil?
+        options.key?(:allow_nil) ? options[:allow_nil] : options[:allow_blank]
+      end
 
-  def allow_blank?
-    options[:allow_blank]
-  end
+      def allow_blank?
+        options[:allow_blank]
+      end
 
-  def input_options
-    options[:input_options] || {}
-  end
+      def input_options
+        options[:input_options] || {}
+      end
 
-  def label_options
-    options[:label_options] || {}
-  end
+      def label_options
+        options[:label_options] || {}
+      end
 
-  def form_builder_helper_name
-    self.class.form_builder_helper_name
-  end
+      def form_builder_helper_name
+        self.class.form_builder_helper_name
+      end
 
-  def self.form_builder_helper_name
-    :"datagrid_#{to_s.demodulize.underscore}"
-  end
+      def self.form_builder_helper_name
+        :"datagrid_#{to_s.demodulize.underscore}"
+      end
 
-  def default_filter_block
-    filter = self
-    lambda do |value, scope, grid|
-      filter.default_filter(value, scope, grid)
-    end
-  end
+      def default_filter_block
+        filter = self
+        lambda do |value, scope, grid|
+          filter.default_filter(value, scope, grid)
+        end
+      end
 
-  def supports_range?
-    self.class.ancestors.include?(::Datagrid::Filters::RangedFilter)
-  end
+      def supports_range?
+        self.class.ancestors.include?(::Datagrid::Filters::RangedFilter)
+      end
 
-  def format(value)
-    value.nil? ? nil : value.to_s
-  end
+      def format(value)
+        value&.to_s
+      end
 
-  def dummy?
-    options[:dummy]
-  end
+      def dummy?
+        options[:dummy]
+      end
 
-  def type
-    Datagrid::Filters::FILTER_TYPES.each do |type, klass|
-      return type if is_a?(klass)
-    end
-    raise "wtf is #{inspect}"
-  end
+      def type
+        Datagrid::Filters::FILTER_TYPES.each do |type, klass|
+          return type if is_a?(klass)
+        end
+        raise "wtf is #{inspect}"
+      end
 
-  def enabled?(grid)
-    ::Datagrid::Utils.process_availability(grid, options[:if], options[:unless])
-  end
+      def enabled?(grid)
+        ::Datagrid::Utils.process_availability(grid, options[:if], options[:unless])
+      end
 
-  def default_html_classes
-    [name, self.class.to_s.demodulize.underscore]
-  end
+      def default_html_classes
+        [name, self.class.to_s.demodulize.underscore]
+      end
 
-  protected
+      protected
 
-  def default_filter_where(scope, value)
-    driver.where(scope, name, value)
-  end
+      def default_filter_where(scope, value)
+        driver.where(scope, name, value)
+      end
 
-  def execute(value, scope, grid_object)
-    if block.arity == 1
-      scope.instance_exec(value, &block)
-    else
-      Datagrid::Utils.apply_args(value, scope, grid_object, &block)
-    end
-  end
+      def execute(value, scope, grid_object)
+        if block.arity == 1
+          scope.instance_exec(value, &block)
+        else
+          Datagrid::Utils.apply_args(value, scope, grid_object, &block)
+        end
+      end
 
-  def normalize_multiple_value(value)
-    case value
-    when String
-      value.split(separator)
-    when Range
-      [value.begin, value.end]
-    when Array
-      value
-    else
-      Array.wrap(value)
-    end
-  end
+      def normalize_multiple_value(value)
+        case value
+        when String
+          value.split(separator)
+        when Range
+          [value.begin, value.end]
+        when Array
+          value
+        else
+          Array.wrap(value)
+        end
+      end
 
-  def default_separator
-    ","
-  end
+      def default_separator
+        ","
+      end
 
-  def driver
-    grid_class.driver
-  end
+      def driver
+        grid_class.driver
+      end
 
-  def default_filter(value, scope, _grid)
-    return nil if dummy?
+      def default_filter(value, scope, _grid)
+        return nil if dummy?
 
-    if !driver.has_column?(scope, name) && scope.respond_to?(name, true)
-      scope.public_send(name, value)
-    else
-      default_filter_where(scope, value)
+        if !driver.has_column?(scope, name) && scope.respond_to?(name, true)
+          scope.public_send(name, value)
+        else
+          default_filter_where(scope, value)
+        end
+      end
     end
   end
 end
diff --git a/lib/datagrid/filters/boolean_filter.rb b/lib/datagrid/filters/boolean_filter.rb
index 89aed9b..4248b7b 100644
--- a/lib/datagrid/filters/boolean_filter.rb
+++ b/lib/datagrid/filters/boolean_filter.rb
@@ -1,7 +1,13 @@
+# frozen_string_literal: true
+
 require "datagrid/utils"
 # @!visibility private
-class Datagrid::Filters::BooleanFilter < Datagrid::Filters::BaseFilter
-  def parse(value)
-    Datagrid::Utils.booleanize(value)
+module Datagrid
+  module Filters
+    class BooleanFilter < Datagrid::Filters::BaseFilter
+      def parse(value)
+        Datagrid::Utils.booleanize(value)
+      end
+    end
   end
 end
diff --git a/lib/datagrid/filters/composite_filters.rb b/lib/datagrid/filters/composite_filters.rb
index 3eb405a..a3d9238 100644
--- a/lib/datagrid/filters/composite_filters.rb
+++ b/lib/datagrid/filters/composite_filters.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   module Filters
     # @!visibility private
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index f9f47c9..170e249 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -1,33 +1,39 @@
+# frozen_string_literal: true
+
 require "datagrid/filters/ranged_filter"
 
-class Datagrid::Filters::DateFilter < Datagrid::Filters::BaseFilter
-  include Datagrid::Filters::RangedFilter
+module Datagrid
+  module Filters
+    class DateFilter < Datagrid::Filters::BaseFilter
+      include Datagrid::Filters::RangedFilter
 
-  def apply(grid_object, scope, value)
-    value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
-    super(grid_object, scope, value)
-  end
+      def apply(grid_object, scope, value)
+        value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
+        super(grid_object, scope, value)
+      end
 
-  def parse(value)
-    Datagrid::Utils.parse_date(value)
-  end
+      def parse(value)
+        Datagrid::Utils.parse_date(value)
+      end
 
-  def format(value)
-    if formats.any? && value
-      value.strftime(formats.first)
-    else
-      super
-    end
-  end
+      def format(value)
+        if formats.any? && value
+          value.strftime(formats.first)
+        else
+          super
+        end
+      end
 
-  def default_filter_where(scope, value)
-    value = Datagrid::Utils.format_date_as_timestamp(value) if driver.is_timestamp?(scope, name)
-    super(scope, value)
-  end
+      def default_filter_where(scope, value)
+        value = Datagrid::Utils.format_date_as_timestamp(value) if driver.is_timestamp?(scope, name)
+        super(scope, value)
+      end
 
-  protected
+      protected
 
-  def formats
-    Array(Datagrid.configuration.date_formats)
+      def formats
+        Array(Datagrid.configuration.date_formats)
+      end
+    end
   end
 end
diff --git a/lib/datagrid/filters/date_time_filter.rb b/lib/datagrid/filters/date_time_filter.rb
index 9809f32..0014eb1 100644
--- a/lib/datagrid/filters/date_time_filter.rb
+++ b/lib/datagrid/filters/date_time_filter.rb
@@ -1,23 +1,29 @@
+# frozen_string_literal: true
+
 require "datagrid/filters/ranged_filter"
 
-class Datagrid::Filters::DateTimeFilter < Datagrid::Filters::BaseFilter
-  include Datagrid::Filters::RangedFilter
+module Datagrid
+  module Filters
+    class DateTimeFilter < Datagrid::Filters::BaseFilter
+      include Datagrid::Filters::RangedFilter
 
-  def parse(value)
-    Datagrid::Utils.parse_datetime(value)
-  end
+      def parse(value)
+        Datagrid::Utils.parse_datetime(value)
+      end
 
-  def format(value)
-    if formats.any? && value
-      value.strftime(formats.first)
-    else
-      super
-    end
-  end
+      def format(value)
+        if formats.any? && value
+          value.strftime(formats.first)
+        else
+          super
+        end
+      end
 
-  protected
+      protected
 
-  def formats
-    Array(Datagrid.configuration.datetime_formats)
+      def formats
+        Array(Datagrid.configuration.datetime_formats)
+      end
+    end
   end
 end
diff --git a/lib/datagrid/filters/default_filter.rb b/lib/datagrid/filters/default_filter.rb
index d547c78..c0a6f39 100644
--- a/lib/datagrid/filters/default_filter.rb
+++ b/lib/datagrid/filters/default_filter.rb
@@ -1,5 +1,11 @@
-class Datagrid::Filters::DefaultFilter < Datagrid::Filters::BaseFilter
-  def parse(value)
-    value
+# frozen_string_literal: true
+
+module Datagrid
+  module Filters
+    class DefaultFilter < Datagrid::Filters::BaseFilter
+      def parse(value)
+        value
+      end
+    end
   end
 end
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index c2b29d3..0a0ae83 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -1,121 +1,127 @@
-require "datagrid/filters/select_options"
+# frozen_string_literal: true
 
-class Datagrid::Filters::DynamicFilter < Datagrid::Filters::BaseFilter
-  include Datagrid::Filters::SelectOptions
-
-  EQUAL_OPERATION = "="
-  LIKE_OPERATION = "=~"
-  MORE_EQUAL_OPERATION = ">="
-  LESS_EQUAL_OPERATION = "<="
-  DEFAULT_OPERATIONS = [
-    EQUAL_OPERATION,
-    LIKE_OPERATION,
-    MORE_EQUAL_OPERATION,
-    LESS_EQUAL_OPERATION
-  ]
-  AVAILABLE_OPERATIONS = %w[= =~ >= <=]
-
-  def initialize(*)
-    super
-    options[:select] ||= default_select
-    options[:operations] ||= DEFAULT_OPERATIONS
-    return if options.has_key?(:include_blank)
-
-    options[:include_blank] = false
-  end
+require "datagrid/filters/select_options"
 
-  def parse_values(filter)
-    field, operation, value = filter
+module Datagrid
+  module Filters
+    class DynamicFilter < Datagrid::Filters::BaseFilter
+      include Datagrid::Filters::SelectOptions
+
+      EQUAL_OPERATION = "="
+      LIKE_OPERATION = "=~"
+      MORE_EQUAL_OPERATION = ">="
+      LESS_EQUAL_OPERATION = "<="
+      DEFAULT_OPERATIONS = [
+        EQUAL_OPERATION,
+        LIKE_OPERATION,
+        MORE_EQUAL_OPERATION,
+        LESS_EQUAL_OPERATION
+      ].freeze
+      AVAILABLE_OPERATIONS = %w[= =~ >= <=].freeze
+
+      def initialize(*)
+        super
+        options[:select] ||= default_select
+        options[:operations] ||= DEFAULT_OPERATIONS
+        return if options.key?(:include_blank)
+
+        options[:include_blank] = false
+      end
 
-    [field, operation, type_cast(field, value)]
-  end
+      def parse_values(filter)
+        field, operation, value = filter
 
-  def unapplicable_value?(filter)
-    _, _, value = filter
-    super(value)
-  end
-
-  def default_filter_where(scope, filter)
-    field, operation, value = filter
-    date_conversion = value.is_a?(Date) && driver.is_timestamp?(scope, field)
+        [field, operation, type_cast(field, value)]
+      end
 
-    return scope if field.blank? || operation.blank?
+      def unapplicable_value?(filter)
+        _, _, value = filter
+        super(value)
+      end
 
-    unless operations.include?(operation)
-      raise Datagrid::FilteringError,
-            "Unknown operation: #{operation.inspect}. Available operations: #{operations.join(' ')}"
-    end
+      def default_filter_where(scope, filter)
+        field, operation, value = filter
+        date_conversion = value.is_a?(Date) && driver.is_timestamp?(scope, field)
+
+        return scope if field.blank? || operation.blank?
+
+        unless operations.include?(operation)
+          raise Datagrid::FilteringError,
+                "Unknown operation: #{operation.inspect}. Available operations: #{operations.join(' ')}"
+        end
+
+        case operation
+        when EQUAL_OPERATION
+          value = Datagrid::Utils.format_date_as_timestamp(value) if date_conversion
+          driver.where(scope, field, value)
+        when LIKE_OPERATION
+          if column_type(field) == :string
+            driver.contains(scope, field, value)
+          else
+            value = Datagrid::Utils.format_date_as_timestamp(value) if date_conversion
+            driver.where(scope, field, value)
+          end
+        when MORE_EQUAL_OPERATION
+          value = value.beginning_of_day if date_conversion
+          driver.greater_equal(scope, field, value)
+        when LESS_EQUAL_OPERATION
+          value = value.end_of_day if date_conversion
+          driver.less_equal(scope, field, value)
+        else
+          raise Datagrid::FilteringError,
+                "Unknown operation: #{operation.inspect}. Use filter block argument to implement operation"
+        end
+      end
 
-    case operation
-    when EQUAL_OPERATION
-      value = Datagrid::Utils.format_date_as_timestamp(value) if date_conversion
-      driver.where(scope, field, value)
-    when LIKE_OPERATION
-      if column_type(field) == :string
-        driver.contains(scope, field, value)
-      else
-        value = Datagrid::Utils.format_date_as_timestamp(value) if date_conversion
-        driver.where(scope, field, value)
+      def operations
+        options[:operations]
       end
-    when MORE_EQUAL_OPERATION
-      value = value.beginning_of_day if date_conversion
-      driver.greater_equal(scope, field, value)
-    when LESS_EQUAL_OPERATION
-      value = value.end_of_day if date_conversion
-      driver.less_equal(scope, field, value)
-    else
-      raise Datagrid::FilteringError,
-            "Unknown operation: #{operation.inspect}. Use filter block argument to implement operation"
-    end
-  end
 
-  def operations
-    options[:operations]
-  end
+      def operations_select
+        operations.map do |operation|
+          [I18n.t(operation, scope: "datagrid.filters.dynamic.operations").html_safe, operation]
+        end
+      end
 
-  def operations_select
-    operations.map do |operation|
-      [I18n.t(operation, scope: "datagrid.filters.dynamic.operations").html_safe, operation]
-    end
-  end
+      protected
 
-  protected
+      def default_select
+        proc { |grid|
+          grid.driver.column_names(grid.scope).map do |name|
+            # Mongodb/Rails problem:
+            # '_id'.humanize returns ''
+            [name.gsub(/^_/, "").humanize.strip, name]
+          end
+        }
+      end
 
-  def default_select
-    proc { |grid|
-      grid.driver.column_names(grid.scope).map do |name|
-        # Mongodb/Rails problem:
-        # '_id'.humanize returns ''
-        [name.gsub(/^_/, "").humanize.strip, name]
+      def type_cast(field, value)
+        type = column_type(field)
+        return nil if value.blank?
+
+        case type
+        when :string
+          value.to_s
+        when :integer
+          value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_i : nil
+        when :float
+          value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_f : nil
+        when :date
+          Datagrid::Utils.parse_date(value)
+        when :timestamp
+          Datagrid::Utils.parse_date(value)
+        when :boolean
+          Datagrid::Utils.booleanize(value)
+        when nil
+          value
+        else
+          raise NotImplementedError, "unknown column type: #{type.inspect}"
+        end
       end
-    }
-  end
 
-  def type_cast(field, value)
-    type = column_type(field)
-    return nil if value.blank?
-
-    case type
-    when :string
-      value.to_s
-    when :integer
-      value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_i : nil
-    when :float
-      value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_f : nil
-    when :date
-      Datagrid::Utils.parse_date(value)
-    when :timestamp
-      Datagrid::Utils.parse_date(value)
-    when :boolean
-      Datagrid::Utils.booleanize(value)
-    when nil
-      value
-    else
-      raise NotImplementedError, "unknown column type: #{type.inspect}"
+      def column_type(field)
+        grid_class.driver.normalized_column_type(grid_class.scope, field)
+      end
     end
   end
-
-  def column_type(field)
-    grid_class.driver.normalized_column_type(grid_class.scope, field)
-  end
 end
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index bc65cc6..d16f7c1 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -1,31 +1,37 @@
+# frozen_string_literal: true
+
 require "datagrid/filters/select_options"
 
-class Datagrid::Filters::EnumFilter < Datagrid::Filters::BaseFilter
-  include Datagrid::Filters::SelectOptions
+module Datagrid
+  module Filters
+    class EnumFilter < Datagrid::Filters::BaseFilter
+      include Datagrid::Filters::SelectOptions
 
-  def initialize(*args)
-    super(*args)
-    options[:multiple] = true if checkboxes?
-    raise Datagrid::ConfigurationError, ":select option not specified" unless options[:select]
-  end
+      def initialize(*args)
+        super(*args)
+        options[:multiple] = true if checkboxes?
+        raise Datagrid::ConfigurationError, ":select option not specified" unless options[:select]
+      end
 
-  def parse(value)
-    return nil if strict && !select.include?(value)
+      def parse(value)
+        return nil if strict && !select.include?(value)
 
-    value
-  end
+        value
+      end
 
-  def default_html_classes
-    res = super
-    res.push("checkboxes") if checkboxes?
-    res
-  end
+      def default_html_classes
+        res = super
+        res.push("checkboxes") if checkboxes?
+        res
+      end
 
-  def strict
-    options[:strict]
-  end
+      def strict
+        options[:strict]
+      end
 
-  def checkboxes?
-    options[:checkboxes]
+      def checkboxes?
+        options[:checkboxes]
+      end
+    end
   end
 end
diff --git a/lib/datagrid/filters/extended_boolean_filter.rb b/lib/datagrid/filters/extended_boolean_filter.rb
index 521cf5e..1e2b96a 100644
--- a/lib/datagrid/filters/extended_boolean_filter.rb
+++ b/lib/datagrid/filters/extended_boolean_filter.rb
@@ -1,39 +1,45 @@
+# frozen_string_literal: true
+
 # @!visibility private
-class Datagrid::Filters::ExtendedBooleanFilter < Datagrid::Filters::EnumFilter
-  YES = "YES"
-  NO = "NO"
-  TRUTH_VALUES = [true, "true", "y", "yes"]
-  FALSE_VALUES = [false, "false", "n", "no"]
+module Datagrid
+  module Filters
+    class ExtendedBooleanFilter < Datagrid::Filters::EnumFilter
+      YES = "YES"
+      NO = "NO"
+      TRUTH_VALUES = [true, "true", "y", "yes"].freeze
+      FALSE_VALUES = [false, "false", "n", "no"].freeze
 
-  def initialize(report, attribute, options = {}, &block)
-    options[:select] = -> { boolean_select }
-    super(report, attribute, options, &block)
-  end
+      def initialize(report, attribute, options = {}, &block)
+        options[:select] = -> { boolean_select }
+        super(report, attribute, options, &block)
+      end
 
-  def execute(value, scope, grid_object)
-    value = value.blank? ? nil : ::Datagrid::Utils.booleanize(value)
-    super(value, scope, grid_object)
-  end
+      def execute(value, scope, grid_object)
+        value = value.blank? ? nil : ::Datagrid::Utils.booleanize(value)
+        super(value, scope, grid_object)
+      end
 
-  def parse(value)
-    value = value.downcase if value.is_a?(String)
-    case value
-    when *TRUTH_VALUES
-      YES
-    when *FALSE_VALUES
-      NO
-    when value.blank?
-      nil
-    else
-      super(value)
-    end
-  end
+      def parse(value)
+        value = value.downcase if value.is_a?(String)
+        case value
+        when *TRUTH_VALUES
+          YES
+        when *FALSE_VALUES
+          NO
+        when value.blank?
+          nil
+        else
+          super(value)
+        end
+      end
 
-  protected
+      protected
 
-  def boolean_select
-    [YES, NO].map do |key, _value|
-      [I18n.t("datagrid.filters.xboolean.#{key.downcase}"), key]
+      def boolean_select
+        [YES, NO].map do |key, _value|
+          [I18n.t("datagrid.filters.xboolean.#{key.downcase}"), key]
+        end
+      end
     end
   end
 end
diff --git a/lib/datagrid/filters/float_filter.rb b/lib/datagrid/filters/float_filter.rb
index 4361d60..d84f6e1 100644
--- a/lib/datagrid/filters/float_filter.rb
+++ b/lib/datagrid/filters/float_filter.rb
@@ -1,9 +1,15 @@
-class Datagrid::Filters::FloatFilter < Datagrid::Filters::BaseFilter
-  include Datagrid::Filters::RangedFilter
+# frozen_string_literal: true
 
-  def parse(value)
-    return nil if value.blank?
+module Datagrid
+  module Filters
+    class FloatFilter < Datagrid::Filters::BaseFilter
+      include Datagrid::Filters::RangedFilter
 
-    value.to_f
+      def parse(value)
+        return nil if value.blank?
+
+        value.to_f
+      end
+    end
   end
 end
diff --git a/lib/datagrid/filters/integer_filter.rb b/lib/datagrid/filters/integer_filter.rb
index e05a14e..6395b0b 100644
--- a/lib/datagrid/filters/integer_filter.rb
+++ b/lib/datagrid/filters/integer_filter.rb
@@ -1,16 +1,22 @@
+# frozen_string_literal: true
+
 require "datagrid/filters/ranged_filter"
 
-class Datagrid::Filters::IntegerFilter < Datagrid::Filters::BaseFilter
-  include Datagrid::Filters::RangedFilter
+module Datagrid
+  module Filters
+    class IntegerFilter < Datagrid::Filters::BaseFilter
+      include Datagrid::Filters::RangedFilter
 
-  def parse(value)
-    return nil if value.blank?
-    if defined?(ActiveRecord) && value.is_a?(ActiveRecord::Base) &&
-       value.respond_to?(:id) && value.id.is_a?(Integer)
-      return value.id
-    end
-    return value if value.is_a?(Range)
+      def parse(value)
+        return nil if value.blank?
+        if defined?(ActiveRecord) && value.is_a?(ActiveRecord::Base) &&
+           value.respond_to?(:id) && value.id.is_a?(Integer)
+          return value.id
+        end
+        return value if value.is_a?(Range)
 
-    value.to_i
+        value.to_i
+      end
+    end
   end
 end
diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index 05ac2c9..be2c683 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -1,48 +1,54 @@
-module Datagrid::Filters::RangedFilter
-  def initialize(grid, name, options, &block)
-    super(grid, name, options, &block)
-    return unless range?
+# frozen_string_literal: true
 
-    options[:multiple] = true
-  end
+module Datagrid
+  module Filters
+    module RangedFilter
+      def initialize(grid, name, options, &block)
+        super(grid, name, options, &block)
+        return unless range?
+
+        options[:multiple] = true
+      end
 
-  def parse_values(value)
-    result = super(value)
-    return result if !range? || result.nil?
-    # Simulate single point range
-    return [result, result] unless result.is_a?(Array)
+      def parse_values(value)
+        result = super(value)
+        return result if !range? || result.nil?
+        # Simulate single point range
+        return [result, result] unless result.is_a?(Array)
 
-    case result.size
-    when 0
-      nil
-    when 1
-      result.first
-    when 2
-      if result.first && result.last && result.first > result.last
-        # If wrong range is given - reverse it to be always valid
-        result.reverse
-      elsif !result.first && !result.last
-        nil
-      else
-        result
+        case result.size
+        when 0
+          nil
+        when 1
+          result.first
+        when 2
+          if result.first && result.last && result.first > result.last
+            # If wrong range is given - reverse it to be always valid
+            result.reverse
+          elsif !result.first && !result.last
+            nil
+          else
+            result
+          end
+        else
+          raise ArgumentError, "Can not create a date range from array of more than two: #{result.inspect}"
+        end
       end
-    else
-      raise ArgumentError, "Can not create a date range from array of more than two: #{result.inspect}"
-    end
-  end
 
-  def range?
-    options[:range]
-  end
+      def range?
+        options[:range]
+      end
 
-  def default_filter_where(scope, value)
-    if range? && value.is_a?(Array)
-      left, right = value
-      scope = driver.greater_equal(scope, name, left) if left
-      scope = driver.less_equal(scope, name, right) if right
-      scope
-    else
-      super(scope, value)
+      def default_filter_where(scope, value)
+        if range? && value.is_a?(Array)
+          left, right = value
+          scope = driver.greater_equal(scope, name, left) if left
+          scope = driver.less_equal(scope, name, right) if right
+          scope
+        else
+          super(scope, value)
+        end
+      end
     end
   end
 end
diff --git a/lib/datagrid/filters/select_options.rb b/lib/datagrid/filters/select_options.rb
index 68ae60b..75b2409 100644
--- a/lib/datagrid/filters/select_options.rb
+++ b/lib/datagrid/filters/select_options.rb
@@ -1,60 +1,66 @@
-module Datagrid::Filters::SelectOptions
-  def select(object)
-    select = options[:select]
-    if select.is_a?(Symbol)
-      object.send(select)
-    elsif select.respond_to?(:call)
-      Datagrid::Utils.apply_args(object, &select)
-    else
-      select
-    end
-  end
+# frozen_string_literal: true
 
-  def select_values(object)
-    options = select(object)
-    groups_used = grouped_choices?(options)
-    options.map do |option|
-      if groups_used
-        option[1].map { |o| option_text_and_value(o) }
-      else
-        option_text_and_value(option)
+module Datagrid
+  module Filters
+    module SelectOptions
+      def select(object)
+        select = options[:select]
+        if select.is_a?(Symbol)
+          object.send(select)
+        elsif select.respond_to?(:call)
+          Datagrid::Utils.apply_args(object, &select)
+        else
+          select
+        end
       end
-    end.map(&:last)
-  end
 
-  def include_blank
-    return if prompt
+      def select_values(object)
+        options = select(object)
+        groups_used = grouped_choices?(options)
+        options.map do |option|
+          if groups_used
+            option[1].map { |o| option_text_and_value(o) }
+          else
+            option_text_and_value(option)
+          end
+        end.map(&:last)
+      end
 
-    if options.has_key?(:include_blank)
-      Datagrid::Utils.callable(options[:include_blank])
-    else
-      !multiple?
-    end
-  end
+      def include_blank
+        return if prompt
 
-  def prompt
-    options.has_key?(:prompt) ? Datagrid::Utils.callable(options[:prompt]) : false
-  end
+        if options.key?(:include_blank)
+          Datagrid::Utils.callable(options[:include_blank])
+        else
+          !multiple?
+        end
+      end
 
-  protected
-
-  # Rails built-in method:
-  # https://github.com/rails/rails/blob/94e80269e36caf7b2d22a7ab68e6898d3a824122/actionview/lib/action_view/helpers/form_options_helper.rb#L789
-  # Code reuse is difficult, so it is easier to duplicate it
-  def option_text_and_value(option)
-    # Options are [text, value] pairs or strings used for both.
-    if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
-      option = option.reject { |e| e.is_a?(Hash) } if option.is_a?(Array)
-      [option.first, option.last]
-    else
-      [option, option]
-    end
-  end
+      def prompt
+        options.key?(:prompt) ? Datagrid::Utils.callable(options[:prompt]) : false
+      end
+
+      protected
 
-  # Rails built-in method:
-  # https://github.com/rails/rails/blob/f95c0b7e96eb36bc3efc0c5beffbb9e84ea664e4/actionview/lib/action_view/helpers/tags/select.rb#L36
-  # Code reuse is difficult, so it is easier to duplicate it
-  def grouped_choices?(choices)
-    !choices.blank? && choices.first.respond_to?(:last) && choices.first.last.is_a?(Array)
+      # Rails built-in method:
+      # https://github.com/rails/rails/blob/94e80269e36caf7b2d22a7ab68e6898d3a824122/actionview/lib/action_view/helpers/form_options_helper.rb#L789
+      # Code reuse is difficult, so it is easier to duplicate it
+      def option_text_and_value(option)
+        # Options are [text, value] pairs or strings used for both.
+        if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
+          option = option.reject { |e| e.is_a?(Hash) } if option.is_a?(Array)
+          [option.first, option.last]
+        else
+          [option, option]
+        end
+      end
+
+      # Rails built-in method:
+      # https://github.com/rails/rails/blob/f95c0b7e96eb36bc3efc0c5beffbb9e84ea664e4/actionview/lib/action_view/helpers/tags/select.rb#L36
+      # Code reuse is difficult, so it is easier to duplicate it
+      def grouped_choices?(choices)
+        !choices.blank? && choices.first.respond_to?(:last) && choices.first.last.is_a?(Array)
+      end
+    end
   end
 end
diff --git a/lib/datagrid/filters/string_filter.rb b/lib/datagrid/filters/string_filter.rb
index 35d20dd..a753efb 100644
--- a/lib/datagrid/filters/string_filter.rb
+++ b/lib/datagrid/filters/string_filter.rb
@@ -1,7 +1,13 @@
-class Datagrid::Filters::StringFilter < Datagrid::Filters::BaseFilter
-  include Datagrid::Filters::RangedFilter
+# frozen_string_literal: true
 
-  def parse(value)
-    value.nil? ? nil : value.to_s
+module Datagrid
+  module Filters
+    class StringFilter < Datagrid::Filters::BaseFilter
+      include Datagrid::Filters::RangedFilter
+
+      def parse(value)
+        value&.to_s
+      end
+    end
   end
 end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index a99888f..65347fe 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "action_view"
 
 module Datagrid
@@ -31,22 +33,23 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       filter = datagrid_get_filter(attribute_or_filter)
       options = add_filter_options(filter, **options)
       type = options.delete(:type)&.to_sym
-      if options.has_key?(:value) && options[:value].nil? && %i[datetime-local date].include?(type)
+      if options.key?(:value) && options[:value].nil? && %i[datetime-local date].include?(type)
         # https://github.com/rails/rails/pull/53387
         options[:value] = ""
       end
-      if type == :"datetime-local"
+      case type
+      when :"datetime-local"
         datetime_local_field filter.name, **options, &block
-      elsif type == :date
+      when :date
         date_field filter.name, **options, &block
-      elsif type == :textarea
+      when :textarea
         text_area filter.name, value: object.filter_value_as_string(filter), **options, &block
-      elsif type == :checkbox
+      when :checkbox
         # raise options.inspect
         check_box filter.name, options, options.fetch(:value, 1)
-      elsif type == :hidden
+      when :hidden
         hidden_field filter.name, **options
-      elsif type == :select
+      when :select
         select(
           filter.name,
           object.select_options(filter) || [],
@@ -218,7 +221,7 @@ def add_html_classes(options, *classes)
     end
 
     def partial_path(name)
-      if partials = options[:partials]
+      if (partials = options[:partials])
         partial_name = File.join(partials, name)
         # Second argument is []: no magical namespaces to lookup added from controller
         return partial_name if @template.lookup_context.template_exists?(partial_name, [], true)
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index 039bc13..6e16ac8 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "action_view"
 
 module Datagrid
diff --git a/lib/datagrid/ordering.rb b/lib/datagrid/ordering.rb
index 83577c6..cab6ee9 100644
--- a/lib/datagrid/ordering.rb
+++ b/lib/datagrid/ordering.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "datagrid/columns"
 
 module Datagrid
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index 3b23449..c838ed6 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "action_view"
 
 module Datagrid
@@ -16,7 +18,7 @@ def format_value(grid, column, asset)
 
       value = grid.html_value(column, @template, asset)
 
-      url = column.options[:url] && column.options[:url].call(asset)
+      url = column.options[:url]&.call(asset)
       if url
         @template.link_to(value, url)
       else
@@ -45,7 +47,7 @@ def table(grid, assets, **options)
     end
 
     def header(grid, options = {})
-      options[:order] = true unless options.has_key?(:order)
+      options[:order] = true unless options.key?(:order)
 
       _render_partial("head", options[:partials],
                       { grid: grid, options: options })
@@ -141,7 +143,7 @@ def to_s
       protected
 
       def method_missing(method, *args, &blk)
-        if column = @grid.column_by_name(method)
+        if (column = @grid.column_by_name(method))
           get(column)
         else
           super
diff --git a/lib/datagrid/rspec.rb b/lib/datagrid/rspec.rb
index 6b07ea7..8f78993 100644
--- a/lib/datagrid/rspec.rb
+++ b/lib/datagrid/rspec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "datagrid"
 
 # TODO: refactor this experimental shit
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index d6c6d23..89bdc73 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -1,126 +1,130 @@
+# frozen_string_literal: true
+
 require "rails/generators"
 
 # @!visibility private
-class Datagrid::Scaffold < Rails::Generators::NamedBase
-  include Rails::Generators::ResourceHelpers
-
-  check_class_collision suffix: "Grid"
-  source_root File.expand_path(__FILE__ + "/../../../templates")
-
-  def create_scaffold
-    template "base.rb.erb", base_grid_file unless file_exists?(base_grid_file)
-    template "grid.rb.erb", "app/grids/#{grid_class_name.underscore}.rb"
-    if file_exists?(grid_controller_file)
-      inject_into_file grid_controller_file, index_action, after: /class .*#{grid_controller_class_name}.*\n/
-    else
-      template "controller.rb.erb", grid_controller_file
-    end
-    template "index.html.erb", view_file
-    route(generate_routing_namespace("resources :#{grid_controller_short_name}"))
-    gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate)
-    in_root do
-      {
-        "css" => " *= require datagrid",
-        "css.sass" => " *= require datagrid",
-        "css.scss" => " *= require datagrid"
-      }.each do |extension, string|
-        file = "app/assets/stylesheets/application.#{extension}"
-        if file_exists?(file)
-          inject_into_file file, string + "\n", { before: /.*require_self/ } # before all
+module Datagrid
+  class Scaffold < Rails::Generators::NamedBase
+    include Rails::Generators::ResourceHelpers
+
+    check_class_collision suffix: "Grid"
+    source_root File.expand_path("/Users/bogdan/makabu/my/datagrid/lib/datagrid/scaffold.rb/../../../templates")
+
+    def create_scaffold
+      template "base.rb.erb", base_grid_file unless file_exists?(base_grid_file)
+      template "grid.rb.erb", "app/grids/#{grid_class_name.underscore}.rb"
+      if file_exists?(grid_controller_file)
+        inject_into_file grid_controller_file, index_action, after: /class .*#{grid_controller_class_name}.*\n/
+      else
+        template "controller.rb.erb", grid_controller_file
+      end
+      template "index.html.erb", view_file
+      route(generate_routing_namespace("resources :#{grid_controller_short_name}"))
+      gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate)
+      in_root do
+        {
+          "css" => " *= require datagrid",
+          "css.sass" => " *= require datagrid",
+          "css.scss" => " *= require datagrid"
+        }.each do |extension, string|
+          file = "app/assets/stylesheets/application.#{extension}"
+          if file_exists?(file)
+            inject_into_file file, "#{string}\n", { before: /.*require_self/ } # before all
+          end
         end
       end
     end
-  end
 
-  def view_file
-    Rails.root.join("app/views").join(controller_file_path).join("index.html.erb")
-  end
+    def view_file
+      Rails.root.join("app/views").join(controller_file_path).join("index.html.erb")
+    end
 
-  def grid_class_name
-    file_name.camelize.pluralize + "Grid"
-  end
+    def grid_class_name
+      "#{file_name.camelize.pluralize}Grid"
+    end
 
-  def grid_controller_class_name
-    controller_class_name.camelize + "Controller"
-  end
+    def grid_controller_class_name
+      "#{controller_class_name.camelize}Controller"
+    end
 
-  def grid_controller_file
-    Rails.root.join("app/controllers").join("#{grid_controller_class_name.underscore}.rb")
-  end
+    def grid_controller_file
+      Rails.root.join("app/controllers").join("#{grid_controller_class_name.underscore}.rb")
+    end
 
-  def grid_controller_short_name
-    controller_file_name
-  end
+    def grid_controller_short_name
+      controller_file_name
+    end
 
-  def grid_model_name
-    file_name.camelize.singularize
-  end
+    def grid_model_name
+      file_name.camelize.singularize
+    end
 
-  def grid_param_name
-    grid_class_name.underscore
-  end
+    def grid_param_name
+      grid_class_name.underscore
+    end
 
-  def pagination_helper_code
-    if defined?(::WillPaginate)
-      "will_paginate(@grid.assets)"
-    else
-      # Kaminari is default
-      "paginate(@grid.assets)"
+    def pagination_helper_code
+      if defined?(::WillPaginate)
+        "will_paginate(@grid.assets)"
+      else
+        # Kaminari is default
+        "paginate(@grid.assets)"
+      end
     end
-  end
 
-  def base_grid_file
-    "app/grids/base_grid.rb"
-  end
+    def base_grid_file
+      "app/grids/base_grid.rb"
+    end
 
-  def grid_route_name
-    controller_class_name.underscore.gsub("/", "_") + "_path"
-  end
+    def grid_route_name
+      "#{controller_class_name.underscore.gsub('/', '_')}_path"
+    end
 
-  def index_action
-    indent(<<~RUBY)
-      def index
-        @grid = #{grid_class_name}.new(grid_params) do |scope|
-          scope.page(params[:page])
+    def index_action
+      indent(<<~RUBY)
+        def index
+          @grid = #{grid_class_name}.new(grid_params) do |scope|
+            scope.page(params[:page])
+          end
         end
-      end
 
-      protected
+        protected
 
-      def grid_params
-        params.fetch(:#{grid_param_name}, {}).permit!
-      end
-    RUBY
-  end
+        def grid_params
+          params.fetch(:#{grid_param_name}, {}).permit!
+        end
+      RUBY
+    end
 
-  protected
-
-  def generate_routing_namespace(code)
-    depth = regular_class_path.length
-    # Create 'namespace' ladder
-    # namespace :foo do
-    #   namespace :bar do
-    namespace_ladder = regular_class_path.each_with_index.map do |ns, i|
-      indent("namespace :#{ns} do\n", i * 2)
-    end.join
-
-    # Create route
-    #     get 'baz/index'
-    route = indent(code, depth * 2)
-
-    # Create `end` ladder
-    #   end
-    # end
-    end_ladder = (1..depth).reverse_each.map do |i|
-      indent("end\n", i * 2)
-    end.join
-
-    # Combine the 3 parts to generate complete route entry
-    namespace_ladder + route + "\n" + end_ladder
-  end
+    protected
+
+    def generate_routing_namespace(code)
+      depth = regular_class_path.length
+      # Create 'namespace' ladder
+      # namespace :foo do
+      #   namespace :bar do
+      namespace_ladder = regular_class_path.each_with_index.map do |ns, i|
+        indent("namespace :#{ns} do\n", i * 2)
+      end.join
+
+      # Create route
+      #     get 'baz/index'
+      route = indent(code, depth * 2)
+
+      # Create `end` ladder
+      #   end
+      # end
+      end_ladder = (1..depth).reverse_each.map do |i|
+        indent("end\n", i * 2)
+      end.join
+
+      # Combine the 3 parts to generate complete route entry
+      "#{namespace_ladder}#{route}\n#{end_ladder}"
+    end
 
-  def file_exists?(name)
-    name = Rails.root.join(name) unless name.to_s.first == "/"
-    File.exist?(name)
+    def file_exists?(name)
+      name = Rails.root.join(name) unless name.to_s.first == "/"
+      File.exist?(name)
+    end
   end
 end
diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index 254ab02..3a2a18d 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
 module Datagrid
   # @!visibility private
   module Utils
     class << self
-      TRUTH = [true, 1, "1", "true", "yes", "on"]
+      TRUTH = [true, 1, "1", "true", "yes", "on"].freeze
 
       def booleanize(value)
         value = value.downcase if value.respond_to?(:downcase)
@@ -65,7 +67,7 @@ def extract_position_from_options(array, options)
       end
 
       def apply_args(*args, &block)
-        size = block.arity < 0 ? args.size : block.arity
+        size = block.arity.negative? ? args.size : block.arity
         block.call(*args.slice(0, size))
       end
 
diff --git a/lib/datagrid/version.rb b/lib/datagrid/version.rb
index 962d03c..12d0cd3 100644
--- a/lib/datagrid/version.rb
+++ b/lib/datagrid/version.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   VERSION = "1.8.4"
 end
diff --git a/lib/tasks/datagrid_tasks.rake b/lib/tasks/datagrid_tasks.rake
index 27eb792..e1ca194 100644
--- a/lib/tasks/datagrid_tasks.rake
+++ b/lib/tasks/datagrid_tasks.rake
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 namespace :datagrid do
   desc "Copy table partials into rails application"
   task :copy_partials do
     require "fileutils"
     views_path = "app/views/datagrid"
     destination_dir = (Rails.root + views_path).to_s
-    pattern = File.expand_path(File.dirname(__FILE__) + "/../../#{views_path}") + "/*"
+    pattern = "#{File.expand_path(File.dirname(__FILE__) + "/../../#{views_path}")}/*"
     Dir[pattern].each do |template|
       puts "* copy #{template} => #{destination_dir}"
       FileUtils.mkdir_p destination_dir
diff --git a/spec/datagrid/active_model_spec.rb b/spec/datagrid/active_model_spec.rb
index b42adbc..a0d8502 100644
--- a/spec/datagrid/active_model_spec.rb
+++ b/spec/datagrid/active_model_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::ActiveModel do
diff --git a/spec/datagrid/column_names_attribute_spec.rb b/spec/datagrid/column_names_attribute_spec.rb
index 321cc5f..1cb04d9 100644
--- a/spec/datagrid/column_names_attribute_spec.rb
+++ b/spec/datagrid/column_names_attribute_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::ColumnNamesAttribute do
diff --git a/spec/datagrid/columns/column_spec.rb b/spec/datagrid/columns/column_spec.rb
index 086ecbc..a2fe70e 100644
--- a/spec/datagrid/columns/column_spec.rb
+++ b/spec/datagrid/columns/column_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Columns::Column do
diff --git a/spec/datagrid/columns_spec.rb b/spec/datagrid/columns_spec.rb
index c644800..13d0378 100644
--- a/spec/datagrid/columns_spec.rb
+++ b/spec/datagrid/columns_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Columns do
diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index e5c5d47..40c4f47 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 require "action_controller/metal/strong_parameters"
 
diff --git a/spec/datagrid/drivers/active_record_spec.rb b/spec/datagrid/drivers/active_record_spec.rb
index c41a1cf..fbc6c06 100644
--- a/spec/datagrid/drivers/active_record_spec.rb
+++ b/spec/datagrid/drivers/active_record_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Drivers::ActiveRecord do
diff --git a/spec/datagrid/drivers/array_spec.rb b/spec/datagrid/drivers/array_spec.rb
index c0263f9..5fdaa2c 100644
--- a/spec/datagrid/drivers/array_spec.rb
+++ b/spec/datagrid/drivers/array_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Drivers::Array do
@@ -11,7 +13,7 @@
 
   describe "api" do
     class ArrayGrid
-      class User < Struct.new(:name, :age); end
+      User = Struct.new(:name, :age)
       include Datagrid
       scope do
         []
diff --git a/spec/datagrid/drivers/mongo_mapper_spec.rb b/spec/datagrid/drivers/mongo_mapper_spec.rb
index 6cacf76..2deec97 100644
--- a/spec/datagrid/drivers/mongo_mapper_spec.rb
+++ b/spec/datagrid/drivers/mongo_mapper_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Drivers::MongoMapper, :mongomapper do
diff --git a/spec/datagrid/drivers/mongoid_spec.rb b/spec/datagrid/drivers/mongoid_spec.rb
index b119951..1f9646e 100644
--- a/spec/datagrid/drivers/mongoid_spec.rb
+++ b/spec/datagrid/drivers/mongoid_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Drivers::Mongoid, :mongoid do
diff --git a/spec/datagrid/drivers/sequel_spec.rb b/spec/datagrid/drivers/sequel_spec.rb
index 731b19d..e95165b 100644
--- a/spec/datagrid/drivers/sequel_spec.rb
+++ b/spec/datagrid/drivers/sequel_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Drivers::Sequel do
diff --git a/spec/datagrid/filters/base_filter_spec.rb b/spec/datagrid/filters/base_filter_spec.rb
index 44160da..fc60ca1 100644
--- a/spec/datagrid/filters/base_filter_spec.rb
+++ b/spec/datagrid/filters/base_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::BaseFilter do
diff --git a/spec/datagrid/filters/composite_filters_spec.rb b/spec/datagrid/filters/composite_filters_spec.rb
index b40e2a4..3a0e96c 100644
--- a/spec/datagrid/filters/composite_filters_spec.rb
+++ b/spec/datagrid/filters/composite_filters_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::CompositeFilters do
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 56fd5ce..865b61d 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 require "active_support/testing/time_helpers"
 
diff --git a/spec/datagrid/filters/date_time_filter_spec.rb b/spec/datagrid/filters/date_time_filter_spec.rb
index 425b442..b0d1627 100644
--- a/spec/datagrid/filters/date_time_filter_spec.rb
+++ b/spec/datagrid/filters/date_time_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::DateTimeFilter do
diff --git a/spec/datagrid/filters/dynamic_filter_spec.rb b/spec/datagrid/filters/dynamic_filter_spec.rb
index 6bbfa7c..149a461 100644
--- a/spec/datagrid/filters/dynamic_filter_spec.rb
+++ b/spec/datagrid/filters/dynamic_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::DynamicFilter do
diff --git a/spec/datagrid/filters/enum_filter_spec.rb b/spec/datagrid/filters/enum_filter_spec.rb
index 7192614..6ea8ec7 100644
--- a/spec/datagrid/filters/enum_filter_spec.rb
+++ b/spec/datagrid/filters/enum_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::EnumFilter do
@@ -20,7 +22,7 @@
   it "should support select option as proc with instace input" do
     klass = test_report do
       scope { Entry }
-      filter(:group_id, :enum, select: proc { |obj| obj.object_id })
+      filter(:group_id, :enum, select: proc(&:object_id))
     end.class
     instance = klass.new
     expect(klass.filter_by_name(:group_id).select(instance)).to eq(instance.object_id)
diff --git a/spec/datagrid/filters/extended_boolean_filter_spec.rb b/spec/datagrid/filters/extended_boolean_filter_spec.rb
index ebff604..f70f6da 100644
--- a/spec/datagrid/filters/extended_boolean_filter_spec.rb
+++ b/spec/datagrid/filters/extended_boolean_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::ExtendedBooleanFilter do
diff --git a/spec/datagrid/filters/float_filter_spec.rb b/spec/datagrid/filters/float_filter_spec.rb
index 131cdd0..e45066f 100644
--- a/spec/datagrid/filters/float_filter_spec.rb
+++ b/spec/datagrid/filters/float_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::FloatFilter do
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index a7f1855..fb6f5d7 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::IntegerFilter do
diff --git a/spec/datagrid/filters/string_filter_spec.rb b/spec/datagrid/filters/string_filter_spec.rb
index 8e84de3..c55fb58 100644
--- a/spec/datagrid/filters/string_filter_spec.rb
+++ b/spec/datagrid/filters/string_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters::StringFilter do
diff --git a/spec/datagrid/filters_spec.rb b/spec/datagrid/filters_spec.rb
index 91614dc..7f335e9 100644
--- a/spec/datagrid/filters_spec.rb
+++ b/spec/datagrid/filters_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Filters do
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index dac013b..d42acce 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 require "action_controller"
 
@@ -19,7 +21,7 @@ class MyTemplate
 
   describe ".datagrid_filter" do
     it "should work for every filter type" do
-      Datagrid::Filters::FILTER_TYPES.each do |_type, klass|
+      Datagrid::Filters::FILTER_TYPES.each_value do |klass|
         expect(Datagrid::FormBuilder.instance_methods.map(&:to_sym)).to include(klass.form_builder_helper_name)
       end
     end
@@ -180,8 +182,8 @@ class MyTemplate
         let(:_range) { [1, 2] }
         it {
           should equal_to_dom(
-            '<input value="1" id="from_hello" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
-            '<span class="separator integer"> - </span>' +
+            '<input value="1" id="from_hello" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' \
+            '<span class="separator integer"> - </span>' \
             '<input value="2" id="to_hello" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -190,8 +192,8 @@ class MyTemplate
         let(:_range) { [10, nil] }
         it {
           should equal_to_dom(
-            '<input value="10" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
-            '<span class="separator integer"> - </span>' +
+            '<input value="10" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' \
+            '<span class="separator integer"> - </span>' \
             '<input class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -201,8 +203,8 @@ class MyTemplate
         let(:_range) { [nil, 10] }
         it {
           should equal_to_dom(
-            '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
-            '<span class="separator integer"> - </span>' +
+            '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' \
+            '<span class="separator integer"> - </span>' \
             '<input value="10" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -213,8 +215,8 @@ class MyTemplate
         let(:_range) { 2..1 }
         it {
           should equal_to_dom(
-            '<input value="1" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' +
-            '<span class="separator integer"> - </span>' +
+            '<input value="1" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' \
+            '<span class="separator integer"> - </span>' \
             '<input value="2" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -252,8 +254,8 @@ class MyTemplate
       let(:_range) { [1.5, 2.5] }
       it {
         should equal_to_dom(
-          '<input value="1.5" class="from rating float_filter" multiple type="text" name="report[rating][]"/>' +
-          '<span class="separator float"> - </span>' +
+          '<input value="1.5" class="from rating float_filter" multiple type="text" name="report[rating][]"/>' \
+          '<span class="separator float"> - </span>' \
           '<input value="2.5" class="to rating float_filter" multiple type="text" name="report[rating][]"/>'
         )
       }
@@ -271,8 +273,8 @@ class MyTemplate
         let(:_range) { ["2012-01-03", nil] }
         it {
           should equal_to_dom(
-            '<input value="2012-01-03" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-            '<span class="separator date"> - </span>' +
+            '<input value="2012-01-03" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<span class="separator date"> - </span>' \
             '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
           )
         }
@@ -287,8 +289,8 @@ class MyTemplate
         let(:_range) { ["2013/01/01", "2013/02/02"] }
         it {
           should equal_to_dom(
-            '<input value="01/01/2013" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-            '<span class="separator date"> - </span>' +
+            '<input value="01/01/2013" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<span class="separator date"> - </span>' \
             '<input value="02/02/2013" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
           )
         }
@@ -297,8 +299,8 @@ class MyTemplate
         let(:_range) { [nil, "2012-01-03"] }
         it {
           should equal_to_dom(
-            '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-            '<span class="separator date"> - </span>' +
+            '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<span class="separator date"> - </span>' \
             '<input value="2012-01-03" class="to created_at date_filter" multiple type="text"  name="report[created_at][]"/>'
           )
         }
@@ -309,8 +311,8 @@ class MyTemplate
         let(:_range) { Date.parse("2012-01-02")..Date.parse("2012-01-01") }
         it {
           should equal_to_dom(
-            '<input value="2012-01-01" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-            '<span class="separator date"> - </span>' +
+            '<input value="2012-01-01" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<span class="separator date"> - </span>' \
             '<input value="2012-01-02" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
           )
         }
@@ -324,8 +326,8 @@ class MyTemplate
         let(:_range) { [nil, nil] }
         it {
           should equal_to_dom(
-            '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' +
-            '<span class="separator date"> - </span>' +
+            '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<span class="separator date"> - </span>' \
             '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
           )
         }
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index adc8efa..bd9a871 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 require "active_support/core_ext/hash"
 require "active_support/core_ext/object"
@@ -193,7 +195,7 @@ class TestGrid
     it "should support urls" do
       rp = test_report do
         scope { Entry }
-        column(:name, url: ->(model) { model.name })
+        column(:name, url: lambda(&:name))
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
         "tr td.name a[href=Star]" => "Star"
@@ -560,7 +562,7 @@ def param_name
     end
     it "should provide an interator" do
       r = subject.datagrid_row(grid, entry)
-      expect(r.map { |z| z.upcase }).to eq(%w[HELLO GREETINGS])
+      expect(r.map(&:upcase)).to eq(%w[HELLO GREETINGS])
       expect(r.name).to eq("Hello")
       expect(r.category).to eq("greetings")
     end
diff --git a/spec/datagrid/ordering_spec.rb b/spec/datagrid/ordering_spec.rb
index 701e6b0..028a448 100644
--- a/spec/datagrid/ordering_spec.rb
+++ b/spec/datagrid/ordering_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Ordering do
diff --git a/spec/datagrid/scaffold_spec.rb b/spec/datagrid/scaffold_spec.rb
index 9bab5fc..6c1bd16 100644
--- a/spec/datagrid/scaffold_spec.rb
+++ b/spec/datagrid/scaffold_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Scaffold do
diff --git a/spec/datagrid/stylesheet_spec.rb b/spec/datagrid/stylesheet_spec.rb
index 0fc65ce..c8888a1 100644
--- a/spec/datagrid/stylesheet_spec.rb
+++ b/spec/datagrid/stylesheet_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe "Datagrid stylesheet" do
diff --git a/spec/datagrid/utils_spec.rb b/spec/datagrid/utils_spec.rb
index f66a495..1128edb 100644
--- a/spec/datagrid/utils_spec.rb
+++ b/spec/datagrid/utils_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 
 describe Datagrid::Utils do
diff --git a/spec/datagrid_spec.rb b/spec/datagrid_spec.rb
index 3b04da1..b05b6f4 100644
--- a/spec/datagrid_spec.rb
+++ b/spec/datagrid_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "spec_helper"
 require "datagrid/rspec"
 
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 995e21f..a5560d5 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "rubygems"
 require "bundler"
 
@@ -110,4 +112,4 @@ def action_view_template
 
 # Requires supporting files with custom matchers and macros, etc,
 # in ./support/ and its subdirectories.
-Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
+Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
diff --git a/spec/support/active_record.rb b/spec/support/active_record.rb
index 3a9a2c4..d20aa2a 100644
--- a/spec/support/active_record.rb
+++ b/spec/support/active_record.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "sqlite3"
 
 ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
diff --git a/spec/support/configuration.rb b/spec/support/configuration.rb
index 07e39a1..c99d4e4 100644
--- a/spec/support/configuration.rb
+++ b/spec/support/configuration.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 def with_date_format(format = "%m/%d/%Y")
   old_format = Datagrid.configuration.date_formats
   Datagrid.configure do |config|
diff --git a/spec/support/i18n_helpers.rb b/spec/support/i18n_helpers.rb
index 9a52846..5ae24ad 100644
--- a/spec/support/i18n_helpers.rb
+++ b/spec/support/i18n_helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 def store_translations(locale, translations)
   I18n.backend.store_translations locale, translations
   yield
diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb
index 3a84ce6..745b40e 100644
--- a/spec/support/matchers.rb
+++ b/spec/support/matchers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "rails/dom/testing"
 
 def equal_to_dom(text)
@@ -55,19 +57,20 @@ def matches?(text)
     @matcher = Nokogiri::HTML::DocumentFragment.parse(text)
     @css_pattern.each do |css, amount_or_pattern_or_string_or_proc|
       path = @matcher.css(css)
-      if amount_or_pattern_or_string_or_proc.is_a?(String) or amount_or_pattern_or_string_or_proc.is_a?(Regexp)
+      case amount_or_pattern_or_string_or_proc
+      when String, Regexp
         pattern_or_string = amount_or_pattern_or_string_or_proc
         html = path.inner_html
         unless html.match(pattern_or_string)
           return error!("#{css.inspect} did not match #{pattern_or_string.inspect}. It was \n:#{html.inspect}")
         end
-      elsif amount_or_pattern_or_string_or_proc.is_a? Numeric
+      when Numeric
         expected_amount = amount_or_pattern_or_string_or_proc
         amount = path.size
         if amount != expected_amount
           return error!("did not find #{css.inspect} #{expected_amount.inspect} times. It was #{amount.inspect}")
         end
-      elsif amount_or_pattern_or_string_or_proc.is_a? Proc
+      when Proc
         unless amount_or_pattern_or_string_or_proc.call(path)
           return error!("#{css.inspect} did not validate (proc must not return a falsy value)")
         end
diff --git a/spec/support/mongo_mapper.rb b/spec/support/mongo_mapper.rb
index 5876028..2d381df 100644
--- a/spec/support/mongo_mapper.rb
+++ b/spec/support/mongo_mapper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 if defined?(MongoMapper)
 
   class MongoMapperEntry
diff --git a/spec/support/mongoid.rb b/spec/support/mongoid.rb
index 014c008..878a20c 100644
--- a/spec/support/mongoid.rb
+++ b/spec/support/mongoid.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "rubygems"
 
 # Mongoid.logger = TEST_LOGGER #TODO: understand why still output to STDOUT
diff --git a/spec/support/sequel.rb b/spec/support/sequel.rb
index 5130fc9..77da337 100644
--- a/spec/support/sequel.rb
+++ b/spec/support/sequel.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require "sequel"
 
 DB = Sequel.sqlite # memory database
diff --git a/spec/support/simple_report.rb b/spec/support/simple_report.rb
index 57e553a..ed5afab 100644
--- a/spec/support/simple_report.rb
+++ b/spec/support/simple_report.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 def test_report(attributes = {}, &block)
   klass = test_report_class(&block)
   klass.new(attributes)
@@ -35,9 +37,7 @@ class SimpleReport
     group.name
   end
 
-  column(:name) do |user|
-    user.name
-  end
+  column(:name, &:name)
 
   column(:actions, html: true) do |model|
     render partial: "/actions", locals: { model: model }

From f9fe54f4761d2d0a404f8308bb5d3d2ba3d78d03 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 19:43:42 +0100
Subject: [PATCH 010/157] Rubocop fixes

---
 .rubocop.yml                         | 10 ++++++++++
 lib/datagrid/filters/float_filter.rb |  1 +
 spec/datagrid/form_builder_spec.rb   |  4 +++-
 spec/spec_helper.rb                  |  9 +--------
 spec/support/active_record.rb        | 12 ++++++------
 5 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index e5ccac6..11ed854 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,3 +1,13 @@
 # inherit_from: .rubocop_todo.yml
 Style/StringLiterals:
   EnforcedStyle: double_quotes
+Metrics/BlockLength:
+  Enabled: false
+Metrics/MethodLength:
+  Enabled: false
+Metrics/AbcSize:
+  Enabled: false
+Lint/ConstantDefinitionInBlock:
+  Enabled: false
+Metrics/CyclomaticComplexity:
+  Enabled: false
diff --git a/lib/datagrid/filters/float_filter.rb b/lib/datagrid/filters/float_filter.rb
index d84f6e1..6cc4aa9 100644
--- a/lib/datagrid/filters/float_filter.rb
+++ b/lib/datagrid/filters/float_filter.rb
@@ -1,5 +1,6 @@
 # frozen_string_literal: true
 
+# @!visibility private
 module Datagrid
   module Filters
     class FloatFilter < Datagrid::Filters::BaseFilter
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index d42acce..c8f7a83 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -495,7 +495,9 @@ class MyTemplate
         let(:_filter) { :name }
 
         it {
-          should equal_to_dom('<input value="one,two" class="name string_filter" type="text" name="report[name]" id="report_name">')
+          should equal_to_dom(
+            '<input value="one,two" class="name string_filter" type="text" name="report[name]" id="report_name">'
+          )
         }
       end
     end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index a5560d5..9ddfab4 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -18,16 +18,9 @@
 require "action_view"
 require "rails"
 require "mongoid"
-begin
-  require "mongo_mapper"
-rescue LoadError
-end
 
 require "datagrid"
-begin
-  require "ruby-debug"
-rescue LoadError
-end
+require "debug"
 require "rspec"
 require "logger"
 
diff --git a/spec/support/active_record.rb b/spec/support/active_record.rb
index d20aa2a..d248295 100644
--- a/spec/support/active_record.rb
+++ b/spec/support/active_record.rb
@@ -26,12 +26,12 @@
     t.float :rating
     t.timestamps
   end
+end
 
-  class ::Entry < ActiveRecord::Base
-    belongs_to :group
-  end
+class ::Entry < ActiveRecord::Base
+  belongs_to :group
+end
 
-  class ::Group < ActiveRecord::Base
-    has_many :entries
-  end
+class ::Group < ActiveRecord::Base
+  has_many :entries
 end

From dd944db3e5426ac65a0f443837e5af2f9af47d90 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 19:51:27 +0100
Subject: [PATCH 011/157] Rubocop

---
 .rubocop.yml                                    |  3 ++-
 datagrid.gemspec                                |  3 ++-
 lib/datagrid/columns.rb                         |  4 ++--
 lib/datagrid/core.rb                            |  4 ++--
 lib/datagrid/drivers/abstract_driver.rb         |  2 +-
 lib/datagrid/filters.rb                         |  6 +++---
 lib/datagrid/filters/date_filter.rb             |  4 ++--
 lib/datagrid/filters/enum_filter.rb             |  2 +-
 lib/datagrid/filters/extended_boolean_filter.rb |  6 +++---
 lib/datagrid/filters/ranged_filter.rb           |  6 +++---
 spec/datagrid/columns_spec.rb                   |  2 +-
 spec/datagrid/core_spec.rb                      |  2 +-
 spec/datagrid/form_builder_spec.rb              | 16 ++++++++++++----
 spec/datagrid/helper_spec.rb                    |  6 +++---
 spec/spec_helper.rb                             |  4 ++--
 15 files changed, 40 insertions(+), 30 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index 11ed854..8560090 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,4 +1,5 @@
-# inherit_from: .rubocop_todo.yml
+AllCops:
+  NewCops: enable
 Style/StringLiterals:
   EnforcedStyle: double_quotes
 Metrics/BlockLength:
diff --git a/datagrid.gemspec b/datagrid.gemspec
index 662bdee..49add80 100644
--- a/datagrid.gemspec
+++ b/datagrid.gemspec
@@ -29,7 +29,8 @@ Gem::Specification.new do |s|
     "bug_tracker_uri" => "#{s.homepage}/issues",
     "documentation_uri" => "#{s.homepage}/wiki",
     "changelog_uri" => "#{s.homepage}/blob/master/CHANGELOG.md",
-    "source_code_uri" => s.homepage
+    "source_code_uri" => s.homepage,
+    "rubygems_mfa_required" => "true"
   }
 
   s.add_dependency "railties", ">= 6.1"
diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index a388ab5..81b3ec9 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -148,13 +148,13 @@ def decorate(model = nil, &block)
         return model unless decorator
 
         presenter = ::Datagrid::Utils.apply_args(model, &decorator)
-        presenter = presenter.is_a?(Class) ? presenter.new(model) : presenter
+        presenter = presenter.new(model) if presenter.is_a?(Class)
         block_given? ? yield(presenter) : presenter
       end
 
       # @!visibility private
       def inherited(child_class)
-        super(child_class)
+        super
         child_class.columns_array = columns_array.clone
       end
 
diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index 4fc1166..44df516 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -117,7 +117,7 @@ def check_scope_defined!(message = nil)
       end
 
       def inherited(child_class)
-        super(child_class)
+        super
         child_class.datagrid_attributes = datagrid_attributes.clone
       end
     end
@@ -266,7 +266,7 @@ def reset
     protected
 
     def sanitize_for_mass_assignment(attributes)
-      forbidden_attributes_protection ? super(attributes) : attributes
+      forbidden_attributes_protection ? super : attributes
     end
   end
 end
diff --git a/lib/datagrid/drivers/abstract_driver.rb b/lib/datagrid/drivers/abstract_driver.rb
index 8807c1b..90f5038 100644
--- a/lib/datagrid/drivers/abstract_driver.rb
+++ b/lib/datagrid/drivers/abstract_driver.rb
@@ -9,7 +9,7 @@ class AbstractDriver
       class_attribute :subclasses, default: []
 
       def self.inherited(base)
-        super(base)
+        super
         subclasses << base
       end
 
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index ecca158..4a3d31e 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -127,7 +127,7 @@ def filters
       protected
 
       def inherited(child_class)
-        super(child_class)
+        super
         child_class.filters_array = filters_array.clone
       end
 
@@ -141,12 +141,12 @@ def filters_inspection
     end
 
     # @!visibility private
-    def initialize(*args, &block)
+    def initialize(...)
       self.filters_array = self.class.filters_array.clone
       filters_array.each do |filter|
         self[filter.name] = filter.default(self)
       end
-      super(*args, &block)
+      super
     end
 
     # @!visibility private
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index 170e249..2a7d29d 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -9,7 +9,7 @@ class DateFilter < Datagrid::Filters::BaseFilter
 
       def apply(grid_object, scope, value)
         value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
-        super(grid_object, scope, value)
+        super
       end
 
       def parse(value)
@@ -26,7 +26,7 @@ def format(value)
 
       def default_filter_where(scope, value)
         value = Datagrid::Utils.format_date_as_timestamp(value) if driver.is_timestamp?(scope, name)
-        super(scope, value)
+        super
       end
 
       protected
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index d16f7c1..6e5a12b 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -8,7 +8,7 @@ class EnumFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::SelectOptions
 
       def initialize(*args)
-        super(*args)
+        super
         options[:multiple] = true if checkboxes?
         raise Datagrid::ConfigurationError, ":select option not specified" unless options[:select]
       end
diff --git a/lib/datagrid/filters/extended_boolean_filter.rb b/lib/datagrid/filters/extended_boolean_filter.rb
index 1e2b96a..5cf418a 100644
--- a/lib/datagrid/filters/extended_boolean_filter.rb
+++ b/lib/datagrid/filters/extended_boolean_filter.rb
@@ -11,12 +11,12 @@ class ExtendedBooleanFilter < Datagrid::Filters::EnumFilter
 
       def initialize(report, attribute, options = {}, &block)
         options[:select] = -> { boolean_select }
-        super(report, attribute, options, &block)
+        super
       end
 
       def execute(value, scope, grid_object)
         value = value.blank? ? nil : ::Datagrid::Utils.booleanize(value)
-        super(value, scope, grid_object)
+        super
       end
 
       def parse(value)
@@ -29,7 +29,7 @@ def parse(value)
         when value.blank?
           nil
         else
-          super(value)
+          super
         end
       end
 
diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index be2c683..9fa95d2 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -4,14 +4,14 @@ module Datagrid
   module Filters
     module RangedFilter
       def initialize(grid, name, options, &block)
-        super(grid, name, options, &block)
+        super
         return unless range?
 
         options[:multiple] = true
       end
 
       def parse_values(value)
-        result = super(value)
+        result = super
         return result if !range? || result.nil?
         # Simulate single point range
         return [result, result] unless result.is_a?(Array)
@@ -46,7 +46,7 @@ def default_filter_where(scope, value)
           scope = driver.less_equal(scope, name, right) if right
           scope
         else
-          super(scope, value)
+          super
         end
       end
     end
diff --git a/spec/datagrid/columns_spec.rb b/spec/datagrid/columns_spec.rb
index 13d0378..b7f90cc 100644
--- a/spec/datagrid/columns_spec.rb
+++ b/spec/datagrid/columns_spec.rb
@@ -87,7 +87,7 @@ class Report27
           column(:name)
         end
 
-        store_translations(:en, datagrid: { "report27": { columns: { name: "Nombre" } } }) do
+        store_translations(:en, datagrid: { report27: { columns: { name: "Nombre" } } }) do
           expect(Report27.new.header.first).to eq("Nombre")
         end
       end
diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index 40c4f47..14626ba 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -203,7 +203,7 @@ class EqualTest
 
   describe "ActionController::Parameters" do
     let(:params) do
-      ::ActionController::Parameters.new(name: "one")
+      ActionController::Parameters.new(name: "one")
     end
 
     it "permites all attributes by default" do
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index c8f7a83..0b5004f 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -427,9 +427,15 @@ class MyTemplate
         it {
           should equal_to_dom(
             '
-<label class="category enum_filter checkboxes" for="report_category_first"><input class="category enum_filter checkboxes" type="checkbox" id="report_category_first" value="first" name="report[category][]" />first</label>
-<label class="category enum_filter checkboxes" for="report_category_second"><input class="category enum_filter checkboxes" type="checkbox" id="report_category_second" value="second" name="report[category][]" />second</label>
-          '
+<label class="category enum_filter checkboxes" for="report_category_first">
+<input class="category enum_filter checkboxes" type="checkbox" id="report_category_first" value="first" name="report[category][]" />
+first
+</label>
+<label class="category enum_filter checkboxes" for="report_category_second">
+<input class="category enum_filter checkboxes" type="checkbox" id="report_category_second" value="second" name="report[category][]" />
+second
+</label>
+'
           )
         }
 
@@ -451,7 +457,9 @@ class MyTemplate
       it {
         should equal_to_dom(
           # hidden is important when default is set to true
-          %(<input name="report[disabled]" type="hidden" value="0" autocomplete="off"><input class="disabled boolean_filter" type="checkbox" value="1" checked name="report[disabled]" id="report_disabled">)
+          %(<input name="report[disabled]" type="hidden" value="0" autocomplete="off">
+          <input class="disabled boolean_filter" type="checkbox" value="1"
+             checked name="report[disabled]" id="report_disabled">)
         )
       }
     end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index bd9a871..04fd4bf 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -62,7 +62,7 @@ class TestGrid
           column(:id)
         end
       end
-      expect(subject.datagrid_table(::Ns23::TestGrid.new)).to match_css_pattern(
+      expect(subject.datagrid_table(Ns23::TestGrid.new)).to match_css_pattern(
         "table.datagrid.ns23_test_grid" => 1
       )
     end
@@ -497,7 +497,7 @@ class TestGrid
           filter(:id)
         end
       end
-      expect(subject.datagrid_form_for(::Ns22::TestGrid.new, url: "grid")).to match_css_pattern(
+      expect(subject.datagrid_form_for(Ns22::TestGrid.new, url: "grid")).to match_css_pattern(
         "form.datagrid-form.ns22_test_grid" => 1,
         "form.datagrid-form label[for=ns22_test_grid_id]" => 1,
         "form.datagrid-form input#ns22_test_grid_id[name='ns22_test_grid[id]']" => 1
@@ -513,7 +513,7 @@ def param_name
           "g"
         end
       end
-      expect(subject.datagrid_form_for(::ParamNameGrid81.new, url: "/grid")).to match_css_pattern(
+      expect(subject.datagrid_form_for(ParamNameGrid81.new, url: "/grid")).to match_css_pattern(
         "form.datagrid-form input[name='g[id]']" => 1
       )
     end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 9ddfab4..b8cb418 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -32,7 +32,7 @@ class DatagridTest < Rails::Application
 
 File.open("spec.log", "w").close
 TEST_LOGGER = Logger.new("spec.log")
-NO_MONGO = ENV["NO_MONGO"]
+NO_MONGO = ENV.fetch("NO_MONGO", nil)
 
 if NO_MONGO
   warn("MONGODB WARNING: Skipping Mongoid and Mongomapper tests.")
@@ -98,7 +98,7 @@ def action_view_template
                                             File.expand_path("support/test_partials", __dir__)
                                           ], {})
   Datagrid::Engine.extend_modules
-  template = ActionView::Base.with_empty_template_cache.new(context, {}, ::ActionController::Base.new)
+  template = ActionView::Base.with_empty_template_cache.new(context, {}, ActionController::Base.new)
   allow(template).to receive(:protect_against_forgery?).and_return(false)
   template
 end

From d0786ae2a6d49cb306b834f80008fb8e1b0d9fb2 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 20:16:47 +0100
Subject: [PATCH 012/157] Rubocop

---
 spec/datagrid/filters_spec.rb      |  8 +--
 spec/datagrid/form_builder_spec.rb | 78 ++++++++++++++++++++----------
 spec/datagrid_spec.rb              |  4 +-
 3 files changed, 60 insertions(+), 30 deletions(-)

diff --git a/spec/datagrid/filters_spec.rb b/spec/datagrid/filters_spec.rb
index 7f335e9..afd239c 100644
--- a/spec/datagrid/filters_spec.rb
+++ b/spec/datagrid/filters_spec.rb
@@ -71,17 +71,17 @@ class TheReport
 
   describe "allow_blank and allow_nil options" do
     def check_performed(value, result, **options)
-      $FILTER_PERFORMED = false
+      filter_performed = false
       report = test_report(name: value) do
         scope { Entry }
         filter(:name, **options) do |_|
-          $FILTER_PERFORMED = true
+          filter_performed = true
           self
         end
       end
       expect(report.name).to eq(value)
       report.assets
-      expect($FILTER_PERFORMED).to eq(result)
+      expect(filter_performed).to eq(result)
     end
 
     it "should support allow_blank argument" do
@@ -304,7 +304,7 @@ class TestGrid8728
     end
 
     context "with delegation to attribute" do
-      let(:role) { OpenStruct.new("admin?" => admin) }
+      let(:role) { Struct.new(:admin?).new(admin) }
       let(:klass) do
         test_report_class do
           attr_accessor :role
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 0b5004f..5cf5ac0 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -92,7 +92,8 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<input value="01/02/2012" class="created_at date_filter" type="text" name="report[created_at]" id="report_created_at"/>'
+            '<input value="01/02/2012" class="created_at date_filter" type="text"
+                name="report[created_at]" id="report_created_at"/>'
           )
         }
       end
@@ -137,7 +138,8 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<input type="datetime-local" class="created_at date_time_filter" value="2024-01-01T09:25:15" name="report[created_at]" id="report_created_at"/>'
+            '<input type="datetime-local" class="created_at date_time_filter"
+                value="2024-01-01T09:25:15" name="report[created_at]" id="report_created_at"/>'
           )
         }
 
@@ -147,7 +149,8 @@ class MyTemplate
           end
           it {
             should equal_to_dom(
-              '<input type="datetime-local" value="" class="created_at date_time_filter" name="report[created_at]" id="report_created_at"/>'
+              '<input type="datetime-local" value="" class="created_at date_time_filter"
+                  name="report[created_at]" id="report_created_at"/>'
             )
           }
         end
@@ -163,7 +166,8 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<input type="date" class="created_at date_time_filter" value="2024-01-01" name="report[created_at]" id="report_created_at"/>'
+            '<input type="date" class="created_at date_time_filter" value="2024-01-01"
+                name="report[created_at]" id="report_created_at"/>'
           )
         }
       end
@@ -182,9 +186,11 @@ class MyTemplate
         let(:_range) { [1, 2] }
         it {
           should equal_to_dom(
-            '<input value="1" id="from_hello" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' \
+            '<input value="1" id="from_hello" class="from group_id integer_filter"
+                multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input value="2" id="to_hello" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
+            '<input value="2" id="to_hello" class="to group_id integer_filter"
+                multiple type="text" name="report[group_id][]"/>'
           )
         }
       end
@@ -192,9 +198,11 @@ class MyTemplate
         let(:_range) { [10, nil] }
         it {
           should equal_to_dom(
-            '<input value="10" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' \
+            '<input value="10" class="from group_id integer_filter"
+                multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
+            '<input class="to group_id integer_filter"
+                multiple type="text" name="report[group_id][]"/>'
           )
         }
         it { should be_html_safe }
@@ -237,7 +245,11 @@ class MyTemplate
         let(:_range) { nil }
         it {
           should equal_to_dom(
-            '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"><span class="separator integer"> - </span><input class="to group_id integer_filter" multiple type="text" name="report[group_id][]">'
+            '<input class="from group_id integer_filter"
+                multiple type="text" name="report[group_id][]">
+            <span class="separator integer"> - </span>
+            <input class="to group_id integer_filter"
+                multiple type="text" name="report[group_id][]">'
           )
         }
       end
@@ -254,9 +266,11 @@ class MyTemplate
       let(:_range) { [1.5, 2.5] }
       it {
         should equal_to_dom(
-          '<input value="1.5" class="from rating float_filter" multiple type="text" name="report[rating][]"/>' \
+          '<input value="1.5" class="from rating float_filter"
+              multiple type="text" name="report[rating][]"/>' \
           '<span class="separator float"> - </span>' \
-          '<input value="2.5" class="to rating float_filter" multiple type="text" name="report[rating][]"/>'
+          '<input value="2.5" class="to rating float_filter"
+              multiple type="text" name="report[rating][]"/>'
         )
       }
     end
@@ -273,9 +287,11 @@ class MyTemplate
         let(:_range) { ["2012-01-03", nil] }
         it {
           should equal_to_dom(
-            '<input value="2012-01-03" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<input value="2012-01-03" class="from created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
+            '<input class="to created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>'
           )
         }
         it { should be_html_safe }
@@ -289,9 +305,11 @@ class MyTemplate
         let(:_range) { ["2013/01/01", "2013/02/02"] }
         it {
           should equal_to_dom(
-            '<input value="01/01/2013" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<input value="01/01/2013" class="from created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="02/02/2013" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
+            '<input value="02/02/2013" class="to created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>'
           )
         }
       end
@@ -299,9 +317,11 @@ class MyTemplate
         let(:_range) { [nil, "2012-01-03"] }
         it {
           should equal_to_dom(
-            '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<input class="from created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="2012-01-03" class="to created_at date_filter" multiple type="text"  name="report[created_at][]"/>'
+            '<input value="2012-01-03" class="to created_at date_filter"
+                multiple type="text"  name="report[created_at][]"/>'
           )
         }
         it { should be_html_safe }
@@ -311,9 +331,11 @@ class MyTemplate
         let(:_range) { Date.parse("2012-01-02")..Date.parse("2012-01-01") }
         it {
           should equal_to_dom(
-            '<input value="2012-01-01" class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<input value="2012-01-01" class="from created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="2012-01-02" class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
+            '<input value="2012-01-02" class="to created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>'
           )
         }
       end
@@ -326,9 +348,11 @@ class MyTemplate
         let(:_range) { [nil, nil] }
         it {
           should equal_to_dom(
-            '<input class="from created_at date_filter" multiple type="text" name="report[created_at][]"/>' \
+            '<input class="from created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input class="to created_at date_filter" multiple type="text" name="report[created_at][]"/>'
+            '<input class="to created_at date_filter"
+                multiple type="text" name="report[created_at][]"/>'
           )
         }
       end
@@ -405,7 +429,8 @@ class MyTemplate
         let(:_category_filter_options) { { prompt: "My Prompt" } }
         it {
           should equal_to_dom(
-            '<select class="category enum_filter" name="report[category]" id="report_category"><option value="">My Prompt</option>
+            '<select class="category enum_filter" name="report[category]" id="report_category">
+            <option value="">My Prompt</option>
          <option value="first">first</option>
          <option value="second">second</option></select>'
           )
@@ -416,7 +441,8 @@ class MyTemplate
         let(:_category_filter_options) { { input_options: { class: "custom-class" } } }
         it {
           should equal_to_dom(
-            '<select class="custom-class category enum_filter" name="report[category]" id="report_category"><option value="" label=" "></option>
+            '<select class="custom-class category enum_filter" name="report[category]" id="report_category">
+            <option value="" label=" "></option>
          <option value="first">first</option>
          <option value="second">second</option></select>'
           )
@@ -428,11 +454,13 @@ class MyTemplate
           should equal_to_dom(
             '
 <label class="category enum_filter checkboxes" for="report_category_first">
-<input class="category enum_filter checkboxes" type="checkbox" id="report_category_first" value="first" name="report[category][]" />
+<input class="category enum_filter checkboxes" type="checkbox" id="report_category_first"
+    value="first" name="report[category][]" />
 first
 </label>
 <label class="category enum_filter checkboxes" for="report_category_second">
-<input class="category enum_filter checkboxes" type="checkbox" id="report_category_second" value="second" name="report[category][]" />
+<input class="category enum_filter checkboxes" type="checkbox" id="report_category_second"
+    value="second" name="report[category][]" />
 second
 </label>
 '
diff --git a/spec/datagrid_spec.rb b/spec/datagrid_spec.rb
index b05b6f4..074ca2c 100644
--- a/spec/datagrid_spec.rb
+++ b/spec/datagrid_spec.rb
@@ -46,7 +46,9 @@
 
     context "when not defined on class level" do
       subject do
-        test_report {}
+        test_report do
+          column(:id)
+        end
       end
 
       it "should raise ConfigurationError" do

From b426776361a1467cc33291284002d28aba4cdfb6 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 20:37:10 +0100
Subject: [PATCH 013/157] Rubocop

---
 .rubocop.yml                  |  8 ++++++++
 lib/datagrid.rb               |  9 +++++++++
 lib/datagrid/active_model.rb  | 12 ++----------
 lib/datagrid/filters.rb       |  7 +++++--
 lib/datagrid/form_builder.rb  |  2 +-
 lib/datagrid/helper.rb        |  1 +
 lib/datagrid/ordering.rb      |  1 +
 lib/datagrid/scaffold.rb      |  1 +
 lib/datagrid/utils.rb         |  1 +
 spec/datagrid/columns_spec.rb |  8 ++++++--
 spec/datagrid/core_spec.rb    |  4 +++-
 11 files changed, 38 insertions(+), 16 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index 8560090..4421b7b 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -12,3 +12,11 @@ Lint/ConstantDefinitionInBlock:
   Enabled: false
 Metrics/CyclomaticComplexity:
   Enabled: false
+Metrics/PerceivedComplexity:
+  Enabled: false
+Metrics/ClassLength:
+  Enabled: false
+Metrics/ModuleLength:
+  Enabled: false
+Style/Documentation:
+  Enabled: false
diff --git a/lib/datagrid.rb b/lib/datagrid.rb
index 4bf2bdf..616d779 100644
--- a/lib/datagrid.rb
+++ b/lib/datagrid.rb
@@ -4,6 +4,15 @@
 require "datagrid/configuration"
 require "datagrid/engine"
 
+# Main datagrid module that needs to be included in grid class
+#
+# @example
+#   class UsersGrid
+#     include Datagrid
+#     scope { User }
+#     column(:id)
+#     column(:name)
+#   end
 module Datagrid
   extend ActiveSupport::Autoload
 
diff --git a/lib/datagrid/active_model.rb b/lib/datagrid/active_model.rb
index 22860e0..f139ab0 100644
--- a/lib/datagrid/active_model.rb
+++ b/lib/datagrid/active_model.rb
@@ -7,16 +7,8 @@ module ActiveModel
     def self.included(base)
       base.extend ClassMethods
       base.class_eval do
-        begin
-          require "active_model/naming"
-          extend ::ActiveModel::Naming
-        rescue LoadError
-        end
-        begin
-          require "active_model/attributes_assignment"
-          extend ::ActiveModel::AttributesAssignment
-        rescue LoadError
-        end
+        require "active_model/naming"
+        extend ::ActiveModel::Naming
       end
     end
 
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 4a3d31e..6b14f3c 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -44,6 +44,7 @@ def self.included(base)
       end
     end
 
+    # Grid class methods related to filters
     module ClassMethods
       # @return [Datagrid::Filters::BaseFilter, nil] filter definition object by name
       def filter_by_name(attribute)
@@ -73,8 +74,10 @@ def filter_by_name(attribute)
       # Available options:
       #
       # * <tt>:header</tt> - determines the header of the filter
-      # * <tt>:default</tt> - the default filter value. Able to accept a <tt>Proc</tt> in case default should be recalculated
-      # * <tt>:range</tt> - if true, filter can accept two values that are treated as a range that will be used for filtering
+      # * <tt>:default</tt> - the default filter value.
+      #   Can be a <tt>Proc</tt> in case default should be recalculated.
+      # * <tt>:range</tt> - if true, filter can accept two values that are treated
+      #   as a range that will be used for filtering.
       #   Not all of the filter types support this option. Here are the list of types that do:
       #   <tt>:integer</tt>, <tt>:float</tt>, <tt>:date</tt>, <tt>:datetime</tt>, <tt>:string</tt>
       # * <tt>:multiple</tt> -  if true multiple values can be assigned to this filter.
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 65347fe..e7dfa8a 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -10,7 +10,7 @@ module FormBuilder
     #   * <tt>select</tt> for enum, xboolean filter types
     #   * <tt>check_box</tt> for boolean filter type
     #   * <tt>text_field</tt> for other filter types
-    def datagrid_filter(filter_or_attribute, partials: nil, **options, &block)
+    def datagrid_filter(filter_or_attribute, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
       send(filter.form_builder_helper_name, filter, **options, &block)
     end
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index 6e16ac8..925af9d 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -3,6 +3,7 @@
 require "action_view"
 
 module Datagrid
+  # Datagrid methods available as helpers in Rails views
   module Helper
     # @param grid [Datagrid] grid object
     # @param column [Datagrid::Columns::Column, String, Symbol] column name
diff --git a/lib/datagrid/ordering.rb b/lib/datagrid/ordering.rb
index cab6ee9..963d7c4 100644
--- a/lib/datagrid/ordering.rb
+++ b/lib/datagrid/ordering.rb
@@ -7,6 +7,7 @@ module Datagrid
   class OrderUnsupported < StandardError
   end
 
+  # Module adds support for ordering by defined columns for Datagrid.
   module Ordering
     # @!visibility private
     def self.included(base)
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index 89bdc73..b1faa50 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -4,6 +4,7 @@
 
 # @!visibility private
 module Datagrid
+  # @!visibility private
   class Scaffold < Rails::Generators::NamedBase
     include Rails::Generators::ResourceHelpers
 
diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index 3a2a18d..43152ce 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -79,6 +79,7 @@ def parse_date(value)
           Array(Datagrid.configuration.date_formats).each do |format|
             return Date.strptime(value, format)
           rescue ::ArgumentError
+            nil
           end
         end
         return Date.parse(value) if value.is_a?(String)
diff --git a/spec/datagrid/columns_spec.rb b/spec/datagrid/columns_spec.rb
index b7f90cc..9fdb23e 100644
--- a/spec/datagrid/columns_spec.rb
+++ b/spec/datagrid/columns_spec.rb
@@ -154,7 +154,9 @@ class Report27
     end
 
     it "should support csv export" do
-      expect(subject.to_csv).to eq("Shipping date,Group,Name,Access level,Pet\n#{date},Pop,Star,admin,ROTTWEILER\n")
+      expect(subject.to_csv).to eq(
+        "Shipping date,Group,Name,Access level,Pet\n#{date},Pop,Star,admin,ROTTWEILER\n"
+      )
     end
 
     it "should support csv export of particular columns" do
@@ -162,7 +164,9 @@ class Report27
     end
 
     it "should support csv export options" do
-      expect(subject.to_csv(col_sep: ";")).to eq("Shipping date;Group;Name;Access level;Pet\n#{date};Pop;Star;admin;ROTTWEILER\n")
+      expect(subject.to_csv(col_sep: ";")).to eq(
+        "Shipping date;Group;Name;Access level;Pet\n#{date};Pop;Star;admin;ROTTWEILER\n"
+      )
     end
   end
 
diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index 14626ba..ca14b37 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -107,7 +107,9 @@ class InspectTest
       end
 
       grid = InspectTest.new(created_at: %w[2014-01-01 2014-08-05], descending: true, order: "name")
-      expect(grid.inspect).to eq("#<InspectTest order: :name, descending: true, created_at: [Wed, 01 Jan 2014, Tue, 05 Aug 2014]>")
+      expect(grid.inspect).to eq(
+        "#<InspectTest order: :name, descending: true, created_at: [Wed, 01 Jan 2014, Tue, 05 Aug 2014]>"
+      )
     end
   end
 

From 7d6b8f1d1781beac97b683a9cc3d8f5499ef6fe1 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 20:46:33 +0100
Subject: [PATCH 014/157] Cleanup

---
 lib/datagrid/columns.rb                | 14 +++++++++++---
 lib/datagrid/filters/dynamic_filter.rb |  4 +---
 lib/datagrid/renderer.rb               |  4 ++++
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index 81b3ec9..360cd0f 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -300,7 +300,8 @@ def columns(*column_names, data: false, html: false)
       end
     end
 
-    # @param column_names [Array<String, Symbol>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String, Symbol>] list of column names
+    #   if you want to limit data only to specified columns
     # @return [Array<Datagrid::Columns::Column>] columns that can be represented in plain data(non-html) way
     def data_columns(*column_names, **options)
       columns(*column_names, **options, data: true)
@@ -374,7 +375,8 @@ def initialize(*)
       super
     end
 
-    # @return [Array<Datagrid::Columns::Column>] all columns that are possible to be displayed for the current grid object
+    # @return [Array<Datagrid::Columns::Column>] all columns
+    #   that are possible to be displayed for the current grid object
     #
     # @example
     #   class MyGrid
@@ -478,7 +480,9 @@ def cache_key(asset)
       end
     rescue NotImplementedError
       raise Datagrid::ConfigurationError,
-            "#{self} is setup to use cache. But there was appropriate cache key found for #{asset.inspect}. Please set cached option to block with asset as argument and cache key as returning value to resolve the issue."
+            <<~MSG
+              #{self} is setup to use cache. But there was appropriate cache key found for #{asset.inspect}.
+            MSG
     end
 
     def map_with_batches(&block)
@@ -526,6 +530,10 @@ def initialize(grid, model)
       def method_missing(meth, *_args)
         @grid.data_value(meth, @model)
       end
+
+      def respond_to_missing?(meth, include_private = false)
+        !!@grid.column_by_name(meth) || super
+      end
     end
   end
 end
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index 0a0ae83..636512c 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -106,9 +106,7 @@ def type_cast(field, value)
           value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_i : nil
         when :float
           value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_f : nil
-        when :date
-          Datagrid::Utils.parse_date(value)
-        when :timestamp
+        when :date, :timestamp
           Datagrid::Utils.parse_date(value)
         when :boolean
           Datagrid::Utils.booleanize(value)
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index c838ed6..d485241 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -149,6 +149,10 @@ def method_missing(method, *args, &blk)
           super
         end
       end
+
+      def respond_to_missing?(method, include_private = false)
+        !!@grid.column_by_name(method) || super
+      end
     end
   end
 end

From 9b76d771d122f83be3443a41c45e8c1ebb76ba07 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 3 Nov 2024 20:53:11 +0100
Subject: [PATCH 015/157] Rubocop

---
 lib/datagrid/drivers/active_record.rb     | 12 +++++-------
 lib/datagrid/drivers/array.rb             |  4 ++--
 lib/datagrid/drivers/mongo_mapper.rb      |  4 ++--
 lib/datagrid/drivers/mongoid.rb           |  4 ++--
 lib/datagrid/drivers/sequel.rb            |  8 ++++----
 lib/datagrid/filters.rb                   |  7 +++++--
 lib/datagrid/filters/base_filter.rb       |  8 +++++---
 lib/datagrid/filters/composite_filters.rb |  2 --
 lib/datagrid/form_builder.rb              | 10 ++++------
 9 files changed, 29 insertions(+), 30 deletions(-)

diff --git a/lib/datagrid/drivers/active_record.rb b/lib/datagrid/drivers/active_record.rb
index 5be39b1..64f5949 100644
--- a/lib/datagrid/drivers/active_record.rb
+++ b/lib/datagrid/drivers/active_record.rb
@@ -32,9 +32,7 @@ def to_scope(scope)
 
       def append_column_queries(assets, columns)
         if columns.present?
-          if assets.select_values.empty?
-            assets = assets.select(Arel.respond_to?(:star) ? assets.klass.arel_table[Arel.star] : "#{assets.quoted_table_name}.*")
-          end
+          assets = assets.select(assets.klass.arel_table[Arel.star]) if assets.select_values.empty?
           columns = columns.map { |c| "#{c.query} AS #{c.name}" }
           assets = assets.select(*columns)
         end
@@ -64,7 +62,7 @@ def reverse_order(scope)
       end
 
       def default_order(scope, column_name)
-        has_column?(scope, column_name) ? prefix_table_name(scope, column_name) : nil
+        scope_has_column?(scope, column_name) ? prefix_table_name(scope, column_name) : nil
       end
 
       def greater_equal(scope, field, value)
@@ -75,7 +73,7 @@ def less_equal(scope, field, value)
         scope.where(["#{prefix_table_name(scope, field)} <= ?", value])
       end
 
-      def has_column?(scope, column_name)
+      def scope_has_column?(scope, column_name)
         scope.column_names.include?(column_name.to_s)
       rescue ::ActiveRecord::StatementInvalid
         false
@@ -91,7 +89,7 @@ def contains(scope, field, value)
       end
 
       def normalized_column_type(scope, field)
-        return nil unless has_column?(scope, field)
+        return nil unless scope_has_column?(scope, field)
 
         builtin_type = scope.columns_hash[field.to_s].type
         {
@@ -130,7 +128,7 @@ def can_preload?(scope, association)
       protected
 
       def prefix_table_name(scope, field)
-        has_column?(scope, field) ? [scope.table_name, field].join(".") : field
+        scope_has_column?(scope, field) ? [scope.table_name, field].join(".") : field
       end
 
       def contains_predicate
diff --git a/lib/datagrid/drivers/array.rb b/lib/datagrid/drivers/array.rb
index bfa36ef..32007cd 100644
--- a/lib/datagrid/drivers/array.rb
+++ b/lib/datagrid/drivers/array.rb
@@ -54,12 +54,12 @@ def less_equal(scope, field, value)
         end
       end
 
-      def has_column?(scope, column_name)
+      def scope_has_column?(scope, column_name)
         scope.any? && scope.first.respond_to?(column_name)
       end
 
       def is_timestamp?(scope, column_name)
-        has_column?(scope, column_name) &&
+        scope_has_column?(scope, column_name) &&
           timestamp_class?(get(scope.first, column_name).class)
       end
 
diff --git a/lib/datagrid/drivers/mongo_mapper.rb b/lib/datagrid/drivers/mongo_mapper.rb
index df1c178..48871ac 100644
--- a/lib/datagrid/drivers/mongo_mapper.rb
+++ b/lib/datagrid/drivers/mongo_mapper.rb
@@ -31,7 +31,7 @@ def desc(scope, order)
       end
 
       def default_order(scope, column_name)
-        has_column?(scope, column_name) ? column_name : nil
+        scope_has_column?(scope, column_name) ? column_name : nil
       end
 
       def greater_equal(scope, field, value)
@@ -42,7 +42,7 @@ def less_equal(scope, field, value)
         scope.where(field => { "$lte" => value })
       end
 
-      def has_column?(scope, column_name)
+      def scope_has_column?(scope, column_name)
         scope.key?(column_name)
       end
 
diff --git a/lib/datagrid/drivers/mongoid.rb b/lib/datagrid/drivers/mongoid.rb
index aa166a8..e75b674 100644
--- a/lib/datagrid/drivers/mongoid.rb
+++ b/lib/datagrid/drivers/mongoid.rb
@@ -36,7 +36,7 @@ def desc(scope, order)
       end
 
       def default_order(scope, column_name)
-        has_column?(scope, column_name) ? column_name : nil
+        scope_has_column?(scope, column_name) ? column_name : nil
       end
 
       def greater_equal(scope, field, value)
@@ -47,7 +47,7 @@ def less_equal(scope, field, value)
         scope.where(field => { "$lte" => value })
       end
 
-      def has_column?(scope, column_name)
+      def scope_has_column?(scope, column_name)
         column_names(scope).include?(column_name.to_s)
       end
 
diff --git a/lib/datagrid/drivers/sequel.rb b/lib/datagrid/drivers/sequel.rb
index a450642..a84766e 100644
--- a/lib/datagrid/drivers/sequel.rb
+++ b/lib/datagrid/drivers/sequel.rb
@@ -33,7 +33,7 @@ def desc(scope, order)
       end
 
       def default_order(scope, column_name)
-        has_column?(scope, column_name) ? ::Sequel.lit(prefix_table_name(scope, column_name)) : nil
+        scope_has_column?(scope, column_name) ? ::Sequel.lit(prefix_table_name(scope, column_name)) : nil
       end
 
       def greater_equal(scope, field, value)
@@ -44,7 +44,7 @@ def less_equal(scope, field, value)
         scope.where(::Sequel.lit("#{prefix_table_name(scope, field)} <= ?", value))
       end
 
-      def has_column?(scope, column_name)
+      def scope_has_column?(scope, column_name)
         scope.columns.include?(column_name.to_sym)
       end
 
@@ -98,11 +98,11 @@ def can_preload?(scope, association)
       protected
 
       def prefix_table_name(scope, field)
-        has_column?(scope, field) ? [to_scope(scope).row_proc.table_name, field].join(".") : field
+        scope_has_column?(scope, field) ? [to_scope(scope).row_proc.table_name, field].join(".") : field
       end
 
       def column_type(scope, field)
-        has_column?(scope, field) ? to_scope(scope).row_proc.db_schema[field.to_sym][:type] : nil
+        scope_has_column?(scope, field) ? to_scope(scope).row_proc.db_schema[field.to_sym][:type] : nil
       end
     end
   end
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 6b14f3c..67f387b 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -217,8 +217,11 @@ def default_filter
     def find_select_filter(filter)
       filter = filter_by_name(filter)
       unless filter.class.included_modules.include?(::Datagrid::Filters::SelectOptions)
-        raise ::Datagrid::ArgumentError,
-              "#{self.class.name}##{filter.name} with type #{FILTER_TYPES.invert[filter.class].inspect} can not have select options"
+        type = FILTER_TYPES.invert[filter.class].inspect
+        raise(
+          ::Datagrid::ArgumentError,
+          "#{self.class.name}##{filter.name} with type #{type} can not have select options"
+        )
       end
       filter
     end
diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index ab43023..00ec275 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -38,8 +38,10 @@ def apply(grid_object, scope, value)
 
         result = default_filter(value, scope, grid_object) if result == Datagrid::Filters::DEFAULT_FILTER_BLOCK
         unless grid_object.driver.match?(result)
-          raise Datagrid::FilteringError,
-                "Can not apply #{name.inspect} filter: result #{result.inspect} no longer match #{grid_object.driver.class}."
+          raise(
+            Datagrid::FilteringError,
+            "Filter #{name.inspect} unapplicable: result no longer match #{grid_object.driver.class}."
+          )
         end
 
         result
@@ -183,7 +185,7 @@ def driver
       def default_filter(value, scope, _grid)
         return nil if dummy?
 
-        if !driver.has_column?(scope, name) && scope.respond_to?(name, true)
+        if !driver.scope_has_column?(scope, name) && scope.respond_to?(name, true)
           scope.public_send(name, value)
         else
           default_filter_where(scope, value)
diff --git a/lib/datagrid/filters/composite_filters.rb b/lib/datagrid/filters/composite_filters.rb
index a3d9238..53cac78 100644
--- a/lib/datagrid/filters/composite_filters.rb
+++ b/lib/datagrid/filters/composite_filters.rb
@@ -6,8 +6,6 @@ module Filters
     module CompositeFilters
       def self.included(base)
         base.extend ClassMethods
-        base.class_eval do
-        end
       end
 
       # @!visibility private
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index e7dfa8a..41ff7d1 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -208,12 +208,10 @@ def datagrid_float_filter(filter, options = {})
     end
 
     def datagrid_get_filter(attribute_or_filter)
-      if Utils.string_like?(attribute_or_filter)
-        object.class.filter_by_name(attribute_or_filter) ||
-          raise(Error, "Datagrid filter #{attribute_or_filter} not found")
-      else
-        attribute_or_filter
-      end
+      return attribute_or_filter unless Utils.string_like?(attribute_or_filter)
+
+      object.class.filter_by_name(attribute_or_filter) ||
+        raise(Error, "Datagrid filter #{attribute_or_filter} not found")
     end
 
     def add_html_classes(options, *classes)

From e2fc7fb542daef8feef3cb4837c25cbcc647bf3a Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 11:28:19 +0100
Subject: [PATCH 016/157] Move formatting classes from input/label to wrapping
 div

---
 app/views/datagrid/_form.html.erb   |   2 +-
 lib/datagrid/filters/base_filter.rb |   5 +-
 lib/datagrid/filters/enum_filter.rb |   2 +-
 lib/datagrid/form_builder.rb        |  10 +-
 spec/datagrid/form_builder_spec.rb  | 155 ++++++++++++++--------------
 spec/datagrid/helper_spec.rb        |   8 +-
 6 files changed, 88 insertions(+), 94 deletions(-)

diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index 911d163..06f0414 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -1,6 +1,6 @@
 <%= form_for grid, **options do |f| -%>
   <% grid.filters.each do |filter| %>
-    <div class="datagrid-filter">
+    <div class="datagrid-filter <%= filter.default_html_classes.join(' ') %>">
       <%= f.datagrid_label filter %>
       <%= f.datagrid_filter filter %>
     </div>
diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index 00ec275..eee49eb 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -144,7 +144,10 @@ def enabled?(grid)
       end
 
       def default_html_classes
-        [name, self.class.to_s.demodulize.underscore]
+        [
+          "datagrid-filter-#{name}",
+          "datagrid-filter-type-#{type}",
+        ]
       end
 
       protected
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 6e5a12b..687fb1b 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -21,7 +21,7 @@ def parse(value)
 
       def default_html_classes
         res = super
-        res.push("checkboxes") if checkboxes?
+        res.push("datagrid-checkboxes") if checkboxes?
         res
       end
 
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 41ff7d1..b45262f 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -21,10 +21,7 @@ def datagrid_filter(filter_or_attribute, **options, &block)
     # @return [String] a form label tag for the corresponding filter name
     def datagrid_label(filter_or_attribute, text = nil, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
-      options = add_html_classes(
-        { **filter.label_options, **options },
-        filter.default_html_classes
-      )
+      options = { **filter.label_options, **options }
       label(filter.name, text || filter.header, **options, &block)
     end
 
@@ -232,10 +229,7 @@ def render_partial(name, locals)
     end
 
     def add_filter_options(filter, **options)
-      add_html_classes(
-        { **filter.input_options, **options },
-        *filter.default_html_classes
-      )
+      { **filter.input_options, **options }
     end
 
     class Error < StandardError
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 5cf5ac0..fb2d5d4 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -42,7 +42,7 @@ class MyTemplate
       let(:_filter) { :name }
       it {
         should equal_to_dom(
-          '<input class="name default_filter" type="text" name="report[name]" id="report_name"/>'
+          '<input type="text" name="report[name]" id="report_name"/>'
         )
       }
     end
@@ -56,7 +56,7 @@ class MyTemplate
       end
       it {
         should equal_to_dom(
-          '<input class="group_id integer_filter" type="text" name="report[group_id]" id="report_group_id"/>'
+          '<input type="text" name="report[group_id]" id="report_group_id"/>'
         )
       }
 
@@ -64,7 +64,7 @@ class MyTemplate
         let(:view_options) { { partials: "anything" } }
         it {
           should equal_to_dom(
-            '<input class="group_id integer_filter" type="text" name="report[group_id]" id="report_group_id"/>'
+            '<input type="text" name="report[group_id]" id="report_group_id"/>'
           )
         }
       end
@@ -80,7 +80,7 @@ class MyTemplate
       end
       it {
         should equal_to_dom(
-          '<input class="created_at date_filter" type="text" name="report[created_at]" id="report_created_at"/>'
+          '<input type="text" name="report[created_at]" id="report_created_at"/>'
         )
       }
       context "when special date format specified" do
@@ -92,7 +92,7 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<input value="01/02/2012" class="created_at date_filter" type="text"
+            '<input value="01/02/2012" type="text"
                 name="report[created_at]" id="report_created_at"/>'
           )
         }
@@ -109,7 +109,7 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<input type="date" class="created_at date_filter" name="report[created_at]" id="report_created_at"/>'
+            '<input type="date" name="report[created_at]" id="report_created_at"/>'
           )
         }
       end
@@ -123,7 +123,7 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<textarea class="name string_filter" name="report[name]" id="report_name"/>'
+            '<textarea name="report[name]" id="report_name"/>'
           )
         }
       end
@@ -138,7 +138,7 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<input type="datetime-local" class="created_at date_time_filter"
+            '<input type="datetime-local"
                 value="2024-01-01T09:25:15" name="report[created_at]" id="report_created_at"/>'
           )
         }
@@ -149,7 +149,7 @@ class MyTemplate
           end
           it {
             should equal_to_dom(
-              '<input type="datetime-local" value="" class="created_at date_time_filter"
+              '<input type="datetime-local" value=""
                   name="report[created_at]" id="report_created_at"/>'
             )
           }
@@ -166,7 +166,7 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<input type="date" class="created_at date_time_filter" value="2024-01-01"
+            '<input type="date" value="2024-01-01"
                 name="report[created_at]" id="report_created_at"/>'
           )
         }
@@ -186,10 +186,10 @@ class MyTemplate
         let(:_range) { [1, 2] }
         it {
           should equal_to_dom(
-            '<input value="1" id="from_hello" class="from group_id integer_filter"
+            '<input value="1" id="from_hello" class="from"
                 multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input value="2" id="to_hello" class="to group_id integer_filter"
+            '<input value="2" id="to_hello" class="to"
                 multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -198,10 +198,10 @@ class MyTemplate
         let(:_range) { [10, nil] }
         it {
           should equal_to_dom(
-            '<input value="10" class="from group_id integer_filter"
+            '<input value="10" class="from"
                 multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input class="to group_id integer_filter"
+            '<input class="to"
                 multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -211,9 +211,9 @@ class MyTemplate
         let(:_range) { [nil, 10] }
         it {
           should equal_to_dom(
-            '<input class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' \
+            '<input class="from" multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input value="10" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
+            '<input value="10" class="to" multiple type="text" name="report[group_id][]"/>'
           )
         }
         it { should be_html_safe }
@@ -223,9 +223,9 @@ class MyTemplate
         let(:_range) { 2..1 }
         it {
           should equal_to_dom(
-            '<input value="1" class="from group_id integer_filter" multiple type="text" name="report[group_id][]"/>' \
+            '<input value="1" class="from" multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input value="2" class="to group_id integer_filter" multiple type="text" name="report[group_id][]"/>'
+            '<input value="2" class="to" multiple type="text" name="report[group_id][]"/>'
           )
         }
       end
@@ -245,11 +245,9 @@ class MyTemplate
         let(:_range) { nil }
         it {
           should equal_to_dom(
-            '<input class="from group_id integer_filter"
-                multiple type="text" name="report[group_id][]">
+            '<input class="from" multiple type="text" name="report[group_id][]">
             <span class="separator integer"> - </span>
-            <input class="to group_id integer_filter"
-                multiple type="text" name="report[group_id][]">'
+            <input class="to" multiple type="text" name="report[group_id][]">'
           )
         }
       end
@@ -266,10 +264,10 @@ class MyTemplate
       let(:_range) { [1.5, 2.5] }
       it {
         should equal_to_dom(
-          '<input value="1.5" class="from rating float_filter"
+          '<input value="1.5" class="from"
               multiple type="text" name="report[rating][]"/>' \
           '<span class="separator float"> - </span>' \
-          '<input value="2.5" class="to rating float_filter"
+          '<input value="2.5" class="to"
               multiple type="text" name="report[rating][]"/>'
         )
       }
@@ -287,11 +285,9 @@ class MyTemplate
         let(:_range) { ["2012-01-03", nil] }
         it {
           should equal_to_dom(
-            '<input value="2012-01-03" class="from created_at date_filter"
-                multiple type="text" name="report[created_at][]"/>' \
+            '<input value="2012-01-03" class="from" multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input class="to created_at date_filter"
-                multiple type="text" name="report[created_at][]"/>'
+            '<input class="to" multiple type="text" name="report[created_at][]"/>'
           )
         }
         it { should be_html_safe }
@@ -305,10 +301,10 @@ class MyTemplate
         let(:_range) { ["2013/01/01", "2013/02/02"] }
         it {
           should equal_to_dom(
-            '<input value="01/01/2013" class="from created_at date_filter"
+            '<input value="01/01/2013" class="from"
                 multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="02/02/2013" class="to created_at date_filter"
+            '<input value="02/02/2013" class="to"
                 multiple type="text" name="report[created_at][]"/>'
           )
         }
@@ -317,10 +313,10 @@ class MyTemplate
         let(:_range) { [nil, "2012-01-03"] }
         it {
           should equal_to_dom(
-            '<input class="from created_at date_filter"
+            '<input class="from"
                 multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="2012-01-03" class="to created_at date_filter"
+            '<input value="2012-01-03" class="to"
                 multiple type="text"  name="report[created_at][]"/>'
           )
         }
@@ -331,10 +327,10 @@ class MyTemplate
         let(:_range) { Date.parse("2012-01-02")..Date.parse("2012-01-01") }
         it {
           should equal_to_dom(
-            '<input value="2012-01-01" class="from created_at date_filter"
+            '<input value="2012-01-01" class="from"
                 multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="2012-01-02" class="to created_at date_filter"
+            '<input value="2012-01-02" class="to"
                 multiple type="text" name="report[created_at][]"/>'
           )
         }
@@ -348,11 +344,9 @@ class MyTemplate
         let(:_range) { [nil, nil] }
         it {
           should equal_to_dom(
-            '<input class="from created_at date_filter"
-                multiple type="text" name="report[created_at][]"/>' \
+            '<input class="from" multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input class="to created_at date_filter"
-                multiple type="text" name="report[created_at][]"/>'
+            '<input class="to" multiple type="text" name="report[created_at][]"/>'
           )
         }
       end
@@ -369,7 +363,7 @@ class MyTemplate
       end
       it {
         should equal_to_dom(
-          %(<select class="category enum_filter" name="report[category]" id="report_category">
+          %(<select name="report[category]" id="report_category">
        <option value="" label=" "></option>
        <option value="first">first</option>
        <option value="second">second</option></select>)
@@ -384,7 +378,7 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            %(<select class="category enum_filter" name="report[category]" id="report_category">
+            %(<select name="report[category]" id="report_category">
           <option value="" label=" "></option>
           <option value="block_value">block option</option></select>)
           )
@@ -396,7 +390,7 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            %(<select class="category enum_filter" name="report[category]" id="report_category">
+            %(<select name="report[category]" id="report_category">
        <option value="" label=" "></option>
        <option selected value="first">first</option>
        <option value="second">second</option></select>)
@@ -407,7 +401,7 @@ class MyTemplate
         let(:_category_filter_options) { { include_blank: false } }
         it {
           should equal_to_dom(
-            '<select class="category enum_filter" name="report[category]" id="report_category">
+            '<select name="report[category]" id="report_category">
          <option value="first">first</option>
          <option value="second">second</option></select>'
           )
@@ -417,7 +411,7 @@ class MyTemplate
         let(:_category_filter_options) { { include_blank: proc { "Choose plz" } } }
         it {
           should equal_to_dom(
-            '<select class="category enum_filter" name="report[category]" id="report_category">
+            '<select name="report[category]" id="report_category">
          <option value="">Choose plz</option>
          <option value="first">first</option>
          <option value="second">second</option></select>'
@@ -429,7 +423,7 @@ class MyTemplate
         let(:_category_filter_options) { { prompt: "My Prompt" } }
         it {
           should equal_to_dom(
-            '<select class="category enum_filter" name="report[category]" id="report_category">
+            '<select name="report[category]" id="report_category">
             <option value="">My Prompt</option>
          <option value="first">first</option>
          <option value="second">second</option></select>'
@@ -441,7 +435,7 @@ class MyTemplate
         let(:_category_filter_options) { { input_options: { class: "custom-class" } } }
         it {
           should equal_to_dom(
-            '<select class="custom-class category enum_filter" name="report[category]" id="report_category">
+            '<select class="custom-class" name="report[category]" id="report_category">
             <option value="" label=" "></option>
          <option value="first">first</option>
          <option value="second">second</option></select>'
@@ -453,13 +447,13 @@ class MyTemplate
         it {
           should equal_to_dom(
             '
-<label class="category enum_filter checkboxes" for="report_category_first">
-<input class="category enum_filter checkboxes" type="checkbox" id="report_category_first"
+<label for="report_category_first">
+<input type="checkbox" id="report_category_first"
     value="first" name="report[category][]" />
 first
 </label>
-<label class="category enum_filter checkboxes" for="report_category_second">
-<input class="category enum_filter checkboxes" type="checkbox" id="report_category_second"
+<label for="report_category_second">
+<input type="checkbox" id="report_category_second"
     value="second" name="report[category][]" />
 second
 </label>
@@ -486,7 +480,7 @@ class MyTemplate
         should equal_to_dom(
           # hidden is important when default is set to true
           %(<input name="report[disabled]" type="hidden" value="0" autocomplete="off">
-          <input class="disabled boolean_filter" type="checkbox" value="1"
+          <input type="checkbox" value="1"
              checked name="report[disabled]" id="report_disabled">)
         )
       }
@@ -501,7 +495,7 @@ class MyTemplate
       end
       it {
         should equal_to_dom(
-          %(<select class="disabled extended_boolean_filter" name="report[disabled]" id="report_disabled">
+          %(<select name="report[disabled]" id="report_disabled">
           <option value="" label=" "></option>
           <option value="YES">Yes</option>
           <option value="NO">No</option></select>)
@@ -518,7 +512,7 @@ class MyTemplate
 
       let(:_filter) { :name }
 
-      it { should equal_to_dom('<input class="name string_filter" type="text" name="report[name]" id="report_name">') }
+      it { should equal_to_dom('<input type="text" name="report[name]" id="report_name">') }
 
       context "when multiple option is set" do
         let(:_grid) do
@@ -532,7 +526,7 @@ class MyTemplate
 
         it {
           should equal_to_dom(
-            '<input value="one,two" class="name string_filter" type="text" name="report[name]" id="report_name">'
+            '<input value="one,two" type="text" name="report[name]" id="report_name">'
           )
         }
       end
@@ -551,7 +545,7 @@ class MyTemplate
         end
       end
       let(:_filter) { :name }
-      it { should equal_to_dom('<select class="name enum_filter" name="report[name]" id="report_name"></select>') }
+      it { should equal_to_dom('<select name="report[name]" id="report_name"></select>') }
     end
     context "with float filter type" do
       let(:_grid) do
@@ -563,7 +557,7 @@ class MyTemplate
       let(:_filter) { :group_id }
       it {
         should equal_to_dom(
-          '<input class="group_id float_filter" type="text" name="report[group_id]" id="report_group_id"/>'
+          '<input type="text" name="report[group_id]" id="report_group_id"/>'
         )
       }
     end
@@ -578,7 +572,7 @@ class MyTemplate
       let(:_filter) { :group_id }
       let(:expected_html) do
         <<~HTML
-          <select multiple class="group_id enum_filter" name="report[group_id][]" id="report_group_id">
+          <select multiple name="report[group_id][]" id="report_group_id">
           <option value="hello">hello</option></select>
         HTML
       end
@@ -601,7 +595,7 @@ class MyTemplate
       let(:_filter) { :column_names }
       let(:expected_html) do
         <<~HTML
-          <select multiple class="column_names enum_filter" name="report[column_names][]" id="report_column_names"><option selected value="id">Id</option>
+          <select multiple name="report[column_names][]" id="report_column_names"><option selected value="id">Id</option>
           <option selected value="name">Name</option>
           <option value="category">Category</option></select>
         HTML
@@ -624,9 +618,18 @@ class MyTemplate
       let(:_filter) { :column_names }
       let(:expected_html) do
         <<~DOM
-          <label class="column_names enum_filter checkboxes" for="report_column_names_id"><input class="column_names enum_filter checkboxes" id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">Id</label>
-          <label class="column_names enum_filter checkboxes" for="report_column_names_name"><input class="column_names enum_filter checkboxes" id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]">Name</label>
-          <label class="column_names enum_filter checkboxes" for="report_column_names_category"><input class="column_names enum_filter checkboxes" id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">Category</label>
+          <label for="report_column_names_id">
+            <input id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">
+            Id
+          </label>
+          <label for="report_column_names_name">
+            <input id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]"/>
+            Name
+          </label>
+          <label for="report_column_names_category">
+            <input id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">
+            Category
+          </label>
         DOM
       end
 
@@ -651,7 +654,7 @@ class MyTemplate
       context "with no options" do
         let(:expected_html) do
           <<-HTML
-         <select class="condition dynamic_filter field" name="report[condition][]" id="report_condition"><option value="id">Id</option>
+         <select class="field" name="report[condition][]" id="report_condition"><option value="id">Id</option>
          <option value="group_id">Group</option>
          <option value="name">Name</option>
          <option value="category">Category</option>
@@ -661,10 +664,10 @@ class MyTemplate
          <option value="confirmed">Confirmed</option>
          <option value="shipping_date">Shipping date</option>
          <option value="created_at">Created at</option>
-         <option value="updated_at">Updated at</option></select><select class="condition dynamic_filter operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
+         <option value="updated_at">Updated at</option></select><select class="operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
          <option value="=~">&asymp;</option>
          <option value="&gt;=">&ge;</option>
-         <option value="&lt;=">&le;</option></select><input class="condition dynamic_filter value"  name="report[condition][]" type="text" id="report_condition">
+         <option value="&lt;=">&le;</option></select><input class="value"  name="report[condition][]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -675,11 +678,11 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-            <select class="condition dynamic_filter field" name="report[condition][]" id="report_condition"><option selected value="id">id</option>
-       <option value="name">name</option></select><select class="condition dynamic_filter operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
+            <select class="field" name="report[condition][]" id="report_condition"><option selected value="id">id</option>
+       <option value="name">name</option></select><select class="operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
        <option value="=~">&asymp;</option>
        <option selected value="&gt;=">&ge;</option>
-       <option value="&lt;=">&le;</option></select><input class="condition dynamic_filter value" name="report[condition][]" value="1" type="text"  id="report_condition">
+       <option value="&lt;=">&le;</option></select><input class="value" name="report[condition][]" value="1" type="text"  id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -691,8 +694,8 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <select class="condition dynamic_filter field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><select class="condition dynamic_filter operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
-       <option value="&lt;=">≤</option></select><input class="condition dynamic_filter value" name="report[condition][]" type="text" id="report_condition">
+          <select class="field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><select class="operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
+       <option value="&lt;=">≤</option></select><input class="value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -704,8 +707,8 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <input class="condition dynamic_filter field" name="report[condition][]" value="id" autocomplete="off" type="hidden" id="report_condition"><select class="condition dynamic_filter operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
-       <option value="&lt;=">≤</option></select><input class="condition dynamic_filter value" name="report[condition][]" type="text" id="report_condition">
+          <input class="field" name="report[condition][]" value="id" autocomplete="off" type="hidden" id="report_condition"><select class="operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
+       <option value="&lt;=">≤</option></select><input class="value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -716,7 +719,7 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <select class="condition dynamic_filter field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><input class="condition dynamic_filter operation" name="report[condition][]" value="=" autocomplete="off" type="hidden" id="report_condition"><input class="condition dynamic_filter value" name="report[condition][]" type="text" id="report_condition">
+          <select class="field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><input class="operation" name="report[condition][]" value="=" autocomplete="off" type="hidden" id="report_condition"><input class="value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -734,27 +737,27 @@ class MyTemplate
     end
     it "should generate label for filter" do
       expect(view.datagrid_label(:created_at)).to equal_to_dom(
-        '<label class="js-date-selector created_at date_filter" for="report_created_at">Created at</label>'
+        '<label class="js-date-selector" for="report_created_at">Created at</label>'
       )
     end
     it "should generate label for filter" do
       expect(view.datagrid_label(:name)).to equal_to_dom(
-        '<label class="name string_filter" for="report_name">Name</label>'
+        '<label for="report_name">Name</label>'
       )
     end
     it "should pass options through to the helper" do
       expect(view.datagrid_label(:name, class: "foo")).to equal_to_dom(
-        '<label class="foo name string_filter" for="report_name">Name</label>'
+        '<label class="foo" for="report_name">Name</label>'
       )
     end
     it "should support block" do
       expect(view.datagrid_label(:name, class: "foo") { "The Name" }).to equal_to_dom(
-        '<label class="foo name string_filter" for="report_name">The Name</label>'
+        '<label class="foo" for="report_name">The Name</label>'
       )
     end
     it "should support explicit label" do
       expect(view.datagrid_label(:name, "The Name")).to equal_to_dom(
-        '<label class="name string_filter" for="report_name">The Name</label>'
+        '<label for="report_name">The Name</label>'
       )
     end
   end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 04fd4bf..55617c5 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -484,7 +484,7 @@ class FormForGrid
         "form.datagrid-form.form_for_grid[action='/grid']" => 1,
         "form input[name=utf8]" => 1,
         "form .datagrid-filter label" => "Category",
-        "form .datagrid-filter input.category.default_filter[name='form_for_grid[category]'][value=hello]" => 1,
+        "form .datagrid-filter-category input[name='form_for_grid[category]'][value=hello]" => 1,
         "form input[name=commit][value=Search]" => 1,
         "form a.datagrid-reset[href='/location']" => 1
       )
@@ -533,12 +533,6 @@ def param_name
                                                   partials: "custom_form"
                                                 })
       expect(rendered_form).to include "form_partial_test"
-      expect(rendered_form).to match_css_pattern([
-                                                   "input.integer_filter.from",
-                                                   "input.integer_filter.to",
-                                                   ".enum_filter input[value='1']",
-                                                   ".enum_filter input[value='2']"
-                                                 ])
     end
   end
 

From 948efc57eacd01450c451d0330aa06427201f84d Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 11:32:13 +0100
Subject: [PATCH 017/157] Add datagrid-range- prefix for from/to css classes

---
 lib/datagrid/form_builder.rb       |  2 +-
 spec/datagrid/form_builder_spec.rb | 44 +++++++++++++++---------------
 2 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index b45262f..7746068 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -180,7 +180,7 @@ def datagrid_range_filter(_type, filter, options = {})
 
     def datagrid_range_filter_options(object, filter, type, options)
       type_method_map = { from: :first, to: :last }
-      options = add_html_classes(options, type)
+      options = add_html_classes(options, "datagrid-range-#{type}")
       options[:value] = filter.format(object[filter.name]&.public_send(type_method_map[type]))
       # In case of datagrid ranged filter
       # from and to input will have same id
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index fb2d5d4..2f4df49 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -186,10 +186,10 @@ class MyTemplate
         let(:_range) { [1, 2] }
         it {
           should equal_to_dom(
-            '<input value="1" id="from_hello" class="from"
+            '<input value="1" id="from_hello" class="datagrid-range-from"
                 multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input value="2" id="to_hello" class="to"
+            '<input value="2" id="to_hello" class="datagrid-range-to"
                 multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -198,10 +198,10 @@ class MyTemplate
         let(:_range) { [10, nil] }
         it {
           should equal_to_dom(
-            '<input value="10" class="from"
+            '<input value="10" class="datagrid-range-from"
                 multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input class="to"
+            '<input class="datagrid-range-to"
                 multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -211,9 +211,9 @@ class MyTemplate
         let(:_range) { [nil, 10] }
         it {
           should equal_to_dom(
-            '<input class="from" multiple type="text" name="report[group_id][]"/>' \
+            '<input class="datagrid-range-from" multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input value="10" class="to" multiple type="text" name="report[group_id][]"/>'
+            '<input value="10" class="datagrid-range-to" multiple type="text" name="report[group_id][]"/>'
           )
         }
         it { should be_html_safe }
@@ -223,9 +223,9 @@ class MyTemplate
         let(:_range) { 2..1 }
         it {
           should equal_to_dom(
-            '<input value="1" class="from" multiple type="text" name="report[group_id][]"/>' \
+            '<input value="1" class="datagrid-range-from" multiple type="text" name="report[group_id][]"/>' \
             '<span class="separator integer"> - </span>' \
-            '<input value="2" class="to" multiple type="text" name="report[group_id][]"/>'
+            '<input value="2" class="datagrid-range-to" multiple type="text" name="report[group_id][]"/>'
           )
         }
       end
@@ -245,9 +245,9 @@ class MyTemplate
         let(:_range) { nil }
         it {
           should equal_to_dom(
-            '<input class="from" multiple type="text" name="report[group_id][]">
+            '<input class="datagrid-range-from" multiple type="text" name="report[group_id][]">
             <span class="separator integer"> - </span>
-            <input class="to" multiple type="text" name="report[group_id][]">'
+            <input class="datagrid-range-to" multiple type="text" name="report[group_id][]">'
           )
         }
       end
@@ -264,10 +264,10 @@ class MyTemplate
       let(:_range) { [1.5, 2.5] }
       it {
         should equal_to_dom(
-          '<input value="1.5" class="from"
+          '<input value="1.5" class="datagrid-range-from"
               multiple type="text" name="report[rating][]"/>' \
           '<span class="separator float"> - </span>' \
-          '<input value="2.5" class="to"
+          '<input value="2.5" class="datagrid-range-to"
               multiple type="text" name="report[rating][]"/>'
         )
       }
@@ -285,9 +285,9 @@ class MyTemplate
         let(:_range) { ["2012-01-03", nil] }
         it {
           should equal_to_dom(
-            '<input value="2012-01-03" class="from" multiple type="text" name="report[created_at][]"/>' \
+            '<input value="2012-01-03" class="datagrid-range-from" multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input class="to" multiple type="text" name="report[created_at][]"/>'
+            '<input class="datagrid-range-to" multiple type="text" name="report[created_at][]"/>'
           )
         }
         it { should be_html_safe }
@@ -301,10 +301,10 @@ class MyTemplate
         let(:_range) { ["2013/01/01", "2013/02/02"] }
         it {
           should equal_to_dom(
-            '<input value="01/01/2013" class="from"
+            '<input value="01/01/2013" class="datagrid-range-from"
                 multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="02/02/2013" class="to"
+            '<input value="02/02/2013" class="datagrid-range-to"
                 multiple type="text" name="report[created_at][]"/>'
           )
         }
@@ -313,10 +313,10 @@ class MyTemplate
         let(:_range) { [nil, "2012-01-03"] }
         it {
           should equal_to_dom(
-            '<input class="from"
+            '<input class="datagrid-range-from"
                 multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="2012-01-03" class="to"
+            '<input value="2012-01-03" class="datagrid-range-to"
                 multiple type="text"  name="report[created_at][]"/>'
           )
         }
@@ -327,10 +327,10 @@ class MyTemplate
         let(:_range) { Date.parse("2012-01-02")..Date.parse("2012-01-01") }
         it {
           should equal_to_dom(
-            '<input value="2012-01-01" class="from"
+            '<input value="2012-01-01" class="datagrid-range-from"
                 multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input value="2012-01-02" class="to"
+            '<input value="2012-01-02" class="datagrid-range-to"
                 multiple type="text" name="report[created_at][]"/>'
           )
         }
@@ -344,9 +344,9 @@ class MyTemplate
         let(:_range) { [nil, nil] }
         it {
           should equal_to_dom(
-            '<input class="from" multiple type="text" name="report[created_at][]"/>' \
+            '<input class="datagrid-range-from" multiple type="text" name="report[created_at][]"/>' \
             '<span class="separator date"> - </span>' \
-            '<input class="to" multiple type="text" name="report[created_at][]"/>'
+            '<input class="datagrid-range-to" multiple type="text" name="report[created_at][]"/>'
           )
         }
       end

From 36bf694a16106bee064c706eb85a13d62bbf504b Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 11:34:51 +0100
Subject: [PATCH 018/157] Add datagrid-dynamic- prefix for
 field/operation/value css classes

---
 lib/datagrid/form_builder.rb       |  6 +++---
 spec/datagrid/form_builder_spec.rb | 22 +++++++++++-----------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 7746068..617d492 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -137,7 +137,7 @@ def datagrid_dynamic_filter(filter, options = {})
           include_hidden: false,
           selected: field
         },
-        add_html_classes(options, "field")
+        add_html_classes(options, "datagrid-dynamic-field")
       )
       operation_input = dynamic_filter_select(
         filter.name, filter.operations_select,
@@ -147,9 +147,9 @@ def datagrid_dynamic_filter(filter, options = {})
           prompt: false,
           selected: operation
         },
-        add_html_classes(options, "operation")
+        add_html_classes(options, "datagrid-dynamic-operation")
       )
-      value_input = text_field(filter.name, **add_html_classes(options, "value"), value: value)
+      value_input = text_field(filter.name, **add_html_classes(options, "datagrid-dynamic-value"), value: value)
       [field_input, operation_input, value_input].join("\n").html_safe
     end
 
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 2f4df49..755b78e 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -654,7 +654,7 @@ class MyTemplate
       context "with no options" do
         let(:expected_html) do
           <<-HTML
-         <select class="field" name="report[condition][]" id="report_condition"><option value="id">Id</option>
+         <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition"><option value="id">Id</option>
          <option value="group_id">Group</option>
          <option value="name">Name</option>
          <option value="category">Category</option>
@@ -664,10 +664,10 @@ class MyTemplate
          <option value="confirmed">Confirmed</option>
          <option value="shipping_date">Shipping date</option>
          <option value="created_at">Created at</option>
-         <option value="updated_at">Updated at</option></select><select class="operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
+         <option value="updated_at">Updated at</option></select><select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
          <option value="=~">&asymp;</option>
          <option value="&gt;=">&ge;</option>
-         <option value="&lt;=">&le;</option></select><input class="value"  name="report[condition][]" type="text" id="report_condition">
+         <option value="&lt;=">&le;</option></select><input class="datagrid-dynamic-value"  name="report[condition][]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -678,11 +678,11 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-            <select class="field" name="report[condition][]" id="report_condition"><option selected value="id">id</option>
-       <option value="name">name</option></select><select class="operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
+            <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition"><option selected value="id">id</option>
+       <option value="name">name</option></select><select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
        <option value="=~">&asymp;</option>
        <option selected value="&gt;=">&ge;</option>
-       <option value="&lt;=">&le;</option></select><input class="value" name="report[condition][]" value="1" type="text"  id="report_condition">
+       <option value="&lt;=">&le;</option></select><input class="datagrid-dynamic-value" name="report[condition][]" value="1" type="text"  id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -694,8 +694,8 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <select class="field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><select class="operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
-       <option value="&lt;=">≤</option></select><input class="value" name="report[condition][]" type="text" id="report_condition">
+          <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
+       <option value="&lt;=">≤</option></select><input class="datagrid-dynamic-value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -707,8 +707,8 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <input class="field" name="report[condition][]" value="id" autocomplete="off" type="hidden" id="report_condition"><select class="operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
-       <option value="&lt;=">≤</option></select><input class="value" name="report[condition][]" type="text" id="report_condition">
+          <input class="datagrid-dynamic-field" name="report[condition][]" value="id" autocomplete="off" type="hidden" id="report_condition"><select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
+       <option value="&lt;=">≤</option></select><input class="datagrid-dynamic-value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -719,7 +719,7 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <select class="field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><input class="operation" name="report[condition][]" value="=" autocomplete="off" type="hidden" id="report_condition"><input class="value" name="report[condition][]" type="text" id="report_condition">
+          <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><input class="datagrid-dynamic-operation" name="report[condition][]" value="=" autocomplete="off" type="hidden" id="report_condition"><input class="datagrid-dynamic-value" name="report[condition][]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }

From a8d4cf6746a2d10f9ac6b436f5fba934c8769d53 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 11:41:46 +0100
Subject: [PATCH 019/157] Update CSS

---
 app/assets/stylesheets/datagrid.sass | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.sass b/app/assets/stylesheets/datagrid.sass
index 63fa4d6..f38e93c 100644
--- a/app/assets/stylesheets/datagrid.sass
+++ b/app/assets/stylesheets/datagrid.sass
@@ -65,14 +65,14 @@ table.datagrid
   a
     float: left
 
-  input[class*='filter']
+  input
     border: 2px solid #ccc
     border-radius: 4px
     float: left
     padding: 5px 12px
     width: 207px
 
-    &.from, &.to
+    &.datagrid-range-from, &.datagrid-range-to
       width: 83px
 
   select
@@ -83,26 +83,26 @@ table.datagrid
       border: 2px solid #ccc
       border-radius: 5px
       height: 100px
-    &.dynamic_filter
-      &.field
-        width: 178px
-      &.operation
-        margin-left: 7px
-        width: 50px
-  input.dynamic_filter.value
+  .datagrid-dynamic-field
+    width: 178px
+  .datagrid-dynamic-operation
+    margin-left: 7px
+    width: 50px
+  .datagrid-dynamic-value
     margin: 10px 0 0 $dg-form-label
     clear: both
 
   .separator
     float: left
     margin: 6px 4px 0
-  .enum_filter.checkboxes 
-    float: none
-    display: block
-    width: 100%
+  &.datagrid-checkboxes 
+    label
+      float: none
+      display: block
+      width: 100%
+      margin-left: 150px
     input
       margin: 7px
-    margin-left: 150px
 
 
 .datagrid-actions

From dc926b67bdba5b8e066b9b1298098f21b1030164 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 11:44:10 +0100
Subject: [PATCH 020/157] Rename separator to datagrid-range-separator

---
 app/assets/stylesheets/datagrid.sass      |  2 +-
 app/views/datagrid/_range_filter.html.erb |  2 +-
 spec/datagrid/form_builder_spec.rb        | 22 +++++++++++-----------
 3 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.sass b/app/assets/stylesheets/datagrid.sass
index f38e93c..915ceaf 100644
--- a/app/assets/stylesheets/datagrid.sass
+++ b/app/assets/stylesheets/datagrid.sass
@@ -92,7 +92,7 @@ table.datagrid
     margin: 10px 0 0 $dg-form-label
     clear: both
 
-  .separator
+  .datagrid-range-separator
     float: left
     margin: 6px 4px 0
   &.datagrid-checkboxes 
diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
index 7a8a123..1b90dc8 100644
--- a/app/views/datagrid/_range_filter.html.erb
+++ b/app/views/datagrid/_range_filter.html.erb
@@ -1,3 +1,3 @@
 <%= form.datagrid_filter_input(filter, **from_options) %>
-<span class="separator <%= filter.type %>"><%= I18n.t('datagrid.filters.range.separator') %></span>
+<span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
 <%= form.datagrid_filter_input(filter, **to_options) %>
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 755b78e..b619e3b 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -188,7 +188,7 @@ class MyTemplate
           should equal_to_dom(
             '<input value="1" id="from_hello" class="datagrid-range-from"
                 multiple type="text" name="report[group_id][]"/>' \
-            '<span class="separator integer"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2" id="to_hello" class="datagrid-range-to"
                 multiple type="text" name="report[group_id][]"/>'
           )
@@ -200,7 +200,7 @@ class MyTemplate
           should equal_to_dom(
             '<input value="10" class="datagrid-range-from"
                 multiple type="text" name="report[group_id][]"/>' \
-            '<span class="separator integer"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to"
                 multiple type="text" name="report[group_id][]"/>'
           )
@@ -212,7 +212,7 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input class="datagrid-range-from" multiple type="text" name="report[group_id][]"/>' \
-            '<span class="separator integer"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input value="10" class="datagrid-range-to" multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -224,7 +224,7 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="1" class="datagrid-range-from" multiple type="text" name="report[group_id][]"/>' \
-            '<span class="separator integer"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2" class="datagrid-range-to" multiple type="text" name="report[group_id][]"/>'
           )
         }
@@ -246,7 +246,7 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input class="datagrid-range-from" multiple type="text" name="report[group_id][]">
-            <span class="separator integer"> - </span>
+            <span class="datagrid-range-separator"> - </span>
             <input class="datagrid-range-to" multiple type="text" name="report[group_id][]">'
           )
         }
@@ -266,7 +266,7 @@ class MyTemplate
         should equal_to_dom(
           '<input value="1.5" class="datagrid-range-from"
               multiple type="text" name="report[rating][]"/>' \
-          '<span class="separator float"> - </span>' \
+          '<span class="datagrid-range-separator"> - </span>' \
           '<input value="2.5" class="datagrid-range-to"
               multiple type="text" name="report[rating][]"/>'
         )
@@ -286,7 +286,7 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="2012-01-03" class="datagrid-range-from" multiple type="text" name="report[created_at][]"/>' \
-            '<span class="separator date"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to" multiple type="text" name="report[created_at][]"/>'
           )
         }
@@ -303,7 +303,7 @@ class MyTemplate
           should equal_to_dom(
             '<input value="01/01/2013" class="datagrid-range-from"
                 multiple type="text" name="report[created_at][]"/>' \
-            '<span class="separator date"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input value="02/02/2013" class="datagrid-range-to"
                 multiple type="text" name="report[created_at][]"/>'
           )
@@ -315,7 +315,7 @@ class MyTemplate
           should equal_to_dom(
             '<input class="datagrid-range-from"
                 multiple type="text" name="report[created_at][]"/>' \
-            '<span class="separator date"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-03" class="datagrid-range-to"
                 multiple type="text"  name="report[created_at][]"/>'
           )
@@ -329,7 +329,7 @@ class MyTemplate
           should equal_to_dom(
             '<input value="2012-01-01" class="datagrid-range-from"
                 multiple type="text" name="report[created_at][]"/>' \
-            '<span class="separator date"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-02" class="datagrid-range-to"
                 multiple type="text" name="report[created_at][]"/>'
           )
@@ -345,7 +345,7 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input class="datagrid-range-from" multiple type="text" name="report[created_at][]"/>' \
-            '<span class="separator date"> - </span>' \
+            '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to" multiple type="text" name="report[created_at][]"/>'
           )
         }

From 350b6da11ae11210ced65223ab28863d2a90d62b Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 12:02:10 +0100
Subject: [PATCH 021/157] Rename datagrid_table autogenerated CSS classes to
 match modern CSS modular frameworks

---
 app/assets/stylesheets/datagrid.sass   | 18 +++----
 app/views/datagrid/_order_for.html.erb |  6 +--
 lib/datagrid/helper.rb                 |  2 +-
 lib/datagrid/renderer.rb               |  2 +-
 spec/datagrid/helper_spec.rb           | 70 +++++++++++++-------------
 5 files changed, 46 insertions(+), 52 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.sass b/app/assets/stylesheets/datagrid.sass
index 915ceaf..71e5b75 100644
--- a/app/assets/stylesheets/datagrid.sass
+++ b/app/assets/stylesheets/datagrid.sass
@@ -16,7 +16,7 @@ $dg-form-label: 150px
   zoom: 1
   *display: inline
 
-table.datagrid
+table.datagrid-table
   background-color: transparent
   border-collapse: collapse
   max-width: 100%
@@ -30,23 +30,17 @@ table.datagrid
     border: 1px solid #d6d6d6
     padding: 5px 10px
 
-    .order
+    .datagrid-order
       a.asc, a.desc
         text-decoration: none
         font-weight: normal
 
-    &.ordered
+    .datagrid-order-active-asc, .datagrid-order-active-desc
       background-color: #fff7d5
 
-      &.asc
-        a.asc
-          font-weight: bold
-          color: #d00
-
-      &.desc
-        a.desc
-          font-weight: bold
-          color: #d00
+      a.datagird-order-control-asc, a.datagird-order-control-desc
+        font-weight: bold
+        color: #d00
   .noresults
     text-align: center
     
diff --git a/app/views/datagrid/_order_for.html.erb b/app/views/datagrid/_order_for.html.erb
index 1545a8e..9960e00 100644
--- a/app/views/datagrid/_order_for.html.erb
+++ b/app/views/datagrid/_order_for.html.erb
@@ -1,10 +1,10 @@
-<div class="order">
+<div class="datagrid-order">
   <%= link_to(
       I18n.t("datagrid.table.order.asc").html_safe,
       datagrid_order_path(grid, column, false),
-      class: "asc") %>
+      class: "datagrid-order-control-asc") %>
   <%= link_to(
       I18n.t("datagrid.table.order.desc").html_safe,
       datagrid_order_path(grid, column, true),
-      class: "desc") %>
+      class: "datagrid-order-control-desc") %>
 </div>
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index 925af9d..4f4361a 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -151,7 +151,7 @@ def datagrid_renderer
 
     def datagrid_column_classes(grid, column)
       order_class = if grid.ordered_by?(column)
-                      ["ordered", grid.descending ? "desc" : "asc"]
+                      [grid.descending ? "datagrid-order-active-desc" : "datagrid-order-active-asc"]
                     end
       [column.name, order_class, column.options[:class]].compact.join(" ")
     end
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index d485241..1be59b9 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -36,7 +36,7 @@ def form_for(grid, options = {})
 
     def table(grid, assets, **options)
       options[:html] ||= {}
-      options[:html][:class] ||= "datagrid #{@template.dom_class(grid)}"
+      options[:html][:class] ||= "datagrid-table #{@template.dom_class(grid)}"
 
       _render_partial("table", options[:partials],
                       {
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 55617c5..bef7a42 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -42,7 +42,7 @@
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern(
-        "table.datagrid tr td.noresults" => 1
+        "table.datagrid-table tr td.noresults" => 1
       )
       expect(datagrid_table).to include(I18n.t("datagrid.no_results"))
     end
@@ -51,7 +51,7 @@
   describe ".datagrid_table" do
     it "should have grid class as html class on table" do
       expect(subject.datagrid_table(grid)).to match_css_pattern(
-        "table.datagrid.simple_report" => 1
+        "table.datagrid-table.simple_report" => 1
       )
     end
     it "should have namespaced grid class as html class on table" do
@@ -63,19 +63,19 @@ class TestGrid
         end
       end
       expect(subject.datagrid_table(Ns23::TestGrid.new)).to match_css_pattern(
-        "table.datagrid.ns23_test_grid" => 1
+        "table.datagrid-table.ns23_test_grid" => 1
       )
     end
     it "should return data table html" do
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern({
-                                                    "table.datagrid tr th.group div.order" => 1,
-                                                    "table.datagrid tr th.group" => /Group.*/,
-                                                    "table.datagrid tr th.name div.order" => 1,
-                                                    "table.datagrid tr th.name" => /Name.*/,
-                                                    "table.datagrid tr td.group" => "Pop",
-                                                    "table.datagrid tr td.name" => "Star"
+                                                    "table.datagrid-table tr th.group div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th.group" => /Group.*/,
+                                                    "table.datagrid-table tr th.name div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th.name" => /Name.*/,
+                                                    "table.datagrid-table tr td.group" => "Pop",
+                                                    "table.datagrid-table tr td.name" => "Star"
                                                   })
     end
 
@@ -84,25 +84,25 @@ class TestGrid
       datagrid_table = subject.datagrid_table(grid, [entry])
 
       expect(datagrid_table).to match_css_pattern({
-                                                    "table.datagrid tr th.group div.order" => 1,
-                                                    "table.datagrid tr th.group" => /Group.*/,
-                                                    "table.datagrid tr th.name div.order" => 1,
-                                                    "table.datagrid tr th.name" => /Name.*/,
-                                                    "table.datagrid tr td.group" => "Pop",
-                                                    "table.datagrid tr td.name" => "Star"
+                                                    "table.datagrid-table tr th.group div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th.group" => /Group.*/,
+                                                    "table.datagrid-table tr th.name div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th.name" => /Name.*/,
+                                                    "table.datagrid-table tr td.group" => "Pop",
+                                                    "table.datagrid-table tr td.name" => "Star"
                                                   })
     end
 
     it "should support no order given" do
-      expect(subject.datagrid_table(grid, [entry], order: false)).to match_css_pattern("table.datagrid th .order" => 0)
+      expect(subject.datagrid_table(grid, [entry], order: false)).to match_css_pattern("table.datagrid-table th .datagrid-order" => 0)
     end
 
     it "should support columns option" do
       expect(subject.datagrid_table(grid, [entry], columns: [:name])).to match_css_pattern(
-        "table.datagrid th.name" => 1,
-        "table.datagrid td.name" => 1,
-        "table.datagrid th.group" => 0,
-        "table.datagrid td.group" => 0
+        "table.datagrid-table th.name" => 1,
+        "table.datagrid-table td.name" => 1,
+        "table.datagrid-table th.group" => 0,
+        "table.datagrid-table td.group" => 0
       )
     end
 
@@ -117,10 +117,10 @@ class TestGrid
 
       it "should output only given column names" do
         expect(subject.datagrid_table(grid, [entry])).to match_css_pattern(
-          "table.datagrid th.name" => 1,
-          "table.datagrid td.name" => 1,
-          "table.datagrid th.category" => 0,
-          "table.datagrid td.category" => 0
+          "table.datagrid-table th.name" => 1,
+          "table.datagrid-table td.name" => 1,
+          "table.datagrid-table th.category" => 0,
+          "table.datagrid-table td.category" => 0
         )
       end
     end
@@ -168,8 +168,8 @@ class TestGrid
       end
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
-          "table.datagrid th.name" => 1,
-          "table.datagrid td.name" => 2
+          "table.datagrid-table th.name" => 1,
+          "table.datagrid-table td.name" => 2
         )
       end
     end
@@ -184,8 +184,8 @@ class TestGrid
       end
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
-          "table.datagrid th.name" => 1,
-          "table.datagrid td.name" => 2
+          "table.datagrid-table th.name" => 1,
+          "table.datagrid-table td.name" => 2
         )
       end
     end
@@ -218,7 +218,7 @@ class TestGrid
         column(:name)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name.ordered.asc" => "Star"
+        "tr td.name.datagrid-order-active-asc" => "Star"
       )
     end
     it "should add ordering classes to column" do
@@ -241,7 +241,7 @@ class TestGrid
         column(:name)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name.ordered.desc" => "Star"
+        "tr td.name.datagrid-order-active-desc" => "Star"
       )
     end
 
@@ -458,9 +458,9 @@ class OrderedGrid
       end
       object = OrderedGrid.new(descending: true, order: :category)
       expect(subject.datagrid_order_for(object, object.column_by_name(:category))).to equal_to_dom(<<~HTML)
-        <div class="order">
-        <a class="asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
-        <a class="desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">&darr;</a>
+        <div class="datagrid-order">
+        <a class="datagrid-order-control-asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
+        <a class="datagrid-order-control-desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">&darr;</a>
         </div>
       HTML
     end
@@ -665,8 +665,8 @@ def param_name
         end
       end
       expect(subject.datagrid_header(grid)).to equal_to_dom(<<~HTML)
-        <tr><th class="category ordered asc">Category<div class="order">
-        <a class="asc" href="/location?grid%5Bdescending%5D=false&amp;grid%5Border%5D=category">&uarr;</a><a class="desc" href="/location?grid%5Bdescending%5D=true&amp;grid%5Border%5D=category">&darr;</a>
+        <tr><th class="category datagrid-order-active-asc">Category<div class="datagrid-order">
+        <a class="datagrid-order-control-asc" href="/location?grid%5Bdescending%5D=false&amp;grid%5Border%5D=category">&uarr;</a><a class="datagrid-order-control-desc" href="/location?grid%5Bdescending%5D=true&amp;grid%5Border%5D=category">&darr;</a>
         </div>
         </th></tr>
       HTML

From 589daaa31170da1d64269615561b664b07ebea42 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 12:03:14 +0100
Subject: [PATCH 022/157] Rename .noresults .datagrid-noresults

---
 app/assets/stylesheets/datagrid.sass                       | 2 +-
 app/views/datagrid/_table.html.erb                         | 2 +-
 spec/datagrid/helper_spec.rb                               | 2 +-
 spec/support/test_partials/client/datagrid/_table.html.erb | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.sass b/app/assets/stylesheets/datagrid.sass
index 71e5b75..5f44b13 100644
--- a/app/assets/stylesheets/datagrid.sass
+++ b/app/assets/stylesheets/datagrid.sass
@@ -41,7 +41,7 @@ table.datagrid-table
       a.datagird-order-control-asc, a.datagird-order-control-desc
         font-weight: bold
         color: #d00
-  .noresults
+  .datagrid-noresults
     text-align: center
     
 .datagrid-form
diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
index 8708c05..0031506 100644
--- a/app/views/datagrid/_table.html.erb
+++ b/app/views/datagrid/_table.html.erb
@@ -13,7 +13,7 @@ Local variables:
       <% if assets.any? %>
         <%= datagrid_rows(grid, assets, **options) %>
       <% else %>
-        <tr><td class="noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
+        <tr><td class="datagrid-noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
       <% end %>
     </tbody>
   <% end %>
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index bef7a42..dc7cb09 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -42,7 +42,7 @@
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern(
-        "table.datagrid-table tr td.noresults" => 1
+        "table.datagrid-table tr td.datagrid-noresults" => 1
       )
       expect(datagrid_table).to include(I18n.t("datagrid.no_results"))
     end
diff --git a/spec/support/test_partials/client/datagrid/_table.html.erb b/spec/support/test_partials/client/datagrid/_table.html.erb
index 986ce52..22c46db 100644
--- a/spec/support/test_partials/client/datagrid/_table.html.erb
+++ b/spec/support/test_partials/client/datagrid/_table.html.erb
@@ -11,7 +11,7 @@ Local variables:
   </thead>
   <tbody>
     <% if assets.empty? %>
-    <tr><td class="noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
+    <tr><td class="datagrid-noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
     <% else %>
     <%= datagrid_rows(grid, assets, **options) %>
     <% end %>

From 544c734593bc77730d023d0708ef75801875f7f1 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 12:05:45 +0100
Subject: [PATCH 023/157] Cleanup css

---
 app/assets/stylesheets/datagrid.sass | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.sass b/app/assets/stylesheets/datagrid.sass
index 5f44b13..6119b6f 100644
--- a/app/assets/stylesheets/datagrid.sass
+++ b/app/assets/stylesheets/datagrid.sass
@@ -30,19 +30,20 @@ table.datagrid-table
     border: 1px solid #d6d6d6
     padding: 5px 10px
 
-    .datagrid-order
-      a.asc, a.desc
-        text-decoration: none
-        font-weight: normal
-
-    .datagrid-order-active-asc, .datagrid-order-active-desc
-      background-color: #fff7d5
-
-      a.datagird-order-control-asc, a.datagird-order-control-desc
-        font-weight: bold
-        color: #d00
-  .datagrid-noresults
-    text-align: center
+.datagrid-order
+  a.asc, a.desc
+    text-decoration: none
+    font-weight: normal
+
+.datagrid-order-active-asc, .datagrid-order-active-desc
+  background-color: #fff7d5
+
+  a.datagird-order-control-asc, a.datagird-order-control-desc
+    font-weight: bold
+    color: #d00
+
+.datagrid-noresults
+  text-align: center
     
 .datagrid-form
   background-color: #f0f0f0

From 6aa94896d91e2896645e8bb444a24a768f7974d1 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 13:52:19 +0100
Subject: [PATCH 024/157] Use html5 input types by default

---
 lib/datagrid/filters/base_filter.rb           |  3 +-
 lib/datagrid/filters/boolean_filter.rb        |  4 +-
 lib/datagrid/filters/date_filter.rb           |  2 +
 lib/datagrid/filters/date_time_filter.rb      |  2 +
 lib/datagrid/filters/dynamic_filter.rb        |  2 +
 lib/datagrid/filters/enum_filter.rb           |  2 +
 .../filters/extended_boolean_filter.rb        |  2 +
 lib/datagrid/filters/float_filter.rb          |  2 +
 lib/datagrid/filters/integer_filter.rb        |  2 +
 lib/datagrid/form_builder.rb                  | 21 +++--
 spec/datagrid/form_builder_spec.rb            | 79 +++++++++----------
 11 files changed, 72 insertions(+), 49 deletions(-)

diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index eee49eb..57e5e09 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -11,7 +11,8 @@ class FilteringError < StandardError
 module Datagrid
   module Filters
     class BaseFilter
-      class_attribute :input_helper_name, instance_writer: false
+      class_attribute :default_input_options, instance_writer: false, default: {type: 'text'}
+
       attr_accessor :grid_class, :options, :block, :name
 
       def initialize(grid_class, name, options = {}, &block)
diff --git a/lib/datagrid/filters/boolean_filter.rb b/lib/datagrid/filters/boolean_filter.rb
index 4248b7b..d54eea3 100644
--- a/lib/datagrid/filters/boolean_filter.rb
+++ b/lib/datagrid/filters/boolean_filter.rb
@@ -1,10 +1,12 @@
 # frozen_string_literal: true
 
 require "datagrid/utils"
-# @!visibility private
+
 module Datagrid
   module Filters
     class BooleanFilter < Datagrid::Filters::BaseFilter
+      self.default_input_options = {type: 'checkbox' }
+
       def parse(value)
         Datagrid::Utils.booleanize(value)
       end
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index 2a7d29d..3fe5428 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -7,6 +7,8 @@ module Filters
     class DateFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
+      self.default_input_options = { type: 'date' }
+
       def apply(grid_object, scope, value)
         value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
         super
diff --git a/lib/datagrid/filters/date_time_filter.rb b/lib/datagrid/filters/date_time_filter.rb
index 0014eb1..37effa0 100644
--- a/lib/datagrid/filters/date_time_filter.rb
+++ b/lib/datagrid/filters/date_time_filter.rb
@@ -7,6 +7,8 @@ module Filters
     class DateTimeFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
+      self.default_input_options = {type: 'datetime-local' }
+
       def parse(value)
         Datagrid::Utils.parse_datetime(value)
       end
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index 636512c..c4e4e06 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -19,6 +19,8 @@ class DynamicFilter < Datagrid::Filters::BaseFilter
       ].freeze
       AVAILABLE_OPERATIONS = %w[= =~ >= <=].freeze
 
+      self.default_input_options = {}
+
       def initialize(*)
         super
         options[:select] ||= default_select
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 687fb1b..841a0ba 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -7,6 +7,8 @@ module Filters
     class EnumFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::SelectOptions
 
+      self.default_input_options = {type: 'select' }
+
       def initialize(*args)
         super
         options[:multiple] = true if checkboxes?
diff --git a/lib/datagrid/filters/extended_boolean_filter.rb b/lib/datagrid/filters/extended_boolean_filter.rb
index 5cf418a..e2bbc6b 100644
--- a/lib/datagrid/filters/extended_boolean_filter.rb
+++ b/lib/datagrid/filters/extended_boolean_filter.rb
@@ -9,6 +9,8 @@ class ExtendedBooleanFilter < Datagrid::Filters::EnumFilter
       TRUTH_VALUES = [true, "true", "y", "yes"].freeze
       FALSE_VALUES = [false, "false", "n", "no"].freeze
 
+      self.default_input_options = {type: 'select' }
+
       def initialize(report, attribute, options = {}, &block)
         options[:select] = -> { boolean_select }
         super
diff --git a/lib/datagrid/filters/float_filter.rb b/lib/datagrid/filters/float_filter.rb
index 6cc4aa9..0257746 100644
--- a/lib/datagrid/filters/float_filter.rb
+++ b/lib/datagrid/filters/float_filter.rb
@@ -6,6 +6,8 @@ module Filters
     class FloatFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
+      self.default_input_options = {type: 'number', step: 'any' }
+
       def parse(value)
         return nil if value.blank?
 
diff --git a/lib/datagrid/filters/integer_filter.rb b/lib/datagrid/filters/integer_filter.rb
index 6395b0b..fd64bb2 100644
--- a/lib/datagrid/filters/integer_filter.rb
+++ b/lib/datagrid/filters/integer_filter.rb
@@ -7,6 +7,8 @@ module Filters
     class IntegerFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
+      self.default_input_options = {type: 'number', step: '1' }
+
       def parse(value)
         return nil if value.blank?
         if defined?(ActiveRecord) && value.is_a?(ActiveRecord::Base) &&
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 617d492..20a484f 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -30,9 +30,15 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       filter = datagrid_get_filter(attribute_or_filter)
       options = add_filter_options(filter, **options)
       type = options.delete(:type)&.to_sym
-      if options.key?(:value) && options[:value].nil? && %i[datetime-local date].include?(type)
-        # https://github.com/rails/rails/pull/53387
-        options[:value] = ""
+      if %i[datetime-local date].include?(type)
+        if options.key?(:value) && options[:value].nil? &&
+            # https://github.com/rails/rails/pull/53387
+            options[:value] = ""
+        end
+      else
+        if options[:value]
+          options[:value] = filter.format(options[:value])
+        end
       end
       case type
       when :"datetime-local"
@@ -42,10 +48,11 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       when :textarea
         text_area filter.name, value: object.filter_value_as_string(filter), **options, &block
       when :checkbox
-        # raise options.inspect
         check_box filter.name, options, options.fetch(:value, 1)
       when :hidden
         hidden_field filter.name, **options
+      when :number
+        number_field filter.name, **options
       when :select
         select(
           filter.name,
@@ -149,7 +156,7 @@ def datagrid_dynamic_filter(filter, options = {})
         },
         add_html_classes(options, "datagrid-dynamic-operation")
       )
-      value_input = text_field(filter.name, **add_html_classes(options, "datagrid-dynamic-value"), value: value)
+      value_input = datagrid_filter_input(filter.name, **add_html_classes(options, "datagrid-dynamic-value"), value: value)
       [field_input, operation_input, value_input].join("\n").html_safe
     end
 
@@ -181,7 +188,7 @@ def datagrid_range_filter(_type, filter, options = {})
     def datagrid_range_filter_options(object, filter, type, options)
       type_method_map = { from: :first, to: :last }
       options = add_html_classes(options, "datagrid-range-#{type}")
-      options[:value] = filter.format(object[filter.name]&.public_send(type_method_map[type]))
+      options[:value] = object[filter.name]&.public_send(type_method_map[type])
       # In case of datagrid ranged filter
       # from and to input will have same id
       if !options.key?(:id)
@@ -229,7 +236,7 @@ def render_partial(name, locals)
     end
 
     def add_filter_options(filter, **options)
-      { **filter.input_options, **options }
+      { **filter.default_input_options, **filter.input_options, **options }
     end
 
     class Error < StandardError
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index b619e3b..81255d8 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -56,7 +56,7 @@ class MyTemplate
       end
       it {
         should equal_to_dom(
-          '<input type="text" name="report[group_id]" id="report_group_id"/>'
+          '<input type="number" step="1" name="report[group_id]" id="report_group_id"/>'
         )
       }
 
@@ -64,7 +64,7 @@ class MyTemplate
         let(:view_options) { { partials: "anything" } }
         it {
           should equal_to_dom(
-            '<input type="text" name="report[group_id]" id="report_group_id"/>'
+            '<input type="number" step="1" name="report[group_id]" id="report_group_id"/>'
           )
         }
       end
@@ -80,7 +80,7 @@ class MyTemplate
       end
       it {
         should equal_to_dom(
-          '<input type="text" name="report[created_at]" id="report_created_at"/>'
+          '<input type="date" name="report[created_at]" id="report_created_at"/>'
         )
       }
       context "when special date format specified" do
@@ -92,28 +92,28 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<input value="01/02/2012" type="text"
+            '<input value="2012-01-02" type="date"
                 name="report[created_at]" id="report_created_at"/>'
           )
         }
       end
     end
     context "with input_options" do
-      context "type is date" do
+      context "date filter type is text" do
         let(:_filter) { :created_at }
         let(:_grid) do
           test_report do
             scope { Entry }
-            filter(:created_at, :date, input_options: { type: :date })
+            filter(:created_at, :date, input_options: { type: 'text' })
           end
         end
         it {
           should equal_to_dom(
-            '<input type="date" name="report[created_at]" id="report_created_at"/>'
+            '<input type="text" name="report[created_at]" id="report_created_at"/>'
           )
         }
       end
-      context "type is textarea" do
+      context "string filter type is textarea" do
         let(:_filter) { :name }
         let(:_grid) do
           test_report do
@@ -128,18 +128,18 @@ class MyTemplate
         }
       end
 
-      context "type is datetime-local" do
+      context "datetime filter type is text" do
         let(:_filter) { :created_at }
         let(:_grid) do
           test_report(created_at: Time.new(2024, 1, 1, 9, 25, 15)) do
             scope { Entry }
-            filter(:created_at, :datetime, input_options: { type: "datetime-local" })
+            filter(:created_at, :datetime, input_options: { type: "text" })
           end
         end
         it {
           should equal_to_dom(
-            '<input type="datetime-local"
-                value="2024-01-01T09:25:15" name="report[created_at]" id="report_created_at"/>'
+            '<input type="text" value="2024-01-01 09:25:15 +0100"
+              name="report[created_at]" id="report_created_at"/>'
           )
         }
 
@@ -149,14 +149,13 @@ class MyTemplate
           end
           it {
             should equal_to_dom(
-              '<input type="datetime-local" value=""
-                  name="report[created_at]" id="report_created_at"/>'
+              '<input type="text" name="report[created_at]" id="report_created_at"/>'
             )
           }
         end
       end
 
-      context "type is date" do
+      context "datetime filter type is date" do
         let(:_filter) { :created_at }
         let(:_grid) do
           test_report(created_at: Date.new(2024, 1, 1)) do
@@ -187,10 +186,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="1" id="from_hello" class="datagrid-range-from"
-                multiple type="text" name="report[group_id][]"/>' \
+                multiple type="number" step="1" name="report[group_id][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2" id="to_hello" class="datagrid-range-to"
-                multiple type="text" name="report[group_id][]"/>'
+                multiple type="number" step="1" name="report[group_id][]"/>'
           )
         }
       end
@@ -199,10 +198,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="10" class="datagrid-range-from"
-                multiple type="text" name="report[group_id][]"/>' \
+                multiple type="number" step="1" name="report[group_id][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to"
-                multiple type="text" name="report[group_id][]"/>'
+                multiple type="number" step="1" name="report[group_id][]"/>'
           )
         }
         it { should be_html_safe }
@@ -211,9 +210,9 @@ class MyTemplate
         let(:_range) { [nil, 10] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="text" name="report[group_id][]"/>' \
+            '<input class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="10" class="datagrid-range-to" multiple type="text" name="report[group_id][]"/>'
+            '<input value="10" class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][]"/>'
           )
         }
         it { should be_html_safe }
@@ -223,9 +222,9 @@ class MyTemplate
         let(:_range) { 2..1 }
         it {
           should equal_to_dom(
-            '<input value="1" class="datagrid-range-from" multiple type="text" name="report[group_id][]"/>' \
+            '<input value="1" class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="2" class="datagrid-range-to" multiple type="text" name="report[group_id][]"/>'
+            '<input value="2" class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][]"/>'
           )
         }
       end
@@ -245,9 +244,9 @@ class MyTemplate
         let(:_range) { nil }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="text" name="report[group_id][]">
+            '<input class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][]">
             <span class="datagrid-range-separator"> - </span>
-            <input class="datagrid-range-to" multiple type="text" name="report[group_id][]">'
+            <input class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][]">'
           )
         }
       end
@@ -265,10 +264,10 @@ class MyTemplate
       it {
         should equal_to_dom(
           '<input value="1.5" class="datagrid-range-from"
-              multiple type="text" name="report[rating][]"/>' \
+              multiple type="number" step="any" name="report[rating][]"/>' \
           '<span class="datagrid-range-separator"> - </span>' \
           '<input value="2.5" class="datagrid-range-to"
-              multiple type="text" name="report[rating][]"/>'
+              multiple type="number" step="any" name="report[rating][]"/>'
         )
       }
     end
@@ -285,9 +284,9 @@ class MyTemplate
         let(:_range) { ["2012-01-03", nil] }
         it {
           should equal_to_dom(
-            '<input value="2012-01-03" class="datagrid-range-from" multiple type="text" name="report[created_at][]"/>' \
+            '<input value="2012-01-03" class="datagrid-range-from" multiple type="date" name="report[created_at][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" multiple type="text" name="report[created_at][]"/>'
+            '<input class="datagrid-range-to" multiple type="date" name="report[created_at][]" value=""/>'
           )
         }
         it { should be_html_safe }
@@ -301,11 +300,11 @@ class MyTemplate
         let(:_range) { ["2013/01/01", "2013/02/02"] }
         it {
           should equal_to_dom(
-            '<input value="01/01/2013" class="datagrid-range-from"
-                multiple type="text" name="report[created_at][]"/>' \
+            '<input value="2013-01-01" class="datagrid-range-from"
+                multiple type="date" name="report[created_at][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="02/02/2013" class="datagrid-range-to"
-                multiple type="text" name="report[created_at][]"/>'
+            '<input value="2013-02-02" class="datagrid-range-to"
+                multiple type="date" name="report[created_at][]"/>'
           )
         }
       end
@@ -314,10 +313,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input class="datagrid-range-from"
-                multiple type="text" name="report[created_at][]"/>' \
+                multiple type="date" value="" name="report[created_at][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-03" class="datagrid-range-to"
-                multiple type="text"  name="report[created_at][]"/>'
+                multiple type="date"  name="report[created_at][]"/>'
           )
         }
         it { should be_html_safe }
@@ -328,10 +327,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="2012-01-01" class="datagrid-range-from"
-                multiple type="text" name="report[created_at][]"/>' \
+                multiple type="date" name="report[created_at][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-02" class="datagrid-range-to"
-                multiple type="text" name="report[created_at][]"/>'
+                multiple type="date" name="report[created_at][]"/>'
           )
         }
       end
@@ -344,9 +343,9 @@ class MyTemplate
         let(:_range) { [nil, nil] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="text" name="report[created_at][]"/>' \
+            '<input class="datagrid-range-from" multiple type="date" value="" name="report[created_at][]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" multiple type="text" name="report[created_at][]"/>'
+            '<input class="datagrid-range-to" multiple type="date" value="" name="report[created_at][]"/>'
           )
         }
       end
@@ -557,7 +556,7 @@ class MyTemplate
       let(:_filter) { :group_id }
       it {
         should equal_to_dom(
-          '<input type="text" name="report[group_id]" id="report_group_id"/>'
+          '<input type="number" step="any" name="report[group_id]" id="report_group_id"/>'
         )
       }
     end

From e486c6b31706a8a53f0333bc397e39c2d45ac42c Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 14:11:35 +0100
Subject: [PATCH 025/157] Rename several css classes

---
 app/assets/stylesheets/datagrid.sass                       | 6 +++---
 app/views/datagrid/_table.html.erb                         | 2 +-
 lib/datagrid/filters/enum_filter.rb                        | 2 +-
 spec/datagrid/form_builder_spec.rb                         | 6 ------
 spec/datagrid/helper_spec.rb                               | 2 +-
 spec/support/test_partials/client/datagrid/_table.html.erb | 2 +-
 6 files changed, 7 insertions(+), 13 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.sass b/app/assets/stylesheets/datagrid.sass
index 6119b6f..0f25722 100644
--- a/app/assets/stylesheets/datagrid.sass
+++ b/app/assets/stylesheets/datagrid.sass
@@ -31,7 +31,7 @@ table.datagrid-table
     padding: 5px 10px
 
 .datagrid-order
-  a.asc, a.desc
+  a.datagird-order-control-asc, a.datagird-order-control-desc
     text-decoration: none
     font-weight: normal
 
@@ -42,7 +42,7 @@ table.datagrid-table
     font-weight: bold
     color: #d00
 
-.datagrid-noresults
+.datagrid-no-results
   text-align: center
     
 .datagrid-form
@@ -90,7 +90,7 @@ table.datagrid-table
   .datagrid-range-separator
     float: left
     margin: 6px 4px 0
-  &.datagrid-checkboxes 
+  &.datagrid-enum-checkboxes 
     label
       float: none
       display: block
diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
index 0031506..e590d2c 100644
--- a/app/views/datagrid/_table.html.erb
+++ b/app/views/datagrid/_table.html.erb
@@ -13,7 +13,7 @@ Local variables:
       <% if assets.any? %>
         <%= datagrid_rows(grid, assets, **options) %>
       <% else %>
-        <tr><td class="datagrid-noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
+        <tr><td class="datagrid-no-results" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
       <% end %>
     </tbody>
   <% end %>
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 841a0ba..75817e0 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -23,7 +23,7 @@ def parse(value)
 
       def default_html_classes
         res = super
-        res.push("datagrid-checkboxes") if checkboxes?
+        res.push("datagrid-enum-checkboxes") if checkboxes?
         res
       end
 
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 81255d8..1162c05 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -20,12 +20,6 @@ class MyTemplate
   let(:view_options) { {} }
 
   describe ".datagrid_filter" do
-    it "should work for every filter type" do
-      Datagrid::Filters::FILTER_TYPES.each_value do |klass|
-        expect(Datagrid::FormBuilder.instance_methods.map(&:to_sym)).to include(klass.form_builder_helper_name)
-      end
-    end
-
     subject do
       view.datagrid_filter(_filter, **_filter_options, &_filter_block)
     end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index dc7cb09..31b956f 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -42,7 +42,7 @@
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern(
-        "table.datagrid-table tr td.datagrid-noresults" => 1
+        "table.datagrid-table tr td.datagrid-no-results" => 1
       )
       expect(datagrid_table).to include(I18n.t("datagrid.no_results"))
     end
diff --git a/spec/support/test_partials/client/datagrid/_table.html.erb b/spec/support/test_partials/client/datagrid/_table.html.erb
index 22c46db..84ff466 100644
--- a/spec/support/test_partials/client/datagrid/_table.html.erb
+++ b/spec/support/test_partials/client/datagrid/_table.html.erb
@@ -11,7 +11,7 @@ Local variables:
   </thead>
   <tbody>
     <% if assets.empty? %>
-    <tr><td class="datagrid-noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
+    <tr><td class="datagrid-no-results" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
     <% else %>
     <%= datagrid_rows(grid, assets, **options) %>
     <% end %>

From 5ebe3e043d8a49a665696c507aa1961b2a4940c8 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 15:45:14 +0100
Subject: [PATCH 026/157] Migration document prototype

---
 VERSION2.md | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)
 create mode 100644 VERSION2.md

diff --git a/VERSION2.md b/VERSION2.md
new file mode 100644
index 0000000..fa0ac45
--- /dev/null
+++ b/VERSION2.md
@@ -0,0 +1,115 @@
+# Datagrid Version 2
+
+Datagrid v1 was released Sep 19 2013 - more than 10 years ago.
+A lot of new things had happened during this period and it is time.
+It caused the library to be designed without support of those technologies
+and implementing some of them would cause a breaking change.
+And now it is time to indroduce them with Version 2.
+
+List of things introduces:
+
+1. Ruby infinite ranges for range filters.
+1. Modern modular CSS classes.
+1. HTML5 input types: number, date, datetime-local.
+1. HTML5 input [names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
+
+## Ruby Infinite Ranges
+
+## Modern CSS classes
+
+Built-in generated CSS classes renamed to match modern CSS naming conventions
+and avoid collisions with other libraries:
+
+| Old Name     | New Name                            |
+|--------------|-------------------------------------|
+| filter       | datagrid-filter                     |
+| from         | datagrid-input-from                 |
+| to           | datagrid-input-to                   |
+| noresults    | datagrid-no-results                 |
+| datagrid     | datagrid-table                      |
+| order        | datagrid-order                      |
+| a.asc        | datagrid-order-control-asc          |
+| a.desc       | datagrid-order-control-desc         |
+| ordered.asc  | datagrid-order-active-asc           |
+| ordered.desc | datagrid-order-active-desc          |
+| field        | datagrid-dynamic-field              |
+| operation    | datagrid-dynamic-operation          |
+| value        | datagrid-dynamic-value              |
+| separator    | datgrid-range-separator             |
+| checkboxes   | datagrid-enum-checkboxes            |
+
+A few automatically generated classes were moved from `<input/>` to `<div class="datagrid-filter">`
+to make sure they are editable through datagrid partials.
+
+### Example
+
+The difference in layout generation from  v1 to v2
+
+``` ruby
+datagrid_form_for(@grid)
+```
+
+Version 1 layout:
+
+``` html
+<form class="datagrid-form partial_default_grid" id="new_g"
+    action="/users" accept-charset="UTF-8" method="get">
+  <input name="utf8" type="hidden" value="✓" autocomplete="off" />
+
+  <div class="datagrid-filter filter">
+    <label for="g_id">Id</label>
+    <input class="id integer_filter from" multiple type="text" name="g[id][]" />
+    <span class="separator integer"> - </span>
+    <input class="id integer_filter to" multiple type="text" name="g[id][]" />
+  </div>
+
+  <div class="datagrid-filter filter">
+    <label for="g_group_id">Group</label>
+    <label class="group_id enum_filter checkboxes" for="g_group_id_1">
+      <input id="g_group_id_1" type="checkbox" value="1" name="g[group_id][]" />1
+    </label>
+    <label class="group_id enum_filter checkboxes" for="g_group_id_2">
+      <input id="g_group_id_2" type="checkbox" value="2" name="g[group_id][]" />2
+    </label>
+  </div>
+
+  <div class="datagrid-actions">
+    <input type="submit" name="commit" value="Search"
+        class="datagrid-submit" data-disable-with="Search" />
+    <a class="datagrid-reset" href="/location">Reset</a>
+  </div>
+</form>
+```
+
+Version 2 layout:
+
+TODO
+
+``` html
+```
+
+## HTML5 input types
+
+Version 1 generated `<input type="text"/>` for every filter type.
+Version 2 uses the appropriate input type for each filter type:
+
+| Type       | HTML Input Element                         |
+|------------|--------------------------------------------|
+| string     | `<input type="text"/>`                     |
+| boolean    | `<input type="checkbox"/>`                 |
+| date       | `<input type="date"/>`                     |
+| datetime   | `<input type="datetime-local"/>`           |
+| enum       | `<select>`                                 |
+| xboolean   | `<select>`                                 |
+| float      | `<input type="number" step="any"/>`        |
+| integer    | `<input type="number" step="1"/>`          |
+
+The default behavior can be changed back by using `input_options`:
+
+``` ruby
+filter(:created_at, :date, range: true, input_options: {type: 'text'})
+filter(:created_at, :datetime, range: true, input_options: {type: 'text'})
+filter(:text, :string, input_options: {type: 'textarea'})
+```
+
+## Names collision restriction

From 76e3aa3658494adec4099d84e4981d6dd02bcd8c Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 15:48:59 +0100
Subject: [PATCH 027/157] Remove automatically generated css classes from grid
 class

---
 lib/datagrid/renderer.rb     |  4 ++--
 spec/datagrid/helper_spec.rb | 18 +++---------------
 2 files changed, 5 insertions(+), 17 deletions(-)

diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index 1be59b9..f7c062d 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -29,14 +29,14 @@ def format_value(grid, column, asset)
     def form_for(grid, options = {})
       options[:method] ||= :get
       options[:html] ||= {}
-      options[:html][:class] ||= "datagrid-form #{@template.dom_class(grid)}"
+      options[:html][:class] ||= "datagrid-form"
       options[:as] ||= grid.param_name
       _render_partial("form", options[:partials], { grid: grid, options: options })
     end
 
     def table(grid, assets, **options)
       options[:html] ||= {}
-      options[:html][:class] ||= "datagrid-table #{@template.dom_class(grid)}"
+      options[:html][:class] ||= "datagrid-table"
 
       _render_partial("table", options[:partials],
                       {
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 31b956f..fdb1431 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -51,19 +51,7 @@
   describe ".datagrid_table" do
     it "should have grid class as html class on table" do
       expect(subject.datagrid_table(grid)).to match_css_pattern(
-        "table.datagrid-table.simple_report" => 1
-      )
-    end
-    it "should have namespaced grid class as html class on table" do
-      module ::Ns23
-        class TestGrid
-          include Datagrid
-          scope { Entry }
-          column(:id)
-        end
-      end
-      expect(subject.datagrid_table(Ns23::TestGrid.new)).to match_css_pattern(
-        "table.datagrid-table.ns23_test_grid" => 1
+        "table.datagrid-table" => 1
       )
     end
     it "should return data table html" do
@@ -481,7 +469,7 @@ class FormForGrid
       end
       object = FormForGrid.new(category: "hello")
       expect(subject.datagrid_form_for(object, url: "/grid")).to match_css_pattern(
-        "form.datagrid-form.form_for_grid[action='/grid']" => 1,
+        "form.datagrid-form[action='/grid']" => 1,
         "form input[name=utf8]" => 1,
         "form .datagrid-filter label" => "Category",
         "form .datagrid-filter-category input[name='form_for_grid[category]'][value=hello]" => 1,
@@ -498,7 +486,7 @@ class TestGrid
         end
       end
       expect(subject.datagrid_form_for(Ns22::TestGrid.new, url: "grid")).to match_css_pattern(
-        "form.datagrid-form.ns22_test_grid" => 1,
+        "form.datagrid-form" => 1,
         "form.datagrid-form label[for=ns22_test_grid_id]" => 1,
         "form.datagrid-form input#ns22_test_grid_id[name='ns22_test_grid[id]']" => 1
       )

From 527f8d6f2bb82cdacd9a482c552d0655dff2284a Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 16:07:40 +0100
Subject: [PATCH 028/157] Make datagrid-table datagrid-form classes explicitly
 set in views

---
 app/views/datagrid/_form.html.erb  | 2 +-
 app/views/datagrid/_table.html.erb | 2 +-
 lib/datagrid/renderer.rb           | 5 -----
 3 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index 06f0414..9648325 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_for grid, **options do |f| -%>
+<%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
   <% grid.filters.each do |filter| %>
     <div class="datagrid-filter <%= filter.default_html_classes.join(' ') %>">
       <%= f.datagrid_label filter %>
diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
index e590d2c..dff7bef 100644
--- a/app/views/datagrid/_table.html.erb
+++ b/app/views/datagrid/_table.html.erb
@@ -5,7 +5,7 @@ Local variables:
 * options - passed options Hash
 %>
 <% if grid.html_columns(*options[:columns]).any? %>
-  <%= content_tag :table, options[:html] do %>
+  <%= content_tag :table, class: 'datagrid-table', **options.fetch(:html, {}) do %>
     <thead>
       <%= datagrid_header(grid, options) %>
     </thead>
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index f7c062d..23efd30 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -28,16 +28,11 @@ def format_value(grid, column, asset)
 
     def form_for(grid, options = {})
       options[:method] ||= :get
-      options[:html] ||= {}
-      options[:html][:class] ||= "datagrid-form"
       options[:as] ||= grid.param_name
       _render_partial("form", options[:partials], { grid: grid, options: options })
     end
 
     def table(grid, assets, **options)
-      options[:html] ||= {}
-      options[:html][:class] ||= "datagrid-table"
-
       _render_partial("table", options[:partials],
                       {
                         grid: grid,

From 874122c40c327340307b913209f6e72179dacea0 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 19:52:20 +0100
Subject: [PATCH 029/157] Remove unnecessary html_safe calls

---
 app/views/datagrid/_form.html.erb      |  4 ++--
 app/views/datagrid/_order_for.html.erb |  4 ++--
 app/views/datagrid/_table.html.erb     |  4 ++--
 lib/datagrid/filters/dynamic_filter.rb |  2 +-
 lib/datagrid/renderer.rb               | 24 ++++++++++--------------
 5 files changed, 17 insertions(+), 21 deletions(-)

diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index 9648325..84cf58e 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -6,7 +6,7 @@
     </div>
   <% end %>
   <div class="datagrid-actions">
-    <%= f.submit I18n.t("datagrid.form.search").html_safe, class: "datagrid-submit" %>
-    <%= link_to I18n.t('datagrid.form.reset').html_safe, url_for(grid.to_param => {}), class: "datagrid-reset" %>
+    <%= f.submit I18n.t("datagrid.form.search"), class: "datagrid-submit" %>
+    <%= link_to I18n.t('datagrid.form.reset'), url_for(grid.to_param => {}), class: "datagrid-reset" %>
   </div>
 <% end -%>
diff --git a/app/views/datagrid/_order_for.html.erb b/app/views/datagrid/_order_for.html.erb
index 9960e00..1c33c37 100644
--- a/app/views/datagrid/_order_for.html.erb
+++ b/app/views/datagrid/_order_for.html.erb
@@ -1,10 +1,10 @@
 <div class="datagrid-order">
   <%= link_to(
-      I18n.t("datagrid.table.order.asc").html_safe,
+      I18n.t("datagrid.table.order.asc"),
       datagrid_order_path(grid, column, false),
       class: "datagrid-order-control-asc") %>
   <%= link_to(
-      I18n.t("datagrid.table.order.desc").html_safe,
+      I18n.t("datagrid.table.order.desc"),
       datagrid_order_path(grid, column, true),
       class: "datagrid-order-control-desc") %>
 </div>
diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
index dff7bef..0b5ff24 100644
--- a/app/views/datagrid/_table.html.erb
+++ b/app/views/datagrid/_table.html.erb
@@ -13,10 +13,10 @@ Local variables:
       <% if assets.any? %>
         <%= datagrid_rows(grid, assets, **options) %>
       <% else %>
-        <tr><td class="datagrid-no-results" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
+        <tr><td class="datagrid-no-results" colspan="100%"><%= I18n.t('datagrid.no_results') %></td></tr>
       <% end %>
     </tbody>
   <% end %>
 <% else -%>
-  <%= I18n.t("datagrid.table.no_columns").html_safe %>
+  <%= I18n.t("datagrid.table.no_columns") %>
 <% end %>
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index c4e4e06..5e08b95 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -81,7 +81,7 @@ def operations
 
       def operations_select
         operations.map do |operation|
-          [I18n.t(operation, scope: "datagrid.filters.dynamic.operations").html_safe, operation]
+          [I18n.t(operation, scope: "datagrid.filters.dynamic.operations"), operation]
         end
       end
 
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index 23efd30..30149ed 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -49,11 +49,11 @@ def header(grid, options = {})
     end
 
     def rows(grid, assets = grid.assets, **options, &block)
-      result = assets.map do |asset|
-        row(grid, asset, **options, &block)
-      end.to_a.join
-
-      _safe(result)
+      @template.safe_join(
+        assets.map do |asset|
+          row(grid, asset, **options, &block)
+        end.to_a
+      )
     end
 
     def row(grid, asset, **options, &block)
@@ -69,24 +69,20 @@ def order_for(grid, column, options = {})
 
     def order_path(grid, column, descending, request)
       column = grid.column_by_name(column)
-      query = request ? request.query_parameters : {}
+      query = request&.query_parameters || {}
       ActionDispatch::Http::URL.path_for(
-        path: request ? request.path : "/",
+        path: request&.path || "/",
         params: query.merge(grid.query_params(order: column.name, descending: descending))
       )
     end
 
     private
 
-    def _safe(string)
-      string.respond_to?(:html_safe) ? string.html_safe : string
-    end
-
     def _render_partial(partial_name, partials_path, locals = {})
       @template.render({
-                         partial: File.join(partials_path || "datagrid", partial_name),
-                         locals: locals
-                       })
+        partial: File.join(partials_path || "datagrid", partial_name),
+        locals: locals
+      })
     end
   end
 

From c9ec12446fbf72777f80e584a5cccb7fe1612729 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 20:25:32 +0100
Subject: [PATCH 030/157] Fully removed column url option

---
 lib/datagrid/renderer.rb     |  9 +--------
 spec/datagrid/helper_spec.rb | 24 ++----------------------
 2 files changed, 3 insertions(+), 30 deletions(-)

diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index 30149ed..8c62935 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -16,14 +16,7 @@ def initialize(template)
     def format_value(grid, column, asset)
       column = grid.column_by_name(column) if column.is_a?(String) || column.is_a?(Symbol)
 
-      value = grid.html_value(column, @template, asset)
-
-      url = column.options[:url]&.call(asset)
-      if url
-        @template.link_to(value, url)
-      else
-        value
-      end
+      grid.html_value(column, @template, asset)
     end
 
     def form_for(grid, options = {})
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index fdb1431..c8bb509 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -180,26 +180,6 @@
   end
 
   describe ".datagrid_rows" do
-    it "should support urls" do
-      rp = test_report do
-        scope { Entry }
-        column(:name, url: lambda(&:name))
-      end
-      expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name a[href=Star]" => "Star"
-      )
-    end
-
-    it "should support conditional urls" do
-      rp = test_report do
-        scope { Entry }
-        column(:name, url: ->(_model) { false })
-      end
-      expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name" => "Star"
-      )
-    end
-
     it "should add ordering classes to column" do
       rp = test_report(order: :name) do
         scope { Entry }
@@ -447,8 +427,8 @@ class OrderedGrid
       object = OrderedGrid.new(descending: true, order: :category)
       expect(subject.datagrid_order_for(object, object.column_by_name(:category))).to equal_to_dom(<<~HTML)
         <div class="datagrid-order">
-        <a class="datagrid-order-control-asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
-        <a class="datagrid-order-control-desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">&darr;</a>
+        <a class="datagrid-order-control-asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">↑</a>
+        <a class="datagrid-order-control-desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">↓</a>
         </div>
       HTML
     end

From 0c6b9cafbddbd0877fb62de1fca43000c3ad6ac1 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 4 Nov 2024 20:27:33 +0100
Subject: [PATCH 031/157] Remove composite filters completely

---
 lib/datagrid/filters.rb                       |  2 -
 lib/datagrid/filters/composite_filters.rb     | 47 ------------
 .../filters/composite_filters_spec.rb         | 76 -------------------
 3 files changed, 125 deletions(-)
 delete mode 100644 lib/datagrid/filters/composite_filters.rb
 delete mode 100644 spec/datagrid/filters/composite_filters_spec.rb

diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 67f387b..69257a1 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -12,7 +12,6 @@ module Filters
     require "datagrid/filters/date_time_filter"
     require "datagrid/filters/default_filter"
     require "datagrid/filters/integer_filter"
-    require "datagrid/filters/composite_filters"
     require "datagrid/filters/string_filter"
     require "datagrid/filters/float_filter"
     require "datagrid/filters/dynamic_filter"
@@ -39,7 +38,6 @@ def self.included(base)
       base.extend ClassMethods
       base.class_eval do
         include Datagrid::Core
-        include Datagrid::Filters::CompositeFilters
         class_attribute :filters_array, default: []
       end
     end
diff --git a/lib/datagrid/filters/composite_filters.rb b/lib/datagrid/filters/composite_filters.rb
deleted file mode 100644
index 53cac78..0000000
--- a/lib/datagrid/filters/composite_filters.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-module Datagrid
-  module Filters
-    # @!visibility private
-    module CompositeFilters
-      def self.included(base)
-        base.extend ClassMethods
-      end
-
-      # @!visibility private
-      module ClassMethods
-        def date_range_filters(field, from_options = {}, to_options = {})
-          Utils.warn_once("date_range_filters is deprecated in favor of range option for date filter")
-          from_options = normalize_composite_filter_options(from_options, field)
-          to_options = normalize_composite_filter_options(to_options, field)
-
-          filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :date,
-                 **from_options) do |date, scope, grid|
-            grid.driver.greater_equal(scope, field, date)
-          end
-          filter(to_options[:name] || :"to_#{field.to_s.tr('.', '_')}", :date, **to_options) do |date, scope, grid|
-            grid.driver.less_equal(scope, field, date)
-          end
-        end
-
-        def integer_range_filters(field, from_options = {}, to_options = {})
-          Utils.warn_once("integer_range_filters is deprecated in favor of range option for integer filter")
-          from_options = normalize_composite_filter_options(from_options, field)
-          to_options = normalize_composite_filter_options(to_options, field)
-          filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :integer,
-                 **from_options) do |value, scope, grid|
-            grid.driver.greater_equal(scope, field, value)
-          end
-          filter(to_options[:name] || :"to_#{field.to_s.tr('.', '_')}", :integer, **to_options) do |value, scope, grid|
-            grid.driver.less_equal(scope, field, value)
-          end
-        end
-
-        def normalize_composite_filter_options(options, _field)
-          options = { name: options } if options.is_a?(String) || options.is_a?(Symbol)
-          options
-        end
-      end
-    end
-  end
-end
diff --git a/spec/datagrid/filters/composite_filters_spec.rb b/spec/datagrid/filters/composite_filters_spec.rb
deleted file mode 100644
index 3a0e96c..0000000
--- a/spec/datagrid/filters/composite_filters_spec.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-describe Datagrid::Filters::CompositeFilters do
-  describe ".date_range_filters" do
-    it "should generate from date and to date filters" do
-      e1 = Entry.create!(shipping_date: 6.days.ago)
-      e2 = Entry.create!(shipping_date: 4.days.ago)
-      e3 = Entry.create!(shipping_date: 1.days.ago)
-      assets = test_report(from_shipping_date: 5.days.ago, to_shipping_date: 2.day.ago) do
-        scope { Entry }
-        silence_warnings do
-          date_range_filters(:shipping_date)
-        end
-      end.assets
-
-      expect(assets).to include(e2)
-      expect(assets).not_to include(e1, e3)
-    end
-
-    it "should support options" do
-      report = test_report do
-        silence_warnings do
-          date_range_filters(:shipping_date, { default: 10.days.ago.to_date }, { default: Date.today })
-        end
-      end
-      expect(report.from_shipping_date).to eq(10.days.ago.to_date)
-      expect(report.to_shipping_date).to eq(Date.today)
-    end
-    it "should support table name in field" do
-      report = test_report do
-        silence_warnings do
-          date_range_filters("entries.shipping_date", { default: 10.days.ago.to_date }, { default: Date.today })
-        end
-      end
-      expect(report.from_entries_shipping_date).to eq(10.days.ago.to_date)
-      expect(report.to_entries_shipping_date).to eq(Date.today)
-    end
-  end
-
-  describe ".integer_range_filters" do
-    it "should generate from integer and to integer filters" do
-      e1 = Entry.create!(group_id: 1)
-      e2 = Entry.create!(group_id: 3)
-      e3 = Entry.create!(group_id: 5)
-      assets = test_report(from_group_id: 2, to_group_id: 4) do
-        scope { Entry }
-        silence_warnings do
-          integer_range_filters(:group_id)
-        end
-      end.assets
-
-      expect(assets).to include(e2)
-      expect(assets).not_to include(e1, e3)
-    end
-    it "should support options" do
-      report = test_report do
-        silence_warnings do
-          integer_range_filters(:group_id, { default: 0 }, { default: 100 })
-        end
-      end
-      expect(report.from_group_id).to eq(0)
-      expect(report.to_group_id).to eq(100)
-    end
-    it "should table name in field name" do
-      report = test_report do
-        silence_warnings do
-          integer_range_filters("entries.group_id", { default: 0 }, { default: 100 })
-        end
-      end
-      expect(report.from_entries_group_id).to eq(0)
-      expect(report.to_entries_group_id).to eq(100)
-    end
-  end
-end

From a1a47a5e5b79bc3bc3c4c21d53f4dcc5c7c5c0a9 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 5 Nov 2024 10:07:07 +0100
Subject: [PATCH 032/157] Cleanup

---
 lib/datagrid.rb              | 10 ++++++++++
 lib/datagrid/active_model.rb | 12 +++++-------
 lib/datagrid/core.rb         |  5 +----
 lib/datagrid/filters.rb      | 11 +++++------
 4 files changed, 21 insertions(+), 17 deletions(-)

diff --git a/lib/datagrid.rb b/lib/datagrid.rb
index 616d779..6955a3a 100644
--- a/lib/datagrid.rb
+++ b/lib/datagrid.rb
@@ -31,6 +31,16 @@ module Datagrid
 
   autoload :Engine
 
+  # extend ActiveSupport::Concern
+
+  # included do
+    # include ::Datagrid::Core
+    # include ::Datagrid::ActiveModel
+    # include ::Datagrid::Filters
+    # include ::Datagrid::Columns
+    # include ::Datagrid::ColumnNamesAttribute
+    # include ::Datagrid::Ordering
+  # end
   # @!visibility private
   def self.included(base)
     base.class_eval do
diff --git a/lib/datagrid/active_model.rb b/lib/datagrid/active_model.rb
index f139ab0..bc77426 100644
--- a/lib/datagrid/active_model.rb
+++ b/lib/datagrid/active_model.rb
@@ -3,13 +3,11 @@
 module Datagrid
   # Required to be ActiveModel compatible
   module ActiveModel
-    # @!visibility private
-    def self.included(base)
-      base.extend ClassMethods
-      base.class_eval do
-        require "active_model/naming"
-        extend ::ActiveModel::Naming
-      end
+    extend ActiveSupport::Concern
+
+    included do
+      require "active_model/naming"
+      extend ::ActiveModel::Naming
     end
 
     module ClassMethods
diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index 44df516..9c8386c 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -24,16 +24,13 @@ module ClassMethods
       def datagrid_attribute(name, &block)
         return if datagrid_attributes.include?(name)
 
-        block ||= lambda do |value|
-          value
-        end
         datagrid_attributes << name
         define_method name do
           instance_variable_get("@#{name}")
         end
 
         define_method :"#{name}=" do |value|
-          instance_variable_set("@#{name}", instance_exec(value, &block))
+          instance_variable_set("@#{name}", block ? instance_exec(value, &block) : value)
         end
       end
 
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 69257a1..b573e9f 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -33,13 +33,12 @@ module Filters
     # @!visibility private
     DEFAULT_FILTER_BLOCK = Object.new
 
+    extend ActiveSupport::Concern
+
     # @!visibility private
-    def self.included(base)
-      base.extend ClassMethods
-      base.class_eval do
-        include Datagrid::Core
-        class_attribute :filters_array, default: []
-      end
+    included do
+      include Datagrid::Core
+      class_attribute :filters_array, default: []
     end
 
     # Grid class methods related to filters

From 7b6e2b062b69ce71ed2bf684a9d3f6357ea7e2f4 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 5 Nov 2024 11:05:45 +0100
Subject: [PATCH 033/157] update upgrade guide

---
 VERSION2.md | 34 +++++++++++++++++++++++++++++-----
 1 file changed, 29 insertions(+), 5 deletions(-)

diff --git a/VERSION2.md b/VERSION2.md
index fa0ac45..86f8779 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -1,9 +1,9 @@
 # Datagrid Version 2
 
 Datagrid v1 was released Sep 19 2013 - more than 10 years ago.
-A lot of new things had happened during this period and it is time.
+A lot of new things had happened during this period.
 It caused the library to be designed without support of those technologies
-and implementing some of them would cause a breaking change.
+or their implementation to be suboptimal because of backward compatibility.
 And now it is time to indroduce them with Version 2.
 
 List of things introduces:
@@ -12,8 +12,30 @@ List of things introduces:
 1. Modern modular CSS classes.
 1. HTML5 input types: number, date, datetime-local.
 1. HTML5 input [names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
+1. Rails Engines: While supported, the library was not initially designed for it.
 
-## Ruby Infinite Ranges
+## Infinite Ranges for range filters
+
+Ruby supports infinite ranges now,
+so there is no need to present infinite ranges as Hash or Array.
+But it introduces a breaking changes to range filters in Datagrid:
+
+``` ruby
+class UsersGrid
+  include Datagrid
+
+  filter(:id, :integer, range: true) do |value, scope|
+    # V1 value: [1, nil]
+    # V2 value: 1..nil
+    scope.where(id: value)
+  end
+end
+
+grid = UsersGrid.new
+grid.id = [1, nil]
+grid.id # V1: [1, nil]
+        # V2: (1..nil)
+```
 
 ## Modern CSS classes
 
@@ -35,7 +57,7 @@ and avoid collisions with other libraries:
 | field        | datagrid-dynamic-field              |
 | operation    | datagrid-dynamic-operation          |
 | value        | datagrid-dynamic-value              |
-| separator    | datgrid-range-separator             |
+| separator    | datagrid-range-separator            |
 | checkboxes   | datagrid-enum-checkboxes            |
 
 A few automatically generated classes were moved from `<input/>` to `<div class="datagrid-filter">`
@@ -108,7 +130,9 @@ The default behavior can be changed back by using `input_options`:
 
 ``` ruby
 filter(:created_at, :date, range: true, input_options: {type: 'text'})
-filter(:created_at, :datetime, range: true, input_options: {type: 'text'})
+filter(:salary, :integer, range: true, input_options: {type: 'text'})
+
+# Rendered as <textarea/> tag:
 filter(:text, :string, input_options: {type: 'textarea'})
 ```
 

From b3d4778287a9a4bb0362bee79bbf6f6be60cc56a Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 5 Nov 2024 11:49:06 +0100
Subject: [PATCH 034/157] Ability to assign range as {from: , to:} hash

---
 lib/datagrid/filters/ranged_filter.rb     | 47 +++++++++++++++--------
 spec/datagrid/filters/date_filter_spec.rb | 26 +++++++++++++
 2 files changed, 58 insertions(+), 15 deletions(-)

diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index 9fa95d2..004afa7 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -11,11 +11,43 @@ def initialize(grid, name, options, &block)
       end
 
       def parse_values(value)
+        if value.is_a?(Hash)
+          value = parse_hash(value)
+        end
         result = super
         return result if !range? || result.nil?
         # Simulate single point range
         return [result, result] unless result.is_a?(Array)
 
+        parse_array(result)
+      end
+
+      def range?
+        options[:range]
+      end
+
+      def default_filter_where(scope, value)
+        if range? && value.is_a?(Array)
+          left, right = value
+          scope = driver.greater_equal(scope, name, left) if left
+          scope = driver.less_equal(scope, name, right) if right
+          scope
+        else
+          super
+        end
+      end
+
+      protected
+
+      def parse_hash(result)
+        if result[:from] || result[:to]
+          [result[:from], result[:to]]
+        else
+          nil
+        end
+      end
+
+      def parse_array(result)
         case result.size
         when 0
           nil
@@ -34,21 +66,6 @@ def parse_values(value)
           raise ArgumentError, "Can not create a date range from array of more than two: #{result.inspect}"
         end
       end
-
-      def range?
-        options[:range]
-      end
-
-      def default_filter_where(scope, value)
-        if range? && value.is_a?(Array)
-          left, right = value
-          scope = driver.greater_equal(scope, name, left) if left
-          scope = driver.less_equal(scope, name, right) if right
-          scope
-        else
-          super
-        end
-      end
     end
   end
 end
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 865b61d..8187b8b 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -31,6 +31,32 @@
     expect(report.assets).not_to include(e2)
   end
 
+  it "supports hash argument" do
+    e1 = Entry.create!(created_at: 7.days.ago)
+    e2 = Entry.create!(created_at: 4.days.ago)
+    e3 = Entry.create!(created_at: 1.day.ago)
+    from = 5.days.ago
+    to = 3.days.ago
+    report = test_report(created_at: {from: , to:}) do
+      scope { Entry }
+      filter(:created_at, :date, range: true)
+    end
+    expect(report.created_at).to eq([from.to_date, to.to_date])
+    expect(report.assets).not_to include(e1)
+    expect(report.assets).to include(e2)
+    expect(report.assets).not_to include(e3)
+    report.created_at = {}
+    expect(report.created_at).to eq(nil)
+    report.created_at = {from: nil, to: nil}
+    expect(report.created_at).to eq(nil)
+    report.created_at = {from: Date.today, to: nil}
+    expect(report.created_at).to eq([Date.today, nil])
+    report.created_at = {from: nil, to: Date.today}
+    expect(report.created_at).to eq([nil, Date.today])
+    report.created_at = {from: Time.now, to: Time.now}
+    expect(report.created_at).to eq([Date.today, Date.today])
+  end
+
   { active_record: Entry, mongoid: MongoidEntry, sequel: SequelEntry }.each do |orm, klass|
     describe "with orm #{orm}", orm => true do
       describe "date to timestamp conversion" do

From 01e50e9ca8536fe72d21bd9e08c1a37b69d62a5e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 10:38:58 +0100
Subject: [PATCH 035/157] Use data attributes instead of CSS classes for column
 names

---
 VERSION2.md                       | 61 +++++++++++++++++++-
 app/views/datagrid/_head.html.erb |  2 +-
 app/views/datagrid/_row.html.erb  |  2 +-
 lib/datagrid/columns/column.rb    |  4 ++
 lib/datagrid/helper.rb            |  4 +-
 spec/datagrid/helper_spec.rb      | 92 +++++++++++++++----------------
 6 files changed, 114 insertions(+), 51 deletions(-)

diff --git a/VERSION2.md b/VERSION2.md
index 86f8779..176c03b 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -13,6 +13,7 @@ List of things introduces:
 1. HTML5 input types: number, date, datetime-local.
 1. HTML5 input [names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
 1. Rails Engines: While supported, the library was not initially designed for it.
+1. HTML5 data attributes
 
 ## Infinite Ranges for range filters
 
@@ -37,7 +38,7 @@ grid.id # V1: [1, nil]
         # V2: (1..nil)
 ```
 
-## Modern CSS classes
+## Modern CSS classes naming conventions
 
 Built-in generated CSS classes renamed to match modern CSS naming conventions
 and avoid collisions with other libraries:
@@ -137,3 +138,61 @@ filter(:text, :string, input_options: {type: 'textarea'})
 ```
 
 ## Names collision restriction
+
+TODO
+
+## HTML5 data attributes
+
+It is more semantic and collision proof to use `data-*` attributes
+instead of classes for meta information from backend.
+Therefor built-in partials now generate data attributes by default
+instead of classes for column names:
+
+Version 1:
+
+``` html
+<tr>
+    <th class="name">Name</th>
+    <th class="category">Category</th>
+</tr>
+<tr>
+    <td class="name">John</th>
+    <td class="category">Worker</th>
+</tr>
+<tr>
+    <td class="name">Mike</th>
+    <td class="category">Manager</th>
+</tr>
+```
+
+Version 2:
+
+``` html
+<tr>
+    <th data-datagrid-column="name">Name</th>
+    <th data-datagrid-column="category">Category</th>
+</tr>
+<tr>
+    <td data-datagrid-column="name">John</th>
+    <td data-datagrid-column="category">Worker</th>
+</tr>
+<tr>
+    <td data-datagrid-column="name">Mike</th>
+    <td data-datagrid-column="category">Manager</th>
+</tr>
+```
+
+If you still want to have an HTML class attached to a column use `class` column option:
+
+``` ruby
+column(:name, class: 'column-name')
+```
+
+``` html
+<th class="column-name" data-datagrid-column="name">Name</th>
+...
+<td class="column-name" data-datagrid-column="name">John</td>
+```
+
+If you want to change this behavior completely,
+modify [built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials).
diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index e939128..affccf4 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -1,6 +1,6 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <th class="<%= datagrid_column_classes(grid, column) %>">
+    <th class="<%= datagrid_column_classes(grid, column) %>" data-datagrid-column="<%= column.name %>">
       <%= column.header %>
       <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
     </th>
diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
index f54d21c..b431ab7 100644
--- a/app/views/datagrid/_row.html.erb
+++ b/app/views/datagrid/_row.html.erb
@@ -1,5 +1,5 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_value(grid, column, asset) %></td>
+    <td class="<%= datagrid_column_classes(grid, column) %>" data-datagrid-column="<%= column.name %>"><%= datagrid_value(grid, column, asset) %></td>
   <% end %>
 </tr>
diff --git a/lib/datagrid/columns/column.rb b/lib/datagrid/columns/column.rb
index 2f15359..502295d 100644
--- a/lib/datagrid/columns/column.rb
+++ b/lib/datagrid/columns/column.rb
@@ -108,6 +108,10 @@ def mandatory?
         !!options[:mandatory]
       end
 
+      def html_class
+        options[:class]
+      end
+
       def mandatory_explicitly_set?
         options.key?(:mandatory)
       end
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index 4f4361a..76a2551 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -151,9 +151,9 @@ def datagrid_renderer
 
     def datagrid_column_classes(grid, column)
       order_class = if grid.ordered_by?(column)
-                      [grid.descending ? "datagrid-order-active-desc" : "datagrid-order-active-asc"]
+                      grid.descending ? "datagrid-order-active-desc" : "datagrid-order-active-asc"
                     end
-      [column.name, order_class, column.options[:class]].compact.join(" ")
+      [order_class, column.html_class].compact.join(" ")
     end
   end
 end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index c8bb509..a38b8ff 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -58,12 +58,12 @@
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern({
-                                                    "table.datagrid-table tr th.group div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th.group" => /Group.*/,
-                                                    "table.datagrid-table tr th.name div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th.name" => /Name.*/,
-                                                    "table.datagrid-table tr td.group" => "Pop",
-                                                    "table.datagrid-table tr td.name" => "Star"
+                                                    "table.datagrid-table tr th[data-datagrid-column=group] div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th[data-datagrid-column=group]" => /Group.*/,
+                                                    "table.datagrid-table tr th[data-datagrid-column=name] div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th[data-datagrid-column=name]" => /Name.*/,
+                                                    "table.datagrid-table tr td[data-datagrid-column=group]" => "Pop",
+                                                    "table.datagrid-table tr td[data-datagrid-column=name]" => "Star"
                                                   })
     end
 
@@ -72,12 +72,12 @@
       datagrid_table = subject.datagrid_table(grid, [entry])
 
       expect(datagrid_table).to match_css_pattern({
-                                                    "table.datagrid-table tr th.group div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th.group" => /Group.*/,
-                                                    "table.datagrid-table tr th.name div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th.name" => /Name.*/,
-                                                    "table.datagrid-table tr td.group" => "Pop",
-                                                    "table.datagrid-table tr td.name" => "Star"
+                                                    "table.datagrid-table tr th[data-datagrid-column=group] div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th[data-datagrid-column=group]" => /Group.*/,
+                                                    "table.datagrid-table tr th[data-datagrid-column=name] div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th[data-datagrid-column=name]" => /Name.*/,
+                                                    "table.datagrid-table tr td[data-datagrid-column=group]" => "Pop",
+                                                    "table.datagrid-table tr td[data-datagrid-column=name]" => "Star"
                                                   })
     end
 
@@ -87,10 +87,10 @@
 
     it "should support columns option" do
       expect(subject.datagrid_table(grid, [entry], columns: [:name])).to match_css_pattern(
-        "table.datagrid-table th.name" => 1,
-        "table.datagrid-table td.name" => 1,
-        "table.datagrid-table th.group" => 0,
-        "table.datagrid-table td.group" => 0
+        "table.datagrid-table th[data-datagrid-column=name]" => 1,
+        "table.datagrid-table td[data-datagrid-column=name]" => 1,
+        "table.datagrid-table th[data-datagrid-column=group]" => 0,
+        "table.datagrid-table td[data-datagrid-column=group]" => 0
       )
     end
 
@@ -105,10 +105,10 @@
 
       it "should output only given column names" do
         expect(subject.datagrid_table(grid, [entry])).to match_css_pattern(
-          "table.datagrid-table th.name" => 1,
-          "table.datagrid-table td.name" => 1,
-          "table.datagrid-table th.category" => 0,
-          "table.datagrid-table td.category" => 0
+          "table.datagrid-table th[data-datagrid-column=name]" => 1,
+          "table.datagrid-table td[data-datagrid-column=name]" => 1,
+          "table.datagrid-table th[data-datagrid-column=category]" => 0,
+          "table.datagrid-table td[data-datagrid-column=category]" => 0
         )
       end
     end
@@ -156,8 +156,8 @@
       end
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
-          "table.datagrid-table th.name" => 1,
-          "table.datagrid-table td.name" => 2
+          "table.datagrid-table th[data-datagrid-column=name]" => 1,
+          "table.datagrid-table td[data-datagrid-column=name]" => 2
         )
       end
     end
@@ -172,8 +172,8 @@
       end
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
-          "table.datagrid-table th.name" => 1,
-          "table.datagrid-table td.name" => 2
+          "table.datagrid-table th[data-datagrid-column=name]" => 1,
+          "table.datagrid-table td[data-datagrid-column=name]" => 2
         )
       end
     end
@@ -186,7 +186,7 @@
         column(:name)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name.datagrid-order-active-asc" => "Star"
+        "tr td[data-datagrid-column=name].datagrid-order-active-asc" => "Star"
       )
     end
     it "should add ordering classes to column" do
@@ -209,7 +209,7 @@
         column(:name)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name.datagrid-order-active-desc" => "Star"
+        "tr td[data-datagrid-column=name].datagrid-order-active-desc" => "Star"
       )
     end
 
@@ -220,7 +220,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name" => "Star"
+        "tr td[data-datagrid-column=name]" => "Star"
       )
     end
 
@@ -232,7 +232,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name span" => "Star"
+        "tr td[data-datagrid-column=name] span" => "Star"
       )
     end
 
@@ -243,7 +243,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name" => "Star"
+        "tr td[data-datagrid-column=name]" => "Star"
       )
     end
 
@@ -256,7 +256,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name" => "Star"
+        "tr td[data-datagrid-column=name]" => "Star"
       )
     end
 
@@ -267,7 +267,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name" => "Star"
+        "tr td[data-datagrid-column=name]" => "Star"
       )
     end
 
@@ -277,7 +277,7 @@
         column(:name, html: ->(data) { content_tag :h1, data })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name h1" => "Star"
+        "tr td[data-datagrid-column=name] h1" => "Star"
       )
     end
 
@@ -289,7 +289,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name em" => "STAR"
+        "tr td[data-datagrid-column=name] em" => "STAR"
       )
     end
 
@@ -301,7 +301,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name span" => "Star-Entry"
+        "tr td[data-datagrid-column=name] span" => "Star-Entry"
       )
     end
 
@@ -313,7 +313,7 @@
         })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name h1" => "Star-star"
+        "tr td[data-datagrid-column=name] h1" => "Star-star"
       )
     end
 
@@ -325,7 +325,7 @@
         })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name h1" => "Star-star-Entry"
+        "tr td[data-datagrid-column=name] h1" => "Star-star-Entry"
       )
     end
 
@@ -339,7 +339,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name h1" => "STAR-Star"
+        "tr td[data-datagrid-column=name] h1" => "STAR-Star"
       )
     end
 
@@ -353,7 +353,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name h1" => "STAR-Star-Entry"
+        "tr td[data-datagrid-column=name] h1" => "STAR-Star-Entry"
       )
     end
 
@@ -364,10 +364,10 @@
         column(:category)
       end
       expect(subject.datagrid_rows(rp, [entry], columns: [:name])).to match_css_pattern(
-        "tr td.name" => "Star"
+        "tr td[data-datagrid-column=name]" => "Star"
       )
       expect(subject.datagrid_rows(rp, [entry], columns: [:name])).to match_css_pattern(
-        "tr td.category" => 0
+        "tr td[data-datagrid-column=category]" => 0
       )
     end
 
@@ -378,7 +378,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td.name.my_class" => "Star"
+        "tr td[data-datagrid-column=name].my_class" => "Star"
       )
     end
 
@@ -394,7 +394,7 @@
       end
       it "should ignore them" do
         expect(subject.datagrid_rows(grid, [entry])).to match_css_pattern(
-          "td.name" => 1
+          "td[data-datagrid-column=name]" => 1
         )
       end
     end
@@ -402,7 +402,7 @@
     it "should escape html" do
       entry.update!(name: "<div>hello</div>")
       expect(subject.datagrid_rows(grid, [entry], columns: [:name])).to equal_to_dom(<<-HTML)
-       <tr><td class="name">&lt;div&gt;hello&lt;/div&gt;</td></tr>
+       <tr><td class="" data-datagrid-column="name">&lt;div&gt;hello&lt;/div&gt;</td></tr>
       HTML
     end
 
@@ -412,7 +412,7 @@
         model.name.html_safe
       end
       expect(subject.datagrid_rows(grid, [entry], columns: [:safe_name])).to equal_to_dom(<<-HTML)
-       <tr><td class="safe_name"><div>hello</div></td></tr>
+       <tr><td class="" data-datagrid-column="safe_name"><div>hello</div></td></tr>
       HTML
     end
   end
@@ -576,7 +576,7 @@ def param_name
 
     it "converts to string using columns option" do
       r = subject.datagrid_row(grid, entry, columns: [:name]).to_s
-      expect(r).to match_css_pattern("tr td.name")
+      expect(r).to match_css_pattern("tr td[data-datagrid-column=name]")
       expect(r).to_not match_css_pattern("tr td.category")
     end
   end
@@ -633,7 +633,7 @@ def param_name
         end
       end
       expect(subject.datagrid_header(grid)).to equal_to_dom(<<~HTML)
-        <tr><th class="category datagrid-order-active-asc">Category<div class="datagrid-order">
+        <tr><th class="datagrid-order-active-asc" data-datagrid-column="category">Category<div class="datagrid-order">
         <a class="datagrid-order-control-asc" href="/location?grid%5Bdescending%5D=false&amp;grid%5Border%5D=category">&uarr;</a><a class="datagrid-order-control-desc" href="/location?grid%5Bdescending%5D=true&amp;grid%5Border%5D=category">&darr;</a>
         </div>
         </th></tr>

From f68ba6b2567878252997f6999369ffb9103773b8 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 10:42:39 +0100
Subject: [PATCH 036/157] Cleanup

---
 .rubocop_todo.yml | 1021 ---------------------------------------------
 1 file changed, 1021 deletions(-)
 delete mode 100644 .rubocop_todo.yml

diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
deleted file mode 100644
index 88da75f..0000000
--- a/.rubocop_todo.yml
+++ /dev/null
@@ -1,1021 +0,0 @@
-# This configuration was generated by
-# `rubocop --auto-gen-config`
-# on 2024-11-03 18:32:09 UTC using RuboCop version 1.68.0.
-# The point is for the user to remove these configuration records
-# one by one as the offenses are removed from the code base.
-# Note that changes in the inspected code, or installation of new
-# versions of RuboCop, may require this file to be generated again.
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
-# Include: **/*.gemfile, **/Gemfile, **/gems.rb
-Bundler/OrderedGems:
-  Exclude:
-    - 'Gemfile'
-    - 'gemfiles/rails_6.1.gemfile'
-    - 'gemfiles/rails_7.0.gemfile'
-    - 'gemfiles/rails_7.1.gemfile'
-    - 'gemfiles/rails_7.2.gemfile'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, IndentationWidth.
-# SupportedStyles: with_first_argument, with_fixed_indentation
-Layout/ArgumentAlignment:
-  Exclude:
-    - 'lib/datagrid/filters.rb'
-    - 'lib/datagrid/form_builder.rb'
-
-# Offense count: 5
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyleAlignWith.
-# SupportedStylesAlignWith: either, start_of_block, start_of_line
-Layout/BlockAlignment:
-  Exclude:
-    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
-    - 'spec/datagrid/filters/enum_filter_spec.rb'
-
-# Offense count: 35
-# This cop supports safe autocorrection (--autocorrect).
-Layout/BlockEndNewline:
-  Exclude:
-    - 'spec/datagrid/form_builder_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/datagrid_spec.rb'
-
-# Offense count: 4
-# This cop supports safe autocorrection (--autocorrect).
-Layout/ClosingHeredocIndentation:
-  Exclude:
-    - 'lib/datagrid/scaffold.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/datagrid/scaffold_spec.rb'
-
-# Offense count: 40
-# This cop supports safe autocorrection (--autocorrect).
-Layout/EmptyLineAfterGuardClause:
-  Exclude:
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/columns/column.rb'
-    - 'lib/datagrid/drivers/active_record.rb'
-    - 'lib/datagrid/drivers/array.rb'
-    - 'lib/datagrid/drivers/mongo_mapper.rb'
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/drivers/sequel.rb'
-    - 'lib/datagrid/filters.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'lib/datagrid/filters/dynamic_filter.rb'
-    - 'lib/datagrid/filters/enum_filter.rb'
-    - 'lib/datagrid/filters/float_filter.rb'
-    - 'lib/datagrid/filters/integer_filter.rb'
-    - 'lib/datagrid/ordering.rb'
-    - 'lib/datagrid/utils.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Layout/EmptyLineAfterMagicComment:
-  Exclude:
-    - 'spec/datagrid/form_builder_spec.rb'
-
-# Offense count: 14
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines.
-Layout/EmptyLineBetweenDefs:
-  Exclude:
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/columns/column.rb'
-    - 'lib/datagrid/core.rb'
-    - 'lib/datagrid/filters/date_filter.rb'
-    - 'lib/datagrid/helper.rb'
-    - 'lib/datagrid/ordering.rb'
-    - 'spec/datagrid/columns_spec.rb'
-    - 'spec/support/active_record.rb'
-    - 'spec/support/matchers.rb'
-    - 'spec/support/sequel.rb'
-
-# Offense count: 60
-# This cop supports safe autocorrection (--autocorrect).
-Layout/EmptyLines:
-  Enabled: false
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: around, only_before
-Layout/EmptyLinesAroundAccessModifier:
-  Exclude:
-    - 'lib/datagrid/core.rb'
-    - 'lib/datagrid/drivers/abstract_driver.rb'
-    - 'lib/datagrid/form_builder.rb'
-    - 'lib/datagrid/renderer.rb'
-    - 'lib/datagrid/scaffold.rb'
-    - 'lib/datagrid/utils.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowAliasSyntax, AllowedMethods.
-# AllowedMethods: alias_method, public, protected, private
-Layout/EmptyLinesAroundAttributeAccessor:
-  Exclude:
-    - 'spec/datagrid/columns_spec.rb'
-    - 'spec/datagrid/filters_spec.rb'
-
-# Offense count: 96
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: empty_lines, no_empty_lines
-Layout/EmptyLinesAroundBlockBody:
-  Enabled: false
-
-# Offense count: 37
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only
-Layout/EmptyLinesAroundClassBody:
-  Enabled: false
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-Layout/EmptyLinesAroundMethodBody:
-  Exclude:
-    - 'lib/datagrid/columns/column.rb'
-    - 'lib/datagrid/filters/ranged_filter.rb'
-    - 'lib/datagrid/utils.rb'
-
-# Offense count: 16
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines
-Layout/EmptyLinesAroundModuleBody:
-  Exclude:
-    - 'lib/datagrid.rb'
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/configuration.rb'
-    - 'lib/datagrid/core.rb'
-    - 'lib/datagrid/filters.rb'
-    - 'lib/datagrid/filters/composite_filters.rb'
-    - 'lib/datagrid/filters/ranged_filter.rb'
-    - 'lib/datagrid/helper.rb'
-    - 'lib/datagrid/ordering.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyleAlignWith, Severity.
-# SupportedStylesAlignWith: keyword, variable, start_of_line
-Layout/EndAlignment:
-  Exclude:
-    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
-
-# Offense count: 16
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
-Layout/ExtraSpacing:
-  Exclude:
-    - 'lib/datagrid/active_model.rb'
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/drivers/active_record.rb'
-    - 'lib/datagrid/drivers/sequel.rb'
-    - 'lib/datagrid/filters/composite_filters.rb'
-    - 'lib/datagrid/ordering.rb'
-    - 'spec/datagrid/columns_spec.rb'
-    - 'spec/datagrid/filters/dynamic_filter_spec.rb'
-    - 'spec/datagrid/filters/enum_filter_spec.rb'
-    - 'spec/datagrid/ordering_spec.rb'
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: IndentationWidth.
-# SupportedStyles: special_inside_parentheses, consistent, align_brackets
-Layout/FirstArrayElementIndentation:
-  EnforcedStyle: consistent
-
-# Offense count: 16
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: IndentationWidth.
-# SupportedStyles: special_inside_parentheses, consistent, align_braces
-Layout/FirstHashElementIndentation:
-  EnforcedStyle: consistent
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
-# SupportedHashRocketStyles: key, separator, table
-# SupportedColonStyles: key, separator, table
-# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
-Layout/HashAlignment:
-  Exclude:
-    - 'spec/spec_helper.rb'
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-Layout/HeredocIndentation:
-  Exclude:
-    - 'lib/datagrid/scaffold.rb'
-    - 'spec/datagrid/form_builder_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-
-# Offense count: 4
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: normal, indented_internal_methods
-Layout/IndentationConsistency:
-  Exclude:
-    - 'spec/datagrid/columns_spec.rb'
-
-# Offense count: 5
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Width, AllowedPatterns.
-Layout/IndentationWidth:
-  Exclude:
-    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-
-# Offense count: 11
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment, AllowRBSInlineAnnotation, AllowSteepAnnotation.
-Layout/LeadingCommentSpace:
-  Exclude:
-    - 'lib/datagrid/drivers/mongo_mapper.rb'
-    - 'lib/datagrid/rspec.rb'
-    - 'spec/datagrid/filters/dynamic_filter_spec.rb'
-    - 'spec/spec_helper.rb'
-    - 'spec/support/active_record.rb'
-    - 'spec/support/mongoid.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Layout/LeadingEmptyLines:
-  Exclude:
-    - 'spec/support/simple_report.rb'
-
-# Offense count: 35
-# This cop supports safe autocorrection (--autocorrect).
-Layout/MultilineBlockLayout:
-  Exclude:
-    - 'spec/datagrid/form_builder_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/datagrid_spec.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, IndentationWidth.
-# SupportedStyles: aligned, indented
-Layout/MultilineOperationIndentation:
-  Exclude:
-    - 'lib/datagrid/filters/integer_filter.rb'
-
-# Offense count: 36
-# This cop supports safe autocorrection (--autocorrect).
-Layout/SpaceAfterComma:
-  Exclude:
-    - 'lib/datagrid/columns.rb'
-    - 'spec/datagrid/drivers/array_spec.rb'
-    - 'spec/datagrid/drivers/sequel_spec.rb'
-    - 'spec/datagrid/filters/date_filter_spec.rb'
-    - 'spec/datagrid/filters/date_time_filter_spec.rb'
-    - 'spec/datagrid/filters/enum_filter_spec.rb'
-    - 'spec/datagrid/filters/integer_filter_spec.rb'
-    - 'spec/datagrid/filters_spec.rb'
-    - 'spec/datagrid/form_builder_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-
-# Offense count: 5
-# This cop supports safe autocorrection (--autocorrect).
-Layout/SpaceAfterNot:
-  Exclude:
-    - 'lib/datagrid/columns/column.rb'
-    - 'lib/datagrid/drivers/active_record.rb'
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/drivers/sequel.rb'
-
-# Offense count: 14
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.
-# SupportedStylesForExponentOperator: space, no_space
-# SupportedStylesForRationalLiterals: space, no_space
-Layout/SpaceAroundOperators:
-  Exclude:
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/drivers/active_record.rb'
-    - 'lib/datagrid/drivers/mongo_mapper.rb'
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/drivers/sequel.rb'
-    - 'spec/datagrid/drivers/array_spec.rb'
-    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
-    - 'spec/datagrid/drivers/mongoid_spec.rb'
-    - 'spec/datagrid/drivers/sequel_spec.rb'
-    - 'spec/datagrid/filters/date_time_filter_spec.rb'
-    - 'spec/datagrid/filters/enum_filter_spec.rb'
-    - 'spec/datagrid/filters/integer_filter_spec.rb'
-    - 'spec/spec_helper.rb'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
-# SupportedStyles: space, no_space
-# SupportedStylesForEmptyBraces: space, no_space
-Layout/SpaceBeforeBlockBraces:
-  Exclude:
-    - 'lib/datagrid/filters.rb'
-    - 'spec/datagrid/filters/dynamic_filter_spec.rb'
-    - 'spec/datagrid/ordering_spec.rb'
-
-# Offense count: 5
-# This cop supports safe autocorrection (--autocorrect).
-Layout/SpaceBeforeComma:
-  Exclude:
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/filters.rb'
-    - 'lib/datagrid/form_builder.rb'
-
-# Offense count: 5
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowForAlignment.
-Layout/SpaceBeforeFirstArg:
-  Exclude:
-    - 'lib/datagrid/active_model.rb'
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/filters/composite_filters.rb'
-    - 'lib/datagrid/ordering.rb'
-    - 'spec/datagrid/columns_spec.rb'
-
-# Offense count: 16
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets.
-# SupportedStyles: space, no_space, compact
-# SupportedStylesForEmptyBrackets: space, no_space
-Layout/SpaceInsideArrayLiteralBrackets:
-  Exclude:
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'spec/datagrid/column_names_attribute_spec.rb'
-    - 'spec/datagrid/drivers/array_spec.rb'
-    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
-    - 'spec/datagrid/drivers/mongoid_spec.rb'
-    - 'spec/datagrid/drivers/sequel_spec.rb'
-
-# Offense count: 425
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
-# SupportedStyles: space, no_space
-# SupportedStylesForEmptyBraces: space, no_space
-Layout/SpaceInsideBlockBraces:
-  Enabled: false
-
-# Offense count: 162
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
-# SupportedStyles: space, no_space, compact
-# SupportedStylesForEmptyBraces: space, no_space
-Layout/SpaceInsideHashLiteralBraces:
-  Enabled: false
-
-# Offense count: 18
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: space, compact, no_space
-Layout/SpaceInsideParens:
-  Exclude:
-    - 'lib/datagrid/form_builder.rb'
-    - 'lib/datagrid/ordering.rb'
-    - 'spec/datagrid/filters/enum_filter_spec.rb'
-    - 'spec/datagrid/filters/integer_filter_spec.rb'
-    - 'spec/datagrid/filters/string_filter_spec.rb'
-    - 'spec/datagrid/filters_spec.rb'
-    - 'spec/datagrid/form_builder_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-
-# Offense count: 15
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: final_newline, final_blank_line
-Layout/TrailingEmptyLines:
-  Exclude:
-    - 'Rakefile'
-    - 'datagrid.gemspec'
-    - 'lib/datagrid.rb'
-    - 'lib/datagrid/column_names_attribute.rb'
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'lib/datagrid/filters/date_filter.rb'
-    - 'lib/datagrid/filters/date_time_filter.rb'
-    - 'lib/datagrid/filters/integer_filter.rb'
-    - 'lib/datagrid/helper.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/support/configuration.rb'
-    - 'spec/support/mongoid.rb'
-    - 'spec/support/sequel.rb'
-    - 'spec/support/simple_report.rb'
-
-# Offense count: 4
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowInHeredoc.
-Layout/TrailingWhitespace:
-  Exclude:
-    - 'spec/datagrid/columns/column_spec.rb'
-    - 'spec/datagrid/filters/base_filter_spec.rb'
-    - 'spec/datagrid/filters/float_filter_spec.rb'
-    - 'spec/datagrid/utils_spec.rb'
-
-# Offense count: 4
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: AllowSafeAssignment.
-Lint/AssignmentInCondition:
-  Exclude:
-    - 'lib/datagrid/columns/column.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'lib/datagrid/form_builder.rb'
-    - 'lib/datagrid/renderer.rb'
-
-# Offense count: 31
-# Configuration parameters: AllowedMethods.
-# AllowedMethods: enums
-Lint/ConstantDefinitionInBlock:
-  Exclude:
-    - 'spec/datagrid/active_model_spec.rb'
-    - 'spec/datagrid/columns/column_spec.rb'
-    - 'spec/datagrid/columns_spec.rb'
-    - 'spec/datagrid/core_spec.rb'
-    - 'spec/datagrid/drivers/array_spec.rb'
-    - 'spec/datagrid/drivers/sequel_spec.rb'
-    - 'spec/datagrid/filters/enum_filter_spec.rb'
-    - 'spec/datagrid/filters_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/datagrid/ordering_spec.rb'
-    - 'spec/support/active_record.rb'
-
-# Offense count: 1
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Lint/NonDeterministicRequireOrder:
-  Exclude:
-    - 'spec/spec_helper.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Lint/RedundantStringCoercion:
-  Exclude:
-    - 'lib/datagrid/form_builder.rb'
-
-# Offense count: 6
-# Configuration parameters: AllowComments, AllowNil.
-Lint/SuppressedException:
-  Exclude:
-    - 'lib/datagrid/active_model.rb'
-    - 'lib/datagrid/utils.rb'
-    - 'spec/spec_helper.rb'
-
-# Offense count: 8
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AutoCorrect, IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
-Lint/UnusedBlockArgument:
-  Exclude:
-    - 'lib/datagrid/filters/extended_boolean_filter.rb'
-    - 'spec/datagrid/columns_spec.rb'
-    - 'spec/datagrid/drivers/active_record_spec.rb'
-    - 'spec/datagrid/form_builder_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/datagrid/ordering_spec.rb'
-
-# Offense count: 20
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods.
-Lint/UnusedMethodArgument:
-  Exclude:
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/drivers/array.rb'
-    - 'lib/datagrid/drivers/mongo_mapper.rb'
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'lib/datagrid/filters/composite_filters.rb'
-    - 'lib/datagrid/form_builder.rb'
-    - 'spec/support/i18n_helpers.rb'
-
-# Offense count: 3
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: AutoCorrect.
-Lint/UselessMethodDefinition:
-  Exclude:
-    - 'lib/datagrid/core.rb'
-    - 'lib/datagrid/drivers/sequel.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AutoCorrect, CheckForMethodsWithNoSideEffects.
-Lint/Void:
-  Exclude:
-    - 'spec/support/matchers.rb'
-
-# Offense count: 14
-# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
-Metrics/AbcSize:
-  Max: 39
-
-# Offense count: 64
-# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
-# AllowedMethods: refine
-Metrics/BlockLength:
-  Max: 606
-
-# Offense count: 5
-# Configuration parameters: CountComments, CountAsOne.
-Metrics/ClassLength:
-  Max: 146
-
-# Offense count: 10
-# Configuration parameters: AllowedMethods, AllowedPatterns.
-Metrics/CyclomaticComplexity:
-  Max: 14
-
-# Offense count: 32
-# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
-Metrics/MethodLength:
-  Max: 34
-
-# Offense count: 3
-# Configuration parameters: CountComments, CountAsOne.
-Metrics/ModuleLength:
-  Max: 193
-
-# Offense count: 10
-# Configuration parameters: AllowedMethods, AllowedPatterns.
-Metrics/PerceivedComplexity:
-  Max: 14
-
-# Offense count: 9
-# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros.
-# NamePrefix: is_, has_, have_
-# ForbiddenPrefixes: is_, has_, have_
-# AllowedMethods: is_a?
-# MethodDefinitionMacros: define_method, define_singleton_method
-Naming/PredicateName:
-  Exclude:
-    - 'spec/**/*'
-    - 'lib/datagrid/drivers/abstract_driver.rb'
-    - 'lib/datagrid/drivers/active_record.rb'
-    - 'lib/datagrid/drivers/array.rb'
-    - 'lib/datagrid/drivers/mongo_mapper.rb'
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/drivers/sequel.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: prefer_alias, prefer_alias_method
-Style/Alias:
-  Exclude:
-    - 'lib/datagrid/ordering.rb'
-
-# Offense count: 1
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: always, conditionals
-Style/AndOr:
-  Exclude:
-    - 'spec/support/matchers.rb'
-
-# Offense count: 28
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
-# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
-# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
-# FunctionalMethods: let, let!, subject, watch
-# AllowedMethods: lambda, proc, it
-Style/BlockDelimiters:
-  Exclude:
-    - 'spec/datagrid/columns_spec.rb'
-    - 'spec/datagrid/core_spec.rb'
-    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
-    - 'spec/datagrid/drivers/mongoid_spec.rb'
-    - 'spec/datagrid/drivers/sequel_spec.rb'
-    - 'spec/datagrid/filters/dynamic_filter_spec.rb'
-    - 'spec/datagrid/filters_spec.rb'
-    - 'spec/datagrid/form_builder_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/datagrid/ordering_spec.rb'
-    - 'spec/datagrid_spec.rb'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowOnConstant, AllowOnSelfClass.
-Style/CaseEquality:
-  Exclude:
-    - 'lib/datagrid/filters/select_options.rb'
-
-# Offense count: 2
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: MinBranchesCount.
-Style/CaseLikeIf:
-  Exclude:
-    - 'lib/datagrid/form_builder.rb'
-    - 'spec/support/matchers.rb'
-
-# Offense count: 16
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: nested, compact
-Style/ClassAndModuleChildren:
-  Exclude:
-    - 'lib/datagrid/columns/column.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'lib/datagrid/filters/boolean_filter.rb'
-    - 'lib/datagrid/filters/date_filter.rb'
-    - 'lib/datagrid/filters/date_time_filter.rb'
-    - 'lib/datagrid/filters/default_filter.rb'
-    - 'lib/datagrid/filters/dynamic_filter.rb'
-    - 'lib/datagrid/filters/enum_filter.rb'
-    - 'lib/datagrid/filters/extended_boolean_filter.rb'
-    - 'lib/datagrid/filters/float_filter.rb'
-    - 'lib/datagrid/filters/integer_filter.rb'
-    - 'lib/datagrid/filters/ranged_filter.rb'
-    - 'lib/datagrid/filters/select_options.rb'
-    - 'lib/datagrid/filters/string_filter.rb'
-    - 'lib/datagrid/scaffold.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Keywords, RequireColon.
-# Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE
-Style/CommentAnnotation:
-  Exclude:
-    - 'lib/datagrid/drivers/mongo_mapper.rb'
-    - 'spec/spec_helper.rb'
-
-# Offense count: 25
-# Configuration parameters: AllowedConstants.
-Style/Documentation:
-  Enabled: false
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AutoCorrect, EnforcedStyle, AllowComments.
-# SupportedStyles: empty, nil, both
-Style/EmptyElse:
-  Exclude:
-    - 'lib/datagrid/ordering.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Style/EmptyLiteral:
-  Exclude:
-    - 'spec/datagrid/drivers/array_spec.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-Style/Encoding:
-  Exclude:
-    - 'Rakefile'
-    - 'spec/datagrid/form_builder_spec.rb'
-
-# Offense count: 3
-# This cop supports safe autocorrection (--autocorrect).
-Style/ExpandPathArguments:
-  Exclude:
-    - 'lib/datagrid.rb'
-    - 'spec/spec_helper.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-Style/ExplicitBlockArgument:
-  Exclude:
-    - 'lib/datagrid/drivers/mongo_mapper.rb'
-    - 'lib/datagrid/drivers/mongoid.rb'
-
-# Offense count: 82
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: always, always_true, never
-Style/FrozenStringLiteralComment:
-  Enabled: false
-
-# Offense count: 3
-# Configuration parameters: AllowedVariables.
-Style/GlobalVars:
-  Exclude:
-    - 'spec/datagrid/filters_spec.rb'
-
-# Offense count: 9
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
-Style/GuardClause:
-  Exclude:
-    - 'lib/datagrid/core.rb'
-    - 'lib/datagrid/drivers/abstract_driver.rb'
-    - 'lib/datagrid/filters/dynamic_filter.rb'
-    - 'lib/datagrid/filters/ranged_filter.rb'
-    - 'lib/datagrid/filters/select_options.rb'
-    - 'lib/datagrid/form_builder.rb'
-    - 'lib/datagrid/ordering.rb'
-    - 'spec/support/matchers.rb'
-
-# Offense count: 1
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: AllowedReceivers.
-# AllowedReceivers: Thread.current
-Style/HashEachMethods:
-  Exclude:
-    - 'spec/datagrid/form_builder_spec.rb'
-
-# Offense count: 226
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
-# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
-# SupportedShorthandSyntax: always, never, either, consistent, either_consistent
-Style/HashSyntax:
-  Enabled: false
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowIfModifier.
-Style/IfInsideElse:
-  Exclude:
-    - 'lib/datagrid/column_names_attribute.rb'
-    - 'lib/datagrid/ordering.rb'
-
-# Offense count: 45
-# This cop supports safe autocorrection (--autocorrect).
-Style/IfUnlessModifier:
-  Enabled: false
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: line_count_dependent, lambda, literal
-Style/Lambda:
-  Exclude:
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/support/simple_report.rb'
-
-# Offense count: 20
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Style/LineEndConcatenation:
-  Exclude:
-    - 'spec/datagrid/form_builder_spec.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowedMethods, AllowedPatterns.
-Style/MethodCallWithoutArgsParentheses:
-  Exclude:
-    - 'spec/datagrid/form_builder_spec.rb'
-
-# Offense count: 2
-Style/MissingRespondToMissing:
-  Exclude:
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/renderer.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-Style/MultilineTernaryOperator:
-  Exclude:
-    - 'lib/datagrid/drivers/active_record.rb'
-    - 'lib/datagrid/filters/select_options.rb'
-
-# Offense count: 14
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: literals, strict
-Style/MutableConstant:
-  Exclude:
-    - 'lib/datagrid/drivers/abstract_driver.rb'
-    - 'lib/datagrid/filters.rb'
-    - 'lib/datagrid/filters/dynamic_filter.rb'
-    - 'lib/datagrid/filters/extended_boolean_filter.rb'
-    - 'lib/datagrid/utils.rb'
-    - 'lib/datagrid/version.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: both, prefix, postfix
-Style/NegatedIf:
-  Exclude:
-    - 'spec/support/matchers.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Style/NestedTernaryOperator:
-  Exclude:
-    - 'lib/datagrid/helper.rb'
-
-# Offense count: 20
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedOctalStyle.
-# SupportedOctalStyles: zero_with_o, zero_only
-Style/NumericLiteralPrefix:
-  Exclude:
-    - 'spec/datagrid/filters/date_filter_spec.rb'
-    - 'spec/datagrid/filters/date_time_filter_spec.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: Strict, AllowedNumbers, AllowedPatterns.
-Style/NumericLiterals:
-  MinDigits: 6
-
-# Offense count: 4
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
-# SupportedStyles: predicate, comparison
-Style/NumericPredicate:
-  Exclude:
-    - 'spec/**/*'
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/utils.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Style/ParallelAssignment:
-  Exclude:
-    - 'lib/datagrid/utils.rb'
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: PreferredDelimiters.
-Style/PercentLiteralDelimiters:
-  Exclude:
-    - 'lib/datagrid/filters/dynamic_filter.rb'
-    - 'spec/datagrid/columns_spec.rb'
-    - 'spec/datagrid/form_builder_spec.rb'
-
-# Offense count: 7
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: EnforcedStyle.
-# SupportedStyles: short, verbose
-Style/PreferredHashMethods:
-  Exclude:
-    - 'lib/datagrid/columns/column.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'lib/datagrid/filters/dynamic_filter.rb'
-    - 'lib/datagrid/filters/select_options.rb'
-    - 'lib/datagrid/form_builder.rb'
-    - 'lib/datagrid/renderer.rb'
-
-# Offense count: 4
-# This cop supports safe autocorrection (--autocorrect).
-Style/RedundantBegin:
-  Exclude:
-    - 'lib/datagrid/utils.rb'
-    - 'spec/support/configuration.rb'
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowMultipleReturnValues.
-Style/RedundantReturn:
-  Exclude:
-    - 'lib/datagrid/drivers/mongoid.rb'
-
-# Offense count: 38
-# This cop supports safe autocorrection (--autocorrect).
-Style/RedundantSelf:
-  Exclude:
-    - 'lib/datagrid/active_model.rb'
-    - 'lib/datagrid/columns.rb'
-    - 'lib/datagrid/columns/column.rb'
-    - 'lib/datagrid/core.rb'
-    - 'lib/datagrid/drivers/abstract_driver.rb'
-    - 'lib/datagrid/filters.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'lib/datagrid/filters/enum_filter.rb'
-    - 'lib/datagrid/filters/select_options.rb'
-    - 'lib/datagrid/form_builder.rb'
-    - 'lib/datagrid/ordering.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/support/simple_report.rb'
-
-# Offense count: 2
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
-# SupportedStyles: slashes, percent_r, mixed
-Style/RegexpLiteral:
-  Exclude:
-    - 'lib/datagrid/scaffold.rb'
-
-# Offense count: 4
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
-# AllowedMethods: present?, blank?, presence, try, try!
-Style/SafeNavigation:
-  Exclude:
-    - 'lib/datagrid/column_names_attribute.rb'
-    - 'lib/datagrid/filters/base_filter.rb'
-    - 'lib/datagrid/filters/string_filter.rb'
-    - 'lib/datagrid/renderer.rb'
-
-# Offense count: 4
-# This cop supports safe autocorrection (--autocorrect).
-Style/StderrPuts:
-  Exclude:
-    - 'Rakefile'
-    - 'spec/spec_helper.rb'
-
-# Offense count: 17
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: Mode.
-Style/StringConcatenation:
-  Exclude:
-    - 'lib/datagrid/scaffold.rb'
-    - 'lib/tasks/datagrid_tasks.rake'
-    - 'spec/datagrid/form_builder_spec.rb'
-
-# Offense count: 1090
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
-# SupportedStyles: single_quotes, double_quotes
-Style/StringLiterals:
-  Enabled: false
-
-# Offense count: 1
-# This cop supports unsafe autocorrection (--autocorrect-all).
-Style/StructInheritance:
-  Exclude:
-    - 'spec/datagrid/drivers/array_spec.rb'
-
-# Offense count: 20
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: MinSize.
-# SupportedStyles: percent, brackets
-Style/SymbolArray:
-  EnforcedStyle: brackets
-
-# Offense count: 1
-# This cop supports safe autocorrection (--autocorrect).
-Style/SymbolLiteral:
-  Exclude:
-    - 'spec/datagrid/columns_spec.rb'
-
-# Offense count: 6
-# This cop supports unsafe autocorrection (--autocorrect-all).
-# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
-# AllowedMethods: define_method
-Style/SymbolProc:
-  Exclude:
-    - 'lib/datagrid/column_names_attribute.rb'
-    - 'spec/datagrid/filters/enum_filter_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/support/simple_report.rb'
-
-# Offense count: 9
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyleForMultiline.
-# SupportedStylesForMultiline: comma, consistent_comma, no_comma
-Style/TrailingCommaInArguments:
-  Exclude:
-    - 'lib/datagrid/column_names_attribute.rb'
-    - 'lib/datagrid/form_builder.rb'
-    - 'spec/datagrid/drivers/active_record_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyleForMultiline.
-# SupportedStylesForMultiline: comma, consistent_comma, no_comma
-Style/TrailingCommaInArrayLiteral:
-  Exclude:
-    - 'datagrid.gemspec'
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/filters/dynamic_filter.rb'
-    - 'spec/datagrid/drivers/array_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/spec_helper.rb'
-
-# Offense count: 6
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyleForMultiline.
-# SupportedStylesForMultiline: comma, consistent_comma, no_comma
-Style/TrailingCommaInHashLiteral:
-  Exclude:
-    - 'datagrid.gemspec'
-    - 'lib/datagrid/drivers/mongoid.rb'
-    - 'lib/datagrid/form_builder.rb'
-    - 'lib/datagrid/scaffold.rb'
-    - 'spec/datagrid/filters_spec.rb'
-
-# Offense count: 31
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: EnforcedStyle, MinSize, WordRegex.
-# SupportedStyles: percent, brackets
-Style/WordArray:
-  Exclude:
-    - 'spec/datagrid/column_names_attribute_spec.rb'
-    - 'spec/datagrid/columns_spec.rb'
-    - 'spec/datagrid/core_spec.rb'
-    - 'spec/datagrid/drivers/array_spec.rb'
-    - 'spec/datagrid/drivers/mongo_mapper_spec.rb'
-    - 'spec/datagrid/drivers/mongoid_spec.rb'
-    - 'spec/datagrid/drivers/sequel_spec.rb'
-    - 'spec/datagrid/filters/date_filter_spec.rb'
-    - 'spec/datagrid/filters/extended_boolean_filter_spec.rb'
-    - 'spec/datagrid/filters/string_filter_spec.rb'
-    - 'spec/datagrid/filters_spec.rb'
-    - 'spec/datagrid/form_builder_spec.rb'
-    - 'spec/datagrid/helper_spec.rb'
-    - 'spec/support/simple_report.rb'
-
-# Offense count: 36
-# This cop supports safe autocorrection (--autocorrect).
-# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
-# URISchemes: http, https
-Layout/LineLength:
-  Max: 250

From bcfdd94ee37d60c1b37a4aa08c3ae907baaf126a Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 10:50:47 +0100
Subject: [PATCH 037/157] V2 guide

---
 VERSION2.md | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 127 insertions(+), 1 deletion(-)

diff --git a/VERSION2.md b/VERSION2.md
index 176c03b..d076475 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -195,4 +195,130 @@ column(:name, class: 'column-name')
 ```
 
 If you want to change this behavior completely,
-modify [built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials).
+modify [built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
+
+## All changes in built-in partials
+
+Version 2 built-in partials are trying to expose
+as much UI as possible for user modification.
+
+Here is a complete diff for built-in partials between V1 and V2:
+
+TODO update
+
+``` diff
+diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
+index 9f48319..f114c17 100644
+--- a/app/views/datagrid/_enum_checkboxes.html.erb
++++ b/app/views/datagrid/_enum_checkboxes.html.erb
+@@ -4,8 +4,8 @@ You can add indent if whitespace doesn't matter for you
+ %>
+ <%- elements.each do |value, text, checked| -%>
+ <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
+-<%= form.label filter.name, options.merge(for: id) do -%>
+-<%= form.check_box(filter.name, {multiple: true, id: id, checked: checked, include_hidden: false}, value.to_s, nil) -%>
++<%= form.datagrid_label(filter.name, **options, for: id) do -%>
++<%= form.datagrid_filter_input(filter.name, type: :checkbox, multiple: true, id: id, checked: checked, include_hidden: false, value: value.to_s) -%>
+ <%= text -%>
+ <%- end -%>
+ <%- end -%>
+diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
+index 7e175c1..84cf58e 100644
+--- a/app/views/datagrid/_form.html.erb
++++ b/app/views/datagrid/_form.html.erb
+@@ -1,12 +1,12 @@
+-<%= form_for grid, options do |f| -%>
++<%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
+   <% grid.filters.each do |filter| %>
+-    <div class="datagrid-filter filter">
++    <div class="datagrid-filter <%= filter.default_html_classes.join(' ') %>">
+       <%= f.datagrid_label filter %>
+       <%= f.datagrid_filter filter %>
+     </div>
+   <% end %>
+   <div class="datagrid-actions">
+-    <%= f.submit I18n.t("datagrid.form.search").html_safe, class: "datagrid-submit" %>
+-    <%= link_to I18n.t('datagrid.form.reset').html_safe, url_for(grid.to_param => {}), class: "datagrid-reset" %>
++    <%= f.submit I18n.t("datagrid.form.search"), class: "datagrid-submit" %>
++    <%= link_to I18n.t('datagrid.form.reset'), url_for(grid.to_param => {}), class: "datagrid-reset" %>
+   </div>
+ <% end -%>
+diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
+index e939128..affccf4 100644
+--- a/app/views/datagrid/_head.html.erb
++++ b/app/views/datagrid/_head.html.erb
+@@ -1,6 +1,6 @@
+ <tr>
+   <% grid.html_columns(*options[:columns]).each do |column| %>
+-    <th class="<%= datagrid_column_classes(grid, column) %>">
++    <th class="<%= datagrid_column_classes(grid, column) %>" data-datagrid-column="<%= column.name %>">
+       <%= column.header %>
+       <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
+     </th>
+diff --git a/app/views/datagrid/_order_for.html.erb b/app/views/datagrid/_order_for.html.erb
+index 1545a8e..1c33c37 100644
+--- a/app/views/datagrid/_order_for.html.erb
++++ b/app/views/datagrid/_order_for.html.erb
+@@ -1,10 +1,10 @@
+-<div class="order">
++<div class="datagrid-order">
+   <%= link_to(
+-      I18n.t("datagrid.table.order.asc").html_safe,
++      I18n.t("datagrid.table.order.asc"),
+       datagrid_order_path(grid, column, false),
+-      class: "asc") %>
++      class: "datagrid-order-control-asc") %>
+   <%= link_to(
+-      I18n.t("datagrid.table.order.desc").html_safe,
++      I18n.t("datagrid.table.order.desc"),
+       datagrid_order_path(grid, column, true),
+-      class: "desc") %>
++      class: "datagrid-order-control-desc") %>
+ </div>
+diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
+index 7a8a123..1b90dc8 100644
+--- a/app/views/datagrid/_range_filter.html.erb
++++ b/app/views/datagrid/_range_filter.html.erb
+@@ -1,3 +1,3 @@
+ <%= form.datagrid_filter_input(filter, **from_options) %>
+-<span class="separator <%= filter.type %>"><%= I18n.t('datagrid.filters.range.separator') %></span>
++<span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
+ <%= form.datagrid_filter_input(filter, **to_options) %>
+diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
+index f54d21c..b431ab7 100644
+--- a/app/views/datagrid/_row.html.erb
++++ b/app/views/datagrid/_row.html.erb
+@@ -1,5 +1,5 @@
+ <tr>
+   <% grid.html_columns(*options[:columns]).each do |column| %>
+-    <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_value(grid, column, asset) %></td>
++    <td class="<%= datagrid_column_classes(grid, column) %>" data-datagrid-column="<%= column.name %>"><%= datagrid_value(grid, column, asset) %></td>
+   <% end %>
+ </tr>
+diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
+index 8708c05..0b5ff24 100644
+--- a/app/views/datagrid/_table.html.erb
++++ b/app/views/datagrid/_table.html.erb
+@@ -5,7 +5,7 @@ Local variables:
+ * options - passed options Hash
+ %>
+ <% if grid.html_columns(*options[:columns]).any? %>
+-  <%= content_tag :table, options[:html] do %>
++  <%= content_tag :table, class: 'datagrid-table', **options.fetch(:html, {}) do %>
+     <thead>
+       <%= datagrid_header(grid, options) %>
+     </thead>
+@@ -13,10 +13,10 @@ Local variables:
+       <% if assets.any? %>
+         <%= datagrid_rows(grid, assets, **options) %>
+       <% else %>
+-        <tr><td class="noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
++        <tr><td class="datagrid-no-results" colspan="100%"><%= I18n.t('datagrid.no_results') %></td></tr>
+       <% end %>
+     </tbody>
+   <% end %>
+ <% else -%>
+-  <%= I18n.t("datagrid.table.no_columns").html_safe %>
++  <%= I18n.t("datagrid.table.no_columns") %>
+ <% end %>
+```

From 47f491d02247f2e2011e77b31012f0c201cb72fc Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 14:42:50 +0100
Subject: [PATCH 038/157] Introduce Datagrid::Base

---
 Readme.markdown                           |  4 +--
 VERSION2.md                               | 16 ++++++++--
 lib/datagrid.rb                           | 38 ++--------------------
 lib/datagrid/base.rb                      | 39 +++++++++++++++++++++++
 lib/datagrid/core.rb                      | 20 ++++++------
 lib/datagrid/scaffold.rb                  |  2 +-
 spec/datagrid/columns/column_spec.rb      |  3 +-
 spec/datagrid/columns_spec.rb             | 15 +++------
 spec/datagrid/core_spec.rb                |  9 ++----
 spec/datagrid/drivers/array_spec.rb       |  6 ++--
 spec/datagrid/drivers/sequel_spec.rb      |  3 +-
 spec/datagrid/filters/enum_filter_spec.rb |  3 +-
 spec/datagrid/filters_spec.rb             | 15 +++------
 spec/datagrid/helper_spec.rb              | 15 +++------
 spec/datagrid/ordering_spec.rb            |  3 +-
 spec/datagrid_spec.rb                     | 12 +++++++
 spec/support/mongoid.rb                   |  3 +-
 spec/support/sequel.rb                    |  4 +--
 spec/support/simple_report.rb             |  7 ++--
 templates/base.rb.erb                     |  6 +---
 20 files changed, 107 insertions(+), 116 deletions(-)
 create mode 100644 lib/datagrid/base.rb

diff --git a/Readme.markdown b/Readme.markdown
index 5d565a4..98a9f80 100644
--- a/Readme.markdown
+++ b/Readme.markdown
@@ -47,9 +47,7 @@ including admin panels, analytics and data representation:
 In order to create a grid:
 
 ``` ruby
-class UsersGrid
-
-  include Datagrid
+class UsersGrid < Datagrid::Base
 
   scope do
     User.includes(:group)
diff --git a/VERSION2.md b/VERSION2.md
index d076475..019f575 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -14,6 +14,7 @@ List of things introduces:
 1. HTML5 input [names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
 1. Rails Engines: While supported, the library was not initially designed for it.
 1. HTML5 data attributes
+1. Inherit `Datagrid::Base` instead of `include Datagrid`
 
 ## Infinite Ranges for range filters
 
@@ -22,9 +23,7 @@ so there is no need to present infinite ranges as Hash or Array.
 But it introduces a breaking changes to range filters in Datagrid:
 
 ``` ruby
-class UsersGrid
-  include Datagrid
-
+class UsersGrid < Datagrid::Base
   filter(:id, :integer, range: true) do |value, scope|
     # V1 value: [1, nil]
     # V2 value: 1..nil
@@ -322,3 +321,14 @@ index 8708c05..0b5ff24 100644
 +  <%= I18n.t("datagrid.table.no_columns") %>
  <% end %>
 ```
+
+## Inherit Datagrid::Base
+
+`include Datagrid` causes method name space to be clamsy.
+Version 2 introduces a difference between the class
+that needs to be inherited and high level namespace (just like most gems do):
+
+``` ruby
+class ApplicationGrid < Datagrid::Base
+end
+```
diff --git a/lib/datagrid.rb b/lib/datagrid.rb
index 6955a3a..80ec058 100644
--- a/lib/datagrid.rb
+++ b/lib/datagrid.rb
@@ -4,45 +4,12 @@
 require "datagrid/configuration"
 require "datagrid/engine"
 
-# Main datagrid module that needs to be included in grid class
-#
-# @example
-#   class UsersGrid
-#     include Datagrid
-#     scope { User }
-#     column(:id)
-#     column(:name)
-#   end
+# @main README.md
 module Datagrid
-  extend ActiveSupport::Autoload
 
-  autoload :Core
-  autoload :ActiveModel
-  autoload :Filters
-  autoload :Columns
-  autoload :ColumnNamesAttribute
-  autoload :Ordering
-  autoload :Configuration
-
-  autoload :Helper
-  autoload :FormBuilder
-
-  autoload :Renderer
-
-  autoload :Engine
-
-  # extend ActiveSupport::Concern
-
-  # included do
-    # include ::Datagrid::Core
-    # include ::Datagrid::ActiveModel
-    # include ::Datagrid::Filters
-    # include ::Datagrid::Columns
-    # include ::Datagrid::ColumnNamesAttribute
-    # include ::Datagrid::Ordering
-  # end
   # @!visibility private
   def self.included(base)
+    Utils.warn_once("Including Datagrid is deprecated. Inherit Datagrid::Base instead.")
     base.class_eval do
       include ::Datagrid::Core
       include ::Datagrid::ActiveModel
@@ -58,5 +25,6 @@ class ArgumentError < ::ArgumentError; end
   class ColumnUnavailableError < StandardError; end
 end
 
+require 'datagrid/base'
 require "datagrid/scaffold"
 I18n.load_path << File.expand_path("datagrid/locale/en.yml", __dir__)
diff --git a/lib/datagrid/base.rb b/lib/datagrid/base.rb
new file mode 100644
index 0000000..6d86f97
--- /dev/null
+++ b/lib/datagrid/base.rb
@@ -0,0 +1,39 @@
+module Datagrid
+  extend ActiveSupport::Autoload
+
+  autoload :Core
+  autoload :ActiveModel
+  autoload :Filters
+  autoload :Columns
+  autoload :ColumnNamesAttribute
+  autoload :Ordering
+  autoload :Configuration
+
+  autoload :Helper
+  autoload :FormBuilder
+
+  autoload :Renderer
+
+  autoload :Engine
+
+  # Main datagrid class allowing to define columns and filters on your objects
+  #
+  # @example
+  #   class UsersGrid < Datagrid::Base
+  #     scope { User }
+  #
+  #     filter(:id, :integer)
+  #     filter(:name, :string)
+  #
+  #     column(:id)
+  #     column(:name)
+  #   end
+  class Base
+    include ::Datagrid::Core
+    include ::Datagrid::ActiveModel
+    include ::Datagrid::Filters
+    include ::Datagrid::Columns
+    include ::Datagrid::ColumnNamesAttribute
+    include ::Datagrid::Ordering
+  end
+end
diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index 9c8386c..1578cbc 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -34,7 +34,8 @@ def datagrid_attribute(name, &block)
         end
       end
 
-      # @return [void] Defines a scope at class level
+      # Defines a relation scope of database models to be filtered
+      # @return [void]
       # @example
       #   scope { User }
       #   scope { Project.where(deleted: false) }
@@ -106,13 +107,15 @@ def dynamic(&block)
           end
       end
 
-      protected
-
+      # @!visibility private
       def check_scope_defined!(message = nil)
         message ||= "#{self}.scope is not defined"
         raise(Datagrid::ConfigurationError, message) unless scope_value
       end
 
+      protected
+
+      # @!visibility private
       def inherited(child_class)
         super
         child_class.datagrid_attributes = datagrid_attributes.clone
@@ -133,7 +136,7 @@ def initialize(attributes = nil, &block)
       scope(&block)
     end
 
-    # @return [Hash<Symbol, Object>] grid attributes including filter values and ordering values
+    # @return [{Symbol => Object}] grid attributes including filter values and ordering values
     def attributes
       result = {}
       datagrid_attributes.each do |name|
@@ -143,7 +146,7 @@ def attributes
     end
 
     # Updates datagrid attributes with a passed hash argument
-    # @param attributes [Hash<Symbol, Object>]
+    # @param attributes [{Symbol => Object}]
     # @example
     #   grid = MyGrid.new
     #   grid.attributes = {first_name: 'John', last_name: 'Smith'}
@@ -217,7 +220,7 @@ def scope(&block)
 
     # @!visibility private
     def original_scope
-      check_scope_defined!
+      self.class.check_scope_defined!
       scope_value.call
     end
 
@@ -236,11 +239,6 @@ def driver
       self.class.driver
     end
 
-    # @!visibility private
-    def check_scope_defined!(message = nil)
-      self.class.send :check_scope_defined!, message
-    end
-
     # @return [String] a datagrid attributes and their values in inspection form
     def inspect
       attrs = attributes.map do |key, value|
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index b1faa50..84cccb4 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -74,7 +74,7 @@ def pagination_helper_code
     end
 
     def base_grid_file
-      "app/grids/base_grid.rb"
+      "app/grids/application_grid.rb"
     end
 
     def grid_route_name
diff --git a/spec/datagrid/columns/column_spec.rb b/spec/datagrid/columns/column_spec.rb
index a2fe70e..12bc2b1 100644
--- a/spec/datagrid/columns/column_spec.rb
+++ b/spec/datagrid/columns/column_spec.rb
@@ -5,8 +5,7 @@
 describe Datagrid::Columns::Column do
   describe ".inspect" do
     subject do
-      class ColumnInspectTest
-        include Datagrid
+      class ColumnInspectTest < Datagrid::Base
         scope { Entry }
         column(:id, header: "ID")
       end
diff --git a/spec/datagrid/columns_spec.rb b/spec/datagrid/columns_spec.rb
index 9fdb23e..4b83f48 100644
--- a/spec/datagrid/columns_spec.rb
+++ b/spec/datagrid/columns_spec.rb
@@ -68,8 +68,7 @@
 
     describe "translations" do
       module ::Ns45
-        class TranslatedReport
-          include Datagrid
+        class TranslatedReport < Datagrid::Base
           scope { Entry }
           column(:name)
         end
@@ -81,8 +80,7 @@ class TranslatedReport
       end
 
       it "translates column-header without namespace" do
-        class Report27
-          include Datagrid
+        class Report27 < Datagrid::Base
           scope { Entry }
           column(:name)
         end
@@ -93,8 +91,7 @@ class Report27
       end
 
       it "translates column-header in using defaults namespace" do
-        class Report27
-          include Datagrid
+        class Report27 < Datagrid::Base
           scope { Entry }
           column(:name)
         end
@@ -188,8 +185,7 @@ class Report27
   end
 
   it "should inherit columns correctly" do
-    parent = Class.new do
-      include Datagrid
+    parent = Class.new(Datagrid::Base) do
       scope { Entry }
       column(:name)
     end
@@ -613,8 +609,7 @@ def capitalized_name
   describe "#data_hash" do
     it "works" do
       pending
-      class DataHashGrid
-        include Datagrid
+      class DataHashGrid < Datagrid::Base
         scope { Entry }
         column(:name, order: true)
       end
diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index ca14b37..4874b68 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -26,8 +26,7 @@
     before { 2.times { Entry.create } }
 
     let(:report_class) do
-      class ScopeTestReport
-        include Datagrid
+      class ScopeTestReport < Datagrid::Base
         scope { Entry.order("id desc") }
       end
       ScopeTestReport
@@ -99,8 +98,7 @@ class TestGrid < ScopeTestReport
 
   describe "#inspect" do
     it "should show all attribute values" do
-      class InspectTest
-        include Datagrid
+      class InspectTest < Datagrid::Base
         scope { Entry }
         filter(:created_at, :date, range: true)
         column(:name)
@@ -114,8 +112,7 @@ class InspectTest
   end
 
   describe "#==" do
-    class EqualTest
-      include Datagrid
+    class EqualTest < Datagrid::Base
       scope { Entry }
       filter(:created_at, :date)
       column(:name)
diff --git a/spec/datagrid/drivers/array_spec.rb b/spec/datagrid/drivers/array_spec.rb
index 5fdaa2c..daf3663 100644
--- a/spec/datagrid/drivers/array_spec.rb
+++ b/spec/datagrid/drivers/array_spec.rb
@@ -12,9 +12,8 @@
   end
 
   describe "api" do
-    class ArrayGrid
+    class ArrayGrid < Datagrid::Base
       User = Struct.new(:name, :age)
-      include Datagrid
       scope do
         []
       end
@@ -102,8 +101,7 @@ class ArrayGrid
   end
 
   describe "array of hashes" do
-    class HashGrid
-      include Datagrid
+    class HashGrid < Datagrid::Base
       scope do
         [{ name: "Bogdan", age: 30 }, { name: "Brad", age: 32 }]
       end
diff --git a/spec/datagrid/drivers/sequel_spec.rb b/spec/datagrid/drivers/sequel_spec.rb
index e95165b..5b79963 100644
--- a/spec/datagrid/drivers/sequel_spec.rb
+++ b/spec/datagrid/drivers/sequel_spec.rb
@@ -33,8 +33,7 @@
     end
 
     it "supports pagination" do
-      class PaginationTest
-        include Datagrid
+      class PaginationTest < Datagrid::Base
         scope { SequelEntry }
       end
       grid = PaginationTest.new do |scope|
diff --git a/spec/datagrid/filters/enum_filter_spec.rb b/spec/datagrid/filters/enum_filter_spec.rb
index 6ea8ec7..bc61a96 100644
--- a/spec/datagrid/filters/enum_filter_spec.rb
+++ b/spec/datagrid/filters/enum_filter_spec.rb
@@ -29,8 +29,7 @@
   end
 
   it "should initialize select option only on instanciation" do
-    class ReportWithLazySelect
-      include Datagrid
+    class ReportWithLazySelect < Datagrid::Base
       scope { Entry }
       filter(:group_id, :enum, select: proc { raise "hello" })
     end
diff --git a/spec/datagrid/filters_spec.rb b/spec/datagrid/filters_spec.rb
index afd239c..16d126e 100644
--- a/spec/datagrid/filters_spec.rb
+++ b/spec/datagrid/filters_spec.rb
@@ -45,8 +45,7 @@
   it "should initialize when report Scope table not exists" do
     class ModelWithoutTable < ActiveRecord::Base; end
     expect(ModelWithoutTable).not_to be_table_exists
-    class TheReport
-      include Datagrid
+    class TheReport < Datagrid::Base
 
       scope { ModelWithoutTable }
 
@@ -57,8 +56,7 @@ class TheReport
   end
 
   it "should support inheritence" do
-    parent = Class.new do
-      include Datagrid
+    parent = Class.new(Datagrid::Base) do
       scope { Entry }
       filter(:name)
     end
@@ -195,8 +193,7 @@ def check_performed(value, result, **options)
 
   describe "tranlations" do
     module ::Ns46
-      class TranslatedReport
-        include Datagrid
+      class TranslatedReport < Datagrid::Base
         scope { Entry }
         filter(:name)
       end
@@ -262,8 +259,7 @@ class InheritedReport < TranslatedReport
   describe "#inspect" do
     it "should list all fitlers with types" do
       module ::NsInspect
-        class TestGrid
-          include Datagrid
+        class TestGrid < Datagrid::Base
           scope { Entry }
           filter(:id, :integer)
           filter(:name, :string)
@@ -277,8 +273,7 @@ class TestGrid
     end
 
     it "dislays no filters" do
-      class TestGrid8728
-        include Datagrid
+      class TestGrid8728 < Datagrid::Base
         scope { Entry }
       end
 
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index a38b8ff..8b1f00b 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -419,8 +419,7 @@
 
   describe ".datagrid_order_for" do
     it "should render ordering layout" do
-      class OrderedGrid
-        include Datagrid
+      class OrderedGrid < Datagrid::Base
         scope { Entry }
         column(:category)
       end
@@ -442,8 +441,7 @@ class OrderedGrid
       expect(rendered_form).to include "Namespaced form partial."
     end
     it "should render form and filter inputs" do
-      class FormForGrid
-        include Datagrid
+      class FormForGrid < Datagrid::Base
         scope { Entry }
         filter(:category)
       end
@@ -459,8 +457,7 @@ class FormForGrid
     end
     it "should support html classes for grid class with namespace" do
       module ::Ns22
-        class TestGrid
-          include Datagrid
+        class TestGrid < Datagrid::Base
           scope { Entry }
           filter(:id)
         end
@@ -473,8 +470,7 @@ class TestGrid
     end
 
     it "should have overridable param_name method" do
-      class ParamNameGrid81
-        include Datagrid
+      class ParamNameGrid81 < Datagrid::Base
         scope { Entry }
         filter(:id)
         def param_name
@@ -487,8 +483,7 @@ def param_name
     end
 
     it "takes default partials if custom doesn't exist" do
-      class PartialDefaultGrid
-        include Datagrid
+      class PartialDefaultGrid < Datagrid::Base
         scope { Entry }
         filter(:id, :integer, range: true)
         filter(:group_id, :enum, multiple: true, checkboxes: true, select: [1, 2])
diff --git a/spec/datagrid/ordering_spec.rb b/spec/datagrid/ordering_spec.rb
index 028a448..a6306d3 100644
--- a/spec/datagrid/ordering_spec.rb
+++ b/spec/datagrid/ordering_spec.rb
@@ -102,8 +102,7 @@
   end
 
   it "should work correctly with inherited classes" do
-    class OrderInheritenceBase
-      include Datagrid
+    class OrderInheritenceBase < Datagrid::Base
       scope { Entry }
     end
 
diff --git a/spec/datagrid_spec.rb b/spec/datagrid_spec.rb
index 074ca2c..f3fdea6 100644
--- a/spec/datagrid_spec.rb
+++ b/spec/datagrid_spec.rb
@@ -90,4 +90,16 @@
       end
     end
   end
+
+  it "deprecates inclucsion of Datagrid module" do
+    silence_warnings do
+      class DeprecatedInclusion
+        include Datagrid
+        scope { Entry }
+        column(:name)
+      end
+    end
+    grid = DeprecatedInclusion.new
+    expect(grid.data).to eq([["Name"], ["Star"]])
+  end
 end
diff --git a/spec/support/mongoid.rb b/spec/support/mongoid.rb
index 878a20c..1fa6ec8 100644
--- a/spec/support/mongoid.rb
+++ b/spec/support/mongoid.rb
@@ -16,8 +16,7 @@ class MongoidEntry
   field :shipping_date, type: Time
 end
 
-class MongoidGrid
-  include ::Datagrid
+class MongoidGrid < Datagrid::Base
 
   scope do
     MongoidEntry
diff --git a/spec/support/sequel.rb b/spec/support/sequel.rb
index 77da337..d0492a9 100644
--- a/spec/support/sequel.rb
+++ b/spec/support/sequel.rb
@@ -20,9 +20,7 @@
 class SequelEntry < Sequel::Model
 end
 
-class SequelGrid
-  include ::Datagrid
-
+class SequelGrid < Datagrid::Base
   scope do
     SequelEntry
   end
diff --git a/spec/support/simple_report.rb b/spec/support/simple_report.rb
index ed5afab..7b5bfeb 100644
--- a/spec/support/simple_report.rb
+++ b/spec/support/simple_report.rb
@@ -6,9 +6,8 @@ def test_report(attributes = {}, &block)
 end
 
 def test_report_class(&block)
-  Class.new.tap do |klass|
+  Class.new(Datagrid::Base).tap do |klass|
     klass.class_eval do
-      include Datagrid
       def self.name
         "TestGrid"
       end
@@ -17,9 +16,7 @@ def self.name
   end
 end
 
-class SimpleReport
-  include Datagrid
-
+class SimpleReport < Datagrid::Base
   scope do
     ::Entry.includes(:group).order("entries.created_at")
   end
diff --git a/templates/base.rb.erb b/templates/base.rb.erb
index ed4281c..cde6316 100644
--- a/templates/base.rb.erb
+++ b/templates/base.rb.erb
@@ -1,7 +1,4 @@
-class BaseGrid
-
-  include Datagrid
-
+class BaseGrid < Datagrid::Base
   self.default_column_options = {
     # Uncomment to disable the default order
     # order: false,
@@ -41,5 +38,4 @@ class BaseGrid
       value ? "Yes" : "No"
     end
   end
-
 end

From debfd76cfa6cadaeba611ceccad43165d2757181 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 14:44:13 +0100
Subject: [PATCH 039/157] Rename BaseGrid to ApplicationGrid

---
 VERSION2.md              | 27 +++++++++++++++++++++++++++
 lib/datagrid/scaffold.rb |  4 ++++
 templates/base.rb.erb    |  2 +-
 templates/grid.rb.erb    |  2 +-
 4 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/VERSION2.md b/VERSION2.md
index 019f575..94b5589 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -15,6 +15,7 @@ List of things introduces:
 1. Rails Engines: While supported, the library was not initially designed for it.
 1. HTML5 data attributes
 1. Inherit `Datagrid::Base` instead of `include Datagrid`
+1. `ApplicationGrid` is now recommended name instead of `BaseGrid`
 
 ## Infinite Ranges for range filters
 
@@ -332,3 +333,29 @@ that needs to be inherited and high level namespace (just like most gems do):
 class ApplicationGrid < Datagrid::Base
 end
 ```
+
+## ApplicationGrid base class
+
+Previously recommended base class `BaseGrid` is incosistent
+with Rails naming conventionsa.
+It was renamed to `ApplicationGrid` instead:
+
+``` ruby
+# app/grids/application_grid.rb
+class ApplicationGrid < Datagrid::Base
+  def self.timestamp_column(name, *args, &block)
+    column(name, *args) do |model|
+      value = block ? block.call(model) : model.public_send(name)
+      value&.strftime("%Y-%m-%d")
+    end
+  end
+end
+
+# app/grids/users_grid.rb
+class UsersGrid < ApplicationGrid
+  scope { User }
+
+  column(:name)
+  timestamp_column(:created_at)
+end
+```
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index 84cccb4..b016c57 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -44,6 +44,10 @@ def grid_class_name
       "#{file_name.camelize.pluralize}Grid"
     end
 
+    def grid_base_class
+      file_exists?('app/grids/base_grid.rb') ? 'BaseGrid' : 'ApplicationGrid'
+    end
+
     def grid_controller_class_name
       "#{controller_class_name.camelize}Controller"
     end
diff --git a/templates/base.rb.erb b/templates/base.rb.erb
index cde6316..c5e5801 100644
--- a/templates/base.rb.erb
+++ b/templates/base.rb.erb
@@ -1,4 +1,4 @@
-class BaseGrid < Datagrid::Base
+class ApplicationGrid < Datagrid::Base
   self.default_column_options = {
     # Uncomment to disable the default order
     # order: false,
diff --git a/templates/grid.rb.erb b/templates/grid.rb.erb
index 2dfa87a..76b3e7d 100644
--- a/templates/grid.rb.erb
+++ b/templates/grid.rb.erb
@@ -1,4 +1,4 @@
-class <%= grid_class_name %> < BaseGrid
+class <%= grid_class_name %> < <%= grid_base_class %>
 
   scope do
     <%= grid_model_name %>

From 9279b7e945647f86e1afd959de0add8ab0080f05 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 17:11:24 +0100
Subject: [PATCH 040/157] Use data-* attributes distinguish filters

---
 VERSION2.md                         | 37 ++++++++++++++++++++++++-----
 app/views/datagrid/_form.html.erb   |  6 ++++-
 lib/datagrid/filters/base_filter.rb |  7 ++----
 lib/datagrid/filters/enum_filter.rb | 10 ++------
 lib/datagrid/form_builder.rb        |  2 +-
 spec/datagrid/helper_spec.rb        | 10 ++++----
 6 files changed, 46 insertions(+), 26 deletions(-)

diff --git a/VERSION2.md b/VERSION2.md
index 94b5589..ea2ab1e 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -1,21 +1,24 @@
 # Datagrid Version 2
 
 Datagrid v1 was released Sep 19 2013 - more than 10 years ago.
-A lot of new things had happened during this period.
+A lot of changes in best practices and available technology
+had happened during this period.
 It caused the library to be designed without support of those technologies
 or their implementation to be suboptimal because of backward compatibility.
-And now it is time to indroduce them with Version 2.
+Version 2 addresses all that evolution.
 
 List of things introduces:
 
 1. Ruby infinite ranges for range filters.
 1. Modern modular CSS classes.
 1. HTML5 input types: number, date, datetime-local.
-1. HTML5 input [names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
-1. Rails Engines: While supported, the library was not initially designed for it.
+1. HTML5 [input names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
+1. Native Rails Engines:
+   while supported, the library was not initially designed for it.
 1. HTML5 data attributes
 1. Inherit `Datagrid::Base` instead of `include Datagrid`
 1. `ApplicationGrid` is now recommended name instead of `BaseGrid`
+1. Remove SASS dependency
 
 ## Infinite Ranges for range filters
 
@@ -61,8 +64,6 @@ and avoid collisions with other libraries:
 | separator    | datagrid-range-separator            |
 | checkboxes   | datagrid-enum-checkboxes            |
 
-A few automatically generated classes were moved from `<input/>` to `<div class="datagrid-filter">`
-to make sure they are editable through datagrid partials.
 
 ### Example
 
@@ -148,6 +149,30 @@ instead of classes for meta information from backend.
 Therefor built-in partials now generate data attributes by default
 instead of classes for column names:
 
+### Filters
+
+``` html
+<div class="datagrid-filter filter">
+  <label for="form_for_grid_category">Category</label>
+  <input class="category default_filter" type="text" name="form_for_grid[category]" id="form_for_grid_category" />
+</div>
+```
+
+Version 2:
+
+``` html
+<div class="datagrid-filter"
+         data-datagrid-filter="category"
+         data-datagrid-filter-type="string"
+         data-datagrid-filter-checkboxes="false"
+>
+  <label for="form_for_grid_category">Category</label>
+  <input type="text" name="form_for_grid[category]" id="form_for_grid_category" />
+</div>
+```
+
+### Columns
+
 Version 1:
 
 ``` html
diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index 84cf58e..88a39f9 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -1,6 +1,10 @@
 <%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
   <% grid.filters.each do |filter| %>
-    <div class="datagrid-filter <%= filter.default_html_classes.join(' ') %>">
+    <div class="datagrid-filter" 
+         data-datagrid-filter="<%= filter.name %>" 
+         data-datagrid-filter-type="<%= filter.type %>"
+         data-datagrid-filter-checkboxes="<%= filter.enum_checkboxes? %>"
+    >
       <%= f.datagrid_label filter %>
       <%= f.datagrid_filter filter %>
     </div>
diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index 57e5e09..7d373aa 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -144,11 +144,8 @@ def enabled?(grid)
         ::Datagrid::Utils.process_availability(grid, options[:if], options[:unless])
       end
 
-      def default_html_classes
-        [
-          "datagrid-filter-#{name}",
-          "datagrid-filter-type-#{type}",
-        ]
+      def enum_checkboxes?
+        false
       end
 
       protected
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 75817e0..4133d07 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -11,7 +11,7 @@ class EnumFilter < Datagrid::Filters::BaseFilter
 
       def initialize(*args)
         super
-        options[:multiple] = true if checkboxes?
+        options[:multiple] = true if enum_checkboxes?
         raise Datagrid::ConfigurationError, ":select option not specified" unless options[:select]
       end
 
@@ -21,17 +21,11 @@ def parse(value)
         value
       end
 
-      def default_html_classes
-        res = super
-        res.push("datagrid-enum-checkboxes") if checkboxes?
-        res
-      end
-
       def strict
         options[:strict]
       end
 
-      def checkboxes?
+      def enum_checkboxes?
         options[:checkboxes]
       end
     end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 20a484f..05ae48b 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -94,7 +94,7 @@ def datagrid_default_filter(filter, options = {})
     end
 
     def datagrid_enum_filter(filter, options = {}, &block)
-      if filter.checkboxes?
+      if filter.enum_checkboxes?
         elements = object.select_options(filter).map do |element|
           text, value = @template.send(:option_text_and_value, element)
           checked = enum_checkbox_checked?(filter, value)
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 8b1f00b..8cb24fd 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -443,16 +443,16 @@ class OrderedGrid < Datagrid::Base
     it "should render form and filter inputs" do
       class FormForGrid < Datagrid::Base
         scope { Entry }
-        filter(:category)
+        filter(:category, :string)
       end
       object = FormForGrid.new(category: "hello")
       expect(subject.datagrid_form_for(object, url: "/grid")).to match_css_pattern(
         "form.datagrid-form[action='/grid']" => 1,
         "form input[name=utf8]" => 1,
-        "form .datagrid-filter label" => "Category",
-        "form .datagrid-filter-category input[name='form_for_grid[category]'][value=hello]" => 1,
-        "form input[name=commit][value=Search]" => 1,
-        "form a.datagrid-reset[href='/location']" => 1
+        "form .datagrid-filter[data-datagrid-filter=category][data-datagrid-filter-type=string] label" => "Category",
+        "form .datagrid-filter input[name='form_for_grid[category]'][value=hello]" => 1,
+        "form .datagrid-actions input[name=commit][value=Search]" => 1,
+        "form .datagrid-actions a.datagrid-reset[href='/location']" => 1
       )
     end
     it "should support html classes for grid class with namespace" do

From 9f0ce55d5e9d41fb2f1ae74311c0017ac5c80e6e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 17:55:24 +0100
Subject: [PATCH 041/157] Use Range for RangedFilter value

---
 VERSION2.md                                   | 12 ++--
 lib/datagrid/filters/ranged_filter.rb         | 62 ++++++++++---------
 lib/datagrid/form_builder.rb                  |  3 +-
 spec/datagrid/core_spec.rb                    |  2 +-
 spec/datagrid/filters/date_filter_spec.rb     | 16 ++---
 .../datagrid/filters/date_time_filter_spec.rb |  8 +--
 spec/datagrid/filters/integer_filter_spec.rb  |  6 +-
 7 files changed, 59 insertions(+), 50 deletions(-)

diff --git a/VERSION2.md b/VERSION2.md
index ea2ab1e..ef9f45d 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -12,12 +12,13 @@ List of things introduces:
 1. Ruby infinite ranges for range filters.
 1. Modern modular CSS classes.
 1. HTML5 input types: number, date, datetime-local.
-1. HTML5 [input names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
+1. Use Hash instead of Array for multiparameters attirubtes
+   to avoid [input names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
 1. Native Rails Engines:
    while supported, the library was not initially designed for it.
 1. HTML5 data attributes
 1. Inherit `Datagrid::Base` instead of `include Datagrid`
-1. `ApplicationGrid` is now recommended name instead of `BaseGrid`
+1. `ApplicationGrid` is recommended base class instead of `BaseGrid`
 1. Remove SASS dependency
 
 ## Infinite Ranges for range filters
@@ -64,7 +65,6 @@ and avoid collisions with other libraries:
 | separator    | datagrid-range-separator            |
 | checkboxes   | datagrid-enum-checkboxes            |
 
-
 ### Example
 
 The difference in layout generation from  v1 to v2
@@ -154,7 +154,8 @@ instead of classes for column names:
 ``` html
 <div class="datagrid-filter filter">
   <label for="form_for_grid_category">Category</label>
-  <input class="category default_filter" type="text" name="form_for_grid[category]" id="form_for_grid_category" />
+  <input class="category default_filter" type="text"
+     name="form_for_grid[category]" id="form_for_grid_category" />
 </div>
 ```
 
@@ -167,7 +168,8 @@ Version 2:
          data-datagrid-filter-checkboxes="false"
 >
   <label for="form_for_grid_category">Category</label>
-  <input type="text" name="form_for_grid[category]" id="form_for_grid_category" />
+  <input type="text"
+      name="form_for_grid[category]" id="form_for_grid_category" />
 </div>
 ```
 
diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index 004afa7..03c9174 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -7,19 +7,24 @@ def initialize(grid, name, options, &block)
         super
         return unless range?
 
-        options[:multiple] = true
+        options[:multiple] = false
       end
 
       def parse_values(value)
-        if value.is_a?(Hash)
-          value = parse_hash(value)
+        unless range?
+          return super
+        end
+        case value
+        when Hash
+          parse_hash(value)
+        when Array
+          parse_array(value)
+        when Range
+          to_range(value.begin, value.end)
+        else
+          result = super
+          to_range(result, result)
         end
-        result = super
-        return result if !range? || result.nil?
-        # Simulate single point range
-        return [result, result] unless result.is_a?(Array)
-
-        parse_array(result)
       end
 
       def range?
@@ -27,10 +32,9 @@ def range?
       end
 
       def default_filter_where(scope, value)
-        if range? && value.is_a?(Array)
-          left, right = value
-          scope = driver.greater_equal(scope, name, left) if left
-          scope = driver.less_equal(scope, name, right) if right
+        if range? && value.is_a?(Range)
+          scope = driver.greater_equal(scope, name, value.begin) if value.begin
+          scope = driver.less_equal(scope, name, value.end) if value.end
           scope
         else
           super
@@ -40,28 +44,30 @@ def default_filter_where(scope, value)
       protected
 
       def parse_hash(result)
-        if result[:from] || result[:to]
-          [result[:from], result[:to]]
-        else
-          nil
+        to_range(result[:from], result[:to])
+      end
+
+      def to_range(from, to)
+        from = parse(from)
+        to = parse(to)
+        return nil unless to || from
+
+        # If wrong range is given - reverse it to be always valid
+        if from && to && from > to
+          from, to = to, from
         end
+        from..to
       end
 
       def parse_array(result)
+        first = result.first
+        last = result.last
+
         case result.size
         when 0
           nil
-        when 1
-          result.first
-        when 2
-          if result.first && result.last && result.first > result.last
-            # If wrong range is given - reverse it to be always valid
-            result.reverse
-          elsif !result.first && !result.last
-            nil
-          else
-            result
-          end
+        when 1,2
+          to_range(first, last)
         else
           raise ArgumentError, "Can not create a date range from array of more than two: #{result.inspect}"
         end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 05ae48b..4893dab 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -186,8 +186,9 @@ def datagrid_range_filter(_type, filter, options = {})
     end
 
     def datagrid_range_filter_options(object, filter, type, options)
-      type_method_map = { from: :first, to: :last }
+      type_method_map = { from: :begin, to: :end }
       options = add_html_classes(options, "datagrid-range-#{type}")
+      options[:multiple] = true
       options[:value] = object[filter.name]&.public_send(type_method_map[type])
       # In case of datagrid ranged filter
       # from and to input will have same id
diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index 4874b68..41fa720 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -106,7 +106,7 @@ class InspectTest < Datagrid::Base
 
       grid = InspectTest.new(created_at: %w[2014-01-01 2014-08-05], descending: true, order: "name")
       expect(grid.inspect).to eq(
-        "#<InspectTest order: :name, descending: true, created_at: [Wed, 01 Jan 2014, Tue, 05 Aug 2014]>"
+        "#<InspectTest order: :name, descending: true, created_at: Wed, 01 Jan 2014..Tue, 05 Aug 2014>"
       )
     end
   end
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 8187b8b..9d09a52 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -41,7 +41,7 @@
       scope { Entry }
       filter(:created_at, :date, range: true)
     end
-    expect(report.created_at).to eq([from.to_date, to.to_date])
+    expect(report.created_at).to eq(from.to_date..to.to_date)
     expect(report.assets).not_to include(e1)
     expect(report.assets).to include(e2)
     expect(report.assets).not_to include(e3)
@@ -50,11 +50,11 @@
     report.created_at = {from: nil, to: nil}
     expect(report.created_at).to eq(nil)
     report.created_at = {from: Date.today, to: nil}
-    expect(report.created_at).to eq([Date.today, nil])
+    expect(report.created_at).to eq(Date.today..nil)
     report.created_at = {from: nil, to: Date.today}
-    expect(report.created_at).to eq([nil, Date.today])
+    expect(report.created_at).to eq(nil..Date.today)
     report.created_at = {from: Time.now, to: Time.now}
-    expect(report.created_at).to eq([Date.today, Date.today])
+    expect(report.created_at).to eq(Date.today..Date.today)
   end
 
   { active_record: Entry, mongoid: MongoidEntry, sequel: SequelEntry }.each do |orm, klass|
@@ -154,7 +154,7 @@ def entry_dated(date)
       scope { Entry }
       filter(:created_at, :date, range: true)
     end
-    expect(report.created_at).to eq([range.last.to_date, range.first.to_date])
+    expect(report.created_at).to eq(range.last.to_date..range.first.to_date)
     expect(report.assets).to include(e1)
     expect(report.assets).to include(e2)
     expect(report.assets).to include(e3)
@@ -166,7 +166,7 @@ def entry_dated(date)
     report = test_report(created_at: date) do
       scope { Entry }
       filter(:created_at, :date, range: true) do |value|
-        where("created_at >= ?", value)
+        where(created_at: value)
       end
     end
     expect(report.assets).not_to include(Entry.create!(created_at: time - 1.day))
@@ -202,14 +202,14 @@ def entry_dated(date)
       scope  { Entry }
       filter(:created_at, :date, range: true)
     end
-    expect(report.created_at).to eq([Date.new(2012, 0o1, 0o1), Date.new(2013, 0o1, 0o1)])
+    expect(report.created_at).to eq(Date.new(2012, 0o1, 0o1)..Date.new(2013, 0o1, 0o1))
   end
   it "should automatically reverse Array if first more than last" do
     report = test_report(created_at: %w[2013-01-01 2012-01-01]) do
       scope  { Entry }
       filter(:created_at, :date, range: true)
     end
-    expect(report.created_at).to eq([Date.new(2012, 0o1, 0o1), Date.new(2013, 0o1, 0o1)])
+    expect(report.created_at).to eq(Date.new(2012, 0o1, 0o1)..Date.new(2013, 0o1, 0o1))
   end
 
   it "should nullify blank range" do
diff --git a/spec/datagrid/filters/date_time_filter_spec.rb b/spec/datagrid/filters/date_time_filter_spec.rb
index b0d1627..b518b10 100644
--- a/spec/datagrid/filters/date_time_filter_spec.rb
+++ b/spec/datagrid/filters/date_time_filter_spec.rb
@@ -121,17 +121,17 @@ def entry_dated(date)
       scope { Entry }
       filter(:created_at, :datetime, range: true)
     end
-    expect(report.created_at).to eq([range.last, range.first])
+    expect(report.created_at).to eq(range.last..range.first)
     expect(report.assets).to include(e1)
     expect(report.assets).to include(e2)
     expect(report.assets).to include(e3)
   end
 
   it "should support block" do
-    report = test_report(created_at: Time.now) do
+    report = test_report(created_at: Time.now..) do
       scope { Entry }
       filter(:created_at, :datetime, range: true) do |value|
-        where("created_at >= ?", value)
+        where(created_at: value)
       end
     end
     expect(report.assets).not_to include(Entry.create!(created_at: 1.day.ago))
@@ -167,6 +167,6 @@ def entry_dated(date)
       scope  { Entry }
       filter(:created_at, :datetime, range: true)
     end
-    expect(report.created_at).to eq([Time.new(2012, 0o1, 0o1, 1, 0), Time.new(2013, 0o1, 0o1, 1, 0)])
+    expect(report.created_at).to eq(Time.new(2012, 0o1, 0o1, 1, 0)..Time.new(2013, 0o1, 0o1, 1, 0))
   end
 end
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index fb6f5d7..7cb937d 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -73,7 +73,7 @@
       scope { Entry }
       filter(:group_id, :integer, range: true)
     end
-    expect(report.group_id).to eq([1, 7])
+    expect(report.group_id).to eq(1..7)
     expect(report.assets).to include(entry7)
     expect(report.assets).to include(entry4)
     expect(report.assets).to include(entry1)
@@ -95,7 +95,7 @@
       scope { Entry.joins(:group) }
       filter(:rating, :integer, range: true)
     end
-    expect(report.rating).to eq([4, nil])
+    expect(report.rating).to eq(4..nil)
     expect(report.assets).not_to include(Entry.create!(group: Group.create!(rating: 3)))
     expect(report.assets).to include(Entry.create!(group: Group.create!(rating: 5)))
   end
@@ -105,7 +105,7 @@
       scope { Entry }
       filter(:group_id, :integer, multiple: true)
     end
-    expect(report.group_id).to eq([1, 2])
+    expect(report.group_id).to eq([1,2])
     expect(report.assets).to include(entry1)
     expect(report.assets).to include(entry2)
     expect(report.assets).not_to include(entry3)

From 68c2340d02a9ca885e6e3f5df6fd2dcf36184284 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 20:30:35 +0100
Subject: [PATCH 042/157] Change range inputs from array to [from] [to] hash

---
 VERSION2.md                           | 10 ++++--
 lib/datagrid/filters/ranged_filter.rb |  2 +-
 lib/datagrid/form_builder.rb          |  1 +
 spec/datagrid/form_builder_spec.rb    | 44 +++++++++++++--------------
 4 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/VERSION2.md b/VERSION2.md
index ef9f45d..88221c3 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -133,7 +133,11 @@ The default behavior can be changed back by using `input_options`:
 ``` ruby
 filter(:created_at, :date, range: true, input_options: {type: 'text'})
 filter(:salary, :integer, range: true, input_options: {type: 'text'})
+```
+
+Additionally, textarea inputs are now supported this way:
 
+``` ruby
 # Rendered as <textarea/> tag:
 filter(:text, :string, input_options: {type: 'textarea'})
 ```
@@ -212,13 +216,13 @@ Version 2:
 If you still want to have an HTML class attached to a column use `class` column option:
 
 ``` ruby
-column(:name, class: 'column-name')
+column(:name, class: 'short-column')
 ```
 
 ``` html
-<th class="column-name" data-datagrid-column="name">Name</th>
+<th class="short-column" data-datagrid-column="name">Name</th>
 ...
-<td class="column-name" data-datagrid-column="name">John</td>
+<td class="short-column" data-datagrid-column="name">John</td>
 ```
 
 If you want to change this behavior completely,
diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index 03c9174..1e0c105 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -69,7 +69,7 @@ def parse_array(result)
         when 1,2
           to_range(first, last)
         else
-          raise ArgumentError, "Can not create a date range from array of more than two: #{result.inspect}"
+          raise ArgumentError, "Can not create a range from array of more than two elements"
         end
       end
     end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 4893dab..95b47ad 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -190,6 +190,7 @@ def datagrid_range_filter_options(object, filter, type, options)
       options = add_html_classes(options, "datagrid-range-#{type}")
       options[:multiple] = true
       options[:value] = object[filter.name]&.public_send(type_method_map[type])
+      options[:name] = @template.field_name(object_name, filter.name, type)
       # In case of datagrid ranged filter
       # from and to input will have same id
       if !options.key?(:id)
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 1162c05..15314fa 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -180,10 +180,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="1" id="from_hello" class="datagrid-range-from"
-                multiple type="number" step="1" name="report[group_id][]"/>' \
+                multiple type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2" id="to_hello" class="datagrid-range-to"
-                multiple type="number" step="1" name="report[group_id][]"/>'
+                multiple type="number" step="1" name="report[group_id][to]"/>'
           )
         }
       end
@@ -192,10 +192,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="10" class="datagrid-range-from"
-                multiple type="number" step="1" name="report[group_id][]"/>' \
+                multiple type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to"
-                multiple type="number" step="1" name="report[group_id][]"/>'
+                multiple type="number" step="1" name="report[group_id][to]"/>'
           )
         }
         it { should be_html_safe }
@@ -204,9 +204,9 @@ class MyTemplate
         let(:_range) { [nil, 10] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][]"/>' \
+            '<input class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="10" class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][]"/>'
+            '<input value="10" class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][to]"/>'
           )
         }
         it { should be_html_safe }
@@ -216,9 +216,9 @@ class MyTemplate
         let(:_range) { 2..1 }
         it {
           should equal_to_dom(
-            '<input value="1" class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][]"/>' \
+            '<input value="1" class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="2" class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][]"/>'
+            '<input value="2" class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][to]"/>'
           )
         }
       end
@@ -238,9 +238,9 @@ class MyTemplate
         let(:_range) { nil }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][]">
+            '<input class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][from]">
             <span class="datagrid-range-separator"> - </span>
-            <input class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][]">'
+            <input class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][to]">'
           )
         }
       end
@@ -258,10 +258,10 @@ class MyTemplate
       it {
         should equal_to_dom(
           '<input value="1.5" class="datagrid-range-from"
-              multiple type="number" step="any" name="report[rating][]"/>' \
+              multiple type="number" step="any" name="report[rating][from]"/>' \
           '<span class="datagrid-range-separator"> - </span>' \
           '<input value="2.5" class="datagrid-range-to"
-              multiple type="number" step="any" name="report[rating][]"/>'
+              multiple type="number" step="any" name="report[rating][to]"/>'
         )
       }
     end
@@ -278,9 +278,9 @@ class MyTemplate
         let(:_range) { ["2012-01-03", nil] }
         it {
           should equal_to_dom(
-            '<input value="2012-01-03" class="datagrid-range-from" multiple type="date" name="report[created_at][]"/>' \
+            '<input value="2012-01-03" class="datagrid-range-from" multiple type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" multiple type="date" name="report[created_at][]" value=""/>'
+            '<input class="datagrid-range-to" multiple type="date" name="report[created_at][to]" value=""/>'
           )
         }
         it { should be_html_safe }
@@ -295,10 +295,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="2013-01-01" class="datagrid-range-from"
-                multiple type="date" name="report[created_at][]"/>' \
+                multiple type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2013-02-02" class="datagrid-range-to"
-                multiple type="date" name="report[created_at][]"/>'
+                multiple type="date" name="report[created_at][to]"/>'
           )
         }
       end
@@ -307,10 +307,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input class="datagrid-range-from"
-                multiple type="date" value="" name="report[created_at][]"/>' \
+                multiple type="date" value="" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-03" class="datagrid-range-to"
-                multiple type="date"  name="report[created_at][]"/>'
+                multiple type="date"  name="report[created_at][to]"/>'
           )
         }
         it { should be_html_safe }
@@ -321,10 +321,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="2012-01-01" class="datagrid-range-from"
-                multiple type="date" name="report[created_at][]"/>' \
+                multiple type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-02" class="datagrid-range-to"
-                multiple type="date" name="report[created_at][]"/>'
+                multiple type="date" name="report[created_at][to]"/>'
           )
         }
       end
@@ -337,9 +337,9 @@ class MyTemplate
         let(:_range) { [nil, nil] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="date" value="" name="report[created_at][]"/>' \
+            '<input class="datagrid-range-from" multiple type="date" value="" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" multiple type="date" value="" name="report[created_at][]"/>'
+            '<input class="datagrid-range-to" multiple type="date" value="" name="report[created_at][to]"/>'
           )
         }
       end

From 252e069d70303c797b557533b756aae5eb229577 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 6 Nov 2024 22:13:39 +0100
Subject: [PATCH 043/157] Dynamic filter rework to use Hash instead of Array

---
 VERSION2.md                                  | 51 +++++++++++++++++++-
 lib/datagrid/filters/dynamic_filter.rb       | 41 ++++++++++++++--
 spec/datagrid/filters/dynamic_filter_spec.rb | 32 +++++++-----
 spec/datagrid/form_builder_spec.rb           | 16 ++++--
 4 files changed, 116 insertions(+), 24 deletions(-)

diff --git a/VERSION2.md b/VERSION2.md
index 88221c3..ecc5db3 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -144,7 +144,56 @@ filter(:text, :string, input_options: {type: 'textarea'})
 
 ## Names collision restriction
 
-TODO
+HTML5 prohibits multiple inputs to have the same name.
+This is contradicts to Rails parameters convention that serializes multiple inputs with same name into array:
+
+``` html
+Date From:
+<input type="number" name="grid[members_count][]" value="1"/>
+Date To:
+<input type="number" name="grid[members_count][]" value="5"/>
+```
+
+Serialized to:
+
+``` ruby
+{grid: {members_count: ['1', '5']}}
+```
+
+V1 had used this convention for `range: true` and `dynamic` filter type.
+Now, they are using the following convention instead:
+
+``` html
+Date From:
+<input type="number" name="grid[members_count][from]" value="1"/>
+Date To:
+<input type="number" name="grid[members_count][to]" value="5"/>
+```
+
+`Grid#members_count` will automatically typecast a hash
+into appropriate `Range` on assignment:
+
+``` ruby
+grid.members_count = {from: 1, to: 5}
+grid.members_count # => 1..5
+```
+
+The old convention would still work
+to ensure smooth transition to new version:
+
+``` ruby
+grid.members_count = [3, 7]
+grid.members_count # => 3..7
+```
+
+However, the `f.datagrid_filter :members_count`
+will always generate from/to inputs instead:
+
+``` html
+<input value="3" type="number" step="1" name="grid[members_count][from]"/>
+<span class="datagrid-range-separator"> - </span>
+<input value="7" type="number" step="1" name="grid[members_count][to]"/>
+```
 
 ## HTML5 data attributes
 
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index 5e08b95..77c6507 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -31,18 +31,22 @@ def initialize(*)
       end
 
       def parse_values(filter)
-        field, operation, value = filter
 
-        [field, operation, type_cast(field, value)]
+        if filter.is_a?(Array)
+          field, operation, value = filter
+          filter = { field:, operation:, value: type_cast(field, value)}
+        end
+        filter ? FilterValue.new(filter) : nil
       end
 
       def unapplicable_value?(filter)
-        _, _, value = filter
-        super(value)
+        super(filter&.value)
       end
 
       def default_filter_where(scope, filter)
-        field, operation, value = filter
+        field = filter.field
+        operation = filter.operation
+        value = filter.value
         date_conversion = value.is_a?(Date) && driver.is_timestamp?(scope, field)
 
         return scope if field.blank? || operation.blank?
@@ -122,6 +126,33 @@ def type_cast(field, value)
       def column_type(field)
         grid_class.driver.normalized_column_type(grid_class.scope, field)
       end
+
+      class FilterValue < Hash
+        def initialize(object = nil)
+          super()
+          update(object) if object
+        end
+
+        def field
+          self[:field]
+        end
+
+        def operation
+          self[:operation]
+        end
+
+        def value
+          self[:value]
+        end
+
+        def to_ary
+          to_a
+        end
+
+        def to_a
+          [field, operation, value]
+        end
+      end
     end
   end
 end
diff --git a/spec/datagrid/filters/dynamic_filter_spec.rb b/spec/datagrid/filters/dynamic_filter_spec.rb
index 149a461..0c4a6cb 100644
--- a/spec/datagrid/filters/dynamic_filter_spec.rb
+++ b/spec/datagrid/filters/dynamic_filter_spec.rb
@@ -62,22 +62,26 @@
 
   it "should nullify incorrect value for integer" do
     report.condition = [:group_id, "<=", "aa"]
-    expect(report.condition).to eq([:group_id, "<=", nil])
+    expect(report.condition).to eq(
+      {field: :group_id, operation: "<=", value: nil}
+    )
   end
 
   it "should nullify incorrect value for date" do
     report.condition = [:shipping_date, "<=", "aa"]
-    expect(report.condition).to eq([:shipping_date, "<=", nil])
+    expect(report.condition).to eq({
+      field: :shipping_date, operation: "<=", value: nil
+    })
   end
 
   it "should nullify incorrect value for datetime" do
     report.condition = [:created_at, "<=", "aa"]
-    expect(report.condition).to eq([:created_at, "<=", nil])
+    expect(report.condition).to eq({field: :created_at, operation: "<=", value: nil})
   end
 
   it "should support date comparation operation by timestamp column" do
     report.condition = [:created_at, "<=", "1986-08-05"]
-    expect(report.condition).to eq([:created_at, "<=", Date.parse("1986-08-05")])
+    expect(report.condition).to eq({field: :created_at, operation: "<=", value: Date.parse("1986-08-05")})
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-04 01:01:01")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:00")))
@@ -87,7 +91,7 @@
 
   it "should support date = operation by timestamp column" do
     report.condition = [:created_at, "=", "1986-08-05"]
-    expect(report.condition).to eq([:created_at, "=", Date.parse("1986-08-05")])
+    expect(report.condition).to eq({field: :created_at, operation: "=", value: Date.parse("1986-08-05")})
     expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-04 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:01")))
@@ -98,7 +102,7 @@
 
   it "should support date =~ operation by timestamp column" do
     report.condition = [:created_at, "=~", "1986-08-05"]
-    expect(report.condition).to eq([:created_at, "=~", Date.parse("1986-08-05")])
+    expect(report.condition).to eq({field: :created_at, operation: "=~", value: Date.parse("1986-08-05")})
     expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-04 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:01")))
@@ -123,8 +127,10 @@
   it "should support allow_nil and allow_blank options" do
     grid = test_report do
       scope { Entry }
-      filter(:condition, :dynamic, allow_nil: true, allow_blank: true,
-                                   operations: [">=", "<="]) do |(field, operation, value), scope|
+      filter(
+        :condition, :dynamic, allow_nil: true, allow_blank: true,
+        operations: [">=", "<="]
+      ) do |(field, operation, value), scope|
         if value.blank?
           scope.where(disabled: false)
         else
@@ -133,8 +139,8 @@
       end
     end
 
-    expect(grid.assets).to_not include(Entry.create!(disabled: true))
-    expect(grid.assets).to include(Entry.create!(disabled: false))
+    # expect(grid.assets).to_not include(Entry.create!(disabled: true))
+    # expect(grid.assets).to include(Entry.create!(disabled: false))
 
     grid.condition = [:group_id, ">=", 3]
     expect(grid.assets).to include(Entry.create!(disabled: true, group_id: 4))
@@ -148,9 +154,9 @@
       scope { Entry }
       filter(
         :condition, :dynamic, operations: ["=", "!="]
-      ) do |(field, operation, value), scope|
-        if operation == "!="
-          scope.where("#{field} != ?", value)
+      ) do |filter, scope|
+        if filter.operation == "!="
+          scope.where("#{filter.field} != ?", filter.value)
         else
           default_filter
         end
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 15314fa..e7cb6b2 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -671,13 +671,19 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-            <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition"><option selected value="id">id</option>
-       <option value="name">name</option></select><select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
-       <option value="=~">&asymp;</option>
-       <option selected value="&gt;=">&ge;</option>
-       <option value="&lt;=">&le;</option></select><input class="datagrid-dynamic-value" name="report[condition][]" value="1" type="text"  id="report_condition">
+         <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition">
+           <option selected value="id">id</option>
+           <option value="name">name</option>
+         </select>
+         <select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition">
+           <option value="=">=</option>
+           <option value="=~">≈</option>
+           <option selected value="&gt;=">≥</option>
+           <option value="&lt;=">≤</option></select>
+         <input value="1" name="report[condition][]" class="datagrid-dynamic-value" type="text" id="report_condition"/>
           HTML
         end
+
         it { should equal_to_dom(expected_html) }
       end
 

From 67baa57435a50826588703266cfa9b13a60a7cc0 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 7 Nov 2024 11:07:41 +0100
Subject: [PATCH 044/157] Remove multiple attribute from range filters

---
 lib/datagrid/form_builder.rb       |  2 --
 spec/datagrid/form_builder_spec.rb | 44 +++++++++++++++---------------
 2 files changed, 22 insertions(+), 24 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 95b47ad..9b6c859 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -174,7 +174,6 @@ def dynamic_filter_select(name, variants, select_options, html_options)
 
     def datagrid_range_filter(_type, filter, options = {})
       if filter.range?
-        options = options.merge(multiple: true)
         from_options = datagrid_range_filter_options(object, filter, :from, options)
         to_options = datagrid_range_filter_options(object, filter, :to, options)
         render_partial "range_filter", {
@@ -188,7 +187,6 @@ def datagrid_range_filter(_type, filter, options = {})
     def datagrid_range_filter_options(object, filter, type, options)
       type_method_map = { from: :begin, to: :end }
       options = add_html_classes(options, "datagrid-range-#{type}")
-      options[:multiple] = true
       options[:value] = object[filter.name]&.public_send(type_method_map[type])
       options[:name] = @template.field_name(object_name, filter.name, type)
       # In case of datagrid ranged filter
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index e7cb6b2..5379fe8 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -180,10 +180,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="1" id="from_hello" class="datagrid-range-from"
-                multiple type="number" step="1" name="report[group_id][from]"/>' \
+                type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2" id="to_hello" class="datagrid-range-to"
-                multiple type="number" step="1" name="report[group_id][to]"/>'
+                type="number" step="1" name="report[group_id][to]"/>'
           )
         }
       end
@@ -192,10 +192,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="10" class="datagrid-range-from"
-                multiple type="number" step="1" name="report[group_id][from]"/>' \
+                type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to"
-                multiple type="number" step="1" name="report[group_id][to]"/>'
+                type="number" step="1" name="report[group_id][to]"/>'
           )
         }
         it { should be_html_safe }
@@ -204,9 +204,9 @@ class MyTemplate
         let(:_range) { [nil, 10] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][from]"/>' \
+            '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="10" class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][to]"/>'
+            '<input value="10" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>'
           )
         }
         it { should be_html_safe }
@@ -216,9 +216,9 @@ class MyTemplate
         let(:_range) { 2..1 }
         it {
           should equal_to_dom(
-            '<input value="1" class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][from]"/>' \
+            '<input value="1" class="datagrid-range-from" type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="2" class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][to]"/>'
+            '<input value="2" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>'
           )
         }
       end
@@ -238,9 +238,9 @@ class MyTemplate
         let(:_range) { nil }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="number" step="1" name="report[group_id][from]">
+            '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]">
             <span class="datagrid-range-separator"> - </span>
-            <input class="datagrid-range-to" multiple type="number" step="1" name="report[group_id][to]">'
+            <input class="datagrid-range-to" type="number" step="1" name="report[group_id][to]">'
           )
         }
       end
@@ -258,10 +258,10 @@ class MyTemplate
       it {
         should equal_to_dom(
           '<input value="1.5" class="datagrid-range-from"
-              multiple type="number" step="any" name="report[rating][from]"/>' \
+              type="number" step="any" name="report[rating][from]"/>' \
           '<span class="datagrid-range-separator"> - </span>' \
           '<input value="2.5" class="datagrid-range-to"
-              multiple type="number" step="any" name="report[rating][to]"/>'
+              type="number" step="any" name="report[rating][to]"/>'
         )
       }
     end
@@ -278,9 +278,9 @@ class MyTemplate
         let(:_range) { ["2012-01-03", nil] }
         it {
           should equal_to_dom(
-            '<input value="2012-01-03" class="datagrid-range-from" multiple type="date" name="report[created_at][from]"/>' \
+            '<input value="2012-01-03" class="datagrid-range-from" type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" multiple type="date" name="report[created_at][to]" value=""/>'
+            '<input class="datagrid-range-to" type="date" name="report[created_at][to]" value=""/>'
           )
         }
         it { should be_html_safe }
@@ -295,10 +295,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="2013-01-01" class="datagrid-range-from"
-                multiple type="date" name="report[created_at][from]"/>' \
+                type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2013-02-02" class="datagrid-range-to"
-                multiple type="date" name="report[created_at][to]"/>'
+                type="date" name="report[created_at][to]"/>'
           )
         }
       end
@@ -307,10 +307,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input class="datagrid-range-from"
-                multiple type="date" value="" name="report[created_at][from]"/>' \
+                type="date" value="" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-03" class="datagrid-range-to"
-                multiple type="date"  name="report[created_at][to]"/>'
+                type="date"  name="report[created_at][to]"/>'
           )
         }
         it { should be_html_safe }
@@ -321,10 +321,10 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="2012-01-01" class="datagrid-range-from"
-                multiple type="date" name="report[created_at][from]"/>' \
+                type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-02" class="datagrid-range-to"
-                multiple type="date" name="report[created_at][to]"/>'
+                type="date" name="report[created_at][to]"/>'
           )
         }
       end
@@ -337,9 +337,9 @@ class MyTemplate
         let(:_range) { [nil, nil] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" multiple type="date" value="" name="report[created_at][from]"/>' \
+            '<input class="datagrid-range-from" type="date" value="" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" multiple type="date" value="" name="report[created_at][to]"/>'
+            '<input class="datagrid-range-to" type="date" value="" name="report[created_at][to]"/>'
           )
         }
       end

From 6856b5320eb7e58e225f98be18f49d930fff8f7d Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 7 Nov 2024 13:48:12 +0100
Subject: [PATCH 045/157] Ability to serialize/deserialize ranged filters

---
 lib/datagrid/filters/ranged_filter.rb         | 19 ++++---
 .../datagrid/filters/date_time_filter_spec.rb | 27 ++++++++++
 spec/datagrid/filters/integer_filter_spec.rb  | 50 ++++++++++++++-----
 3 files changed, 75 insertions(+), 21 deletions(-)

diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index 1e0c105..abe73de 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -3,18 +3,21 @@
 module Datagrid
   module Filters
     module RangedFilter
-      def initialize(grid, name, options, &block)
-        super
-        return unless range?
-
-        options[:multiple] = false
-      end
+      SERIALIZED_RANGE =  /\A(.*)\.{2,3}(.*)\z/
 
       def parse_values(value)
         unless range?
           return super
         end
         case value
+        when String
+          if value == '..' || value == '...'
+            nil
+          elsif match = value.match(SERIALIZED_RANGE)
+            to_range(match.captures[0], match.captures[1], value.include?('...'))
+          else
+            super
+          end
         when Hash
           parse_hash(value)
         when Array
@@ -47,7 +50,7 @@ def parse_hash(result)
         to_range(result[:from], result[:to])
       end
 
-      def to_range(from, to)
+      def to_range(from, to, exclusive = false)
         from = parse(from)
         to = parse(to)
         return nil unless to || from
@@ -56,7 +59,7 @@ def to_range(from, to)
         if from && to && from > to
           from, to = to, from
         end
-        from..to
+        exclusive ? from...to : from..to
       end
 
       def parse_array(result)
diff --git a/spec/datagrid/filters/date_time_filter_spec.rb b/spec/datagrid/filters/date_time_filter_spec.rb
index b518b10..c3d99c9 100644
--- a/spec/datagrid/filters/date_time_filter_spec.rb
+++ b/spec/datagrid/filters/date_time_filter_spec.rb
@@ -169,4 +169,31 @@ def entry_dated(date)
     end
     expect(report.created_at).to eq(Time.new(2012, 0o1, 0o1, 1, 0)..Time.new(2013, 0o1, 0o1, 1, 0))
   end
+
+  it "supports serialized range value" do
+    from = Time.parse("2013-01-01 01:00")
+    to = Time.parse("2013-01-02 02:00")
+    report  = test_report do
+      scope { Entry }
+      filter(:created_at, :datetime, range: true)
+    end
+
+    report.created_at = (from..to).as_json
+    expect(report.created_at).to eq(from..to)
+
+    report.created_at = (from..).as_json
+    expect(report.created_at).to eq(from..)
+
+    report.created_at = (..to).as_json
+    expect(report.created_at).to eq(..to)
+
+    report.created_at = (from...to).as_json
+    expect(report.created_at).to eq(from...to)
+
+    report.created_at = (nil..nil).as_json
+    expect(report.created_at).to eq(nil)
+
+    report.created_at = (nil...nil).as_json
+    expect(report.created_at).to eq(nil)
+  end
 end
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index 7cb937d..9ea1183 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -33,9 +33,7 @@
       scope { Entry }
       filter(:group_id, :integer, range: true)
     end
-    expect(report.assets).not_to include(entry7)
-    expect(report.assets).to include(entry4)
-    expect(report.assets).not_to include(entry1)
+    expect(report.group_id).to eq(3..5)
   end
 
   it "should support minimum integer argument" do
@@ -43,9 +41,7 @@
       scope { Entry }
       filter(:group_id, :integer, range: true)
     end
-    expect(report.assets).not_to include(entry1)
-    expect(report.assets).not_to include(entry4)
-    expect(report.assets).to include(entry7)
+    expect(report.group_id).to eq(5..)
   end
 
   it "should support maximum integer argument" do
@@ -53,9 +49,7 @@
       scope { Entry }
       filter(:group_id, :integer, range: true)
     end
-    expect(report.assets).to include(entry1)
-    expect(report.assets).to include(entry4)
-    expect(report.assets).not_to include(entry7)
+    expect(report.group_id).to eq(..5)
   end
 
   it "should find something in one integer interval" do
@@ -68,15 +62,20 @@
     expect(report.assets).not_to include(entry1)
   end
 
-  it "should support invalid range" do
+  it "supports range inversion" do
     report = test_report(group_id: (7..1)) do
       scope { Entry }
       filter(:group_id, :integer, range: true)
     end
     expect(report.group_id).to eq(1..7)
-    expect(report.assets).to include(entry7)
-    expect(report.assets).to include(entry4)
-    expect(report.assets).to include(entry1)
+  end
+
+  it "converts infinite range to nil" do
+    report = test_report(group_id: (nil..nil)) do
+      scope { Entry }
+      filter(:group_id, :integer, range: true)
+    end
+    expect(report.group_id).to eq(nil)
   end
 
   it "should support block" do
@@ -146,4 +145,29 @@
 
     expect(report.group_id).to eq(group.id)
   end
+
+  it "supports serialized range value" do
+    report  = test_report do
+      scope { Entry }
+      filter(:group_id, :integer, range: true)
+    end
+
+    report.group_id = (1..5).as_json
+    expect(report.group_id).to eq(1..5)
+
+    report.group_id = (1..).as_json
+    expect(report.group_id).to eq(1..)
+
+    report.group_id = (..5).as_json
+    expect(report.group_id).to eq(..5)
+
+    report.group_id = (1...5).as_json
+    expect(report.group_id).to eq(1...5)
+
+    report.group_id = (nil..nil).as_json
+    expect(report.group_id).to eq(nil)
+
+    report.group_id = (nil...nil).as_json
+    expect(report.group_id).to eq(nil)
+  end
 end

From 329f32d04e75ce365656bef9d1bc5bf1209b4c9c Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 7 Nov 2024 13:57:37 +0100
Subject: [PATCH 046/157] Use hash instead of array when generating dynamic
 filter inputs

---
 lib/datagrid/form_builder.rb       | 15 ++++++++++-----
 spec/datagrid/form_builder_spec.rb | 22 +++++++++++-----------
 2 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 9b6c859..865ab96 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -131,10 +131,8 @@ def datagrid_integer_filter(filter, options = {})
     end
 
     def datagrid_dynamic_filter(filter, options = {})
-      input_name = "#{object_name}[#{filter.name}][]"
       field, operation, value = object.filter_value(filter)
       options = add_filter_options(filter, **options)
-      options = options.merge(name: input_name)
       field_input = dynamic_filter_select(
         filter.name,
         object.select_options(filter) || [],
@@ -144,7 +142,8 @@ def datagrid_dynamic_filter(filter, options = {})
           include_hidden: false,
           selected: field
         },
-        add_html_classes(options, "datagrid-dynamic-field")
+        **add_html_classes(options, "datagrid-dynamic-field"),
+        name: @template.field_name(object_name, filter.name, 'field')
       )
       operation_input = dynamic_filter_select(
         filter.name, filter.operations_select,
@@ -154,9 +153,15 @@ def datagrid_dynamic_filter(filter, options = {})
           prompt: false,
           selected: operation
         },
-        add_html_classes(options, "datagrid-dynamic-operation")
+        **add_html_classes(options, "datagrid-dynamic-operation"),
+        name: @template.field_name(object_name, filter.name, 'operation')
+      )
+      value_input = datagrid_filter_input(
+        filter.name,
+        **add_html_classes(options, "datagrid-dynamic-value"),
+        value: value,
+        name: @template.field_name(object_name, filter.name, 'value')
       )
-      value_input = datagrid_filter_input(filter.name, **add_html_classes(options, "datagrid-dynamic-value"), value: value)
       [field_input, operation_input, value_input].join("\n").html_safe
     end
 
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 5379fe8..27db321 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -647,7 +647,7 @@ class MyTemplate
       context "with no options" do
         let(:expected_html) do
           <<-HTML
-         <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition"><option value="id">Id</option>
+         <select class="datagrid-dynamic-field" name="report[condition][field]" id="report_condition"><option value="id">Id</option>
          <option value="group_id">Group</option>
          <option value="name">Name</option>
          <option value="category">Category</option>
@@ -657,10 +657,10 @@ class MyTemplate
          <option value="confirmed">Confirmed</option>
          <option value="shipping_date">Shipping date</option>
          <option value="created_at">Created at</option>
-         <option value="updated_at">Updated at</option></select><select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition"><option value="=">=</option>
+         <option value="updated_at">Updated at</option></select><select class="datagrid-dynamic-operation" name="report[condition][operation]" id="report_condition"><option value="=">=</option>
          <option value="=~">&asymp;</option>
          <option value="&gt;=">&ge;</option>
-         <option value="&lt;=">&le;</option></select><input class="datagrid-dynamic-value"  name="report[condition][]" type="text" id="report_condition">
+         <option value="&lt;=">&le;</option></select><input class="datagrid-dynamic-value"  name="report[condition][value]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -671,16 +671,16 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-         <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition">
+         <select class="datagrid-dynamic-field" name="report[condition][field]" id="report_condition">
            <option selected value="id">id</option>
            <option value="name">name</option>
          </select>
-         <select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition">
+         <select class="datagrid-dynamic-operation" name="report[condition][operation]" id="report_condition">
            <option value="=">=</option>
            <option value="=~">≈</option>
            <option selected value="&gt;=">≥</option>
            <option value="&lt;=">≤</option></select>
-         <input value="1" name="report[condition][]" class="datagrid-dynamic-value" type="text" id="report_condition"/>
+         <input value="1" name="report[condition][value]" class="datagrid-dynamic-value" type="text" id="report_condition"/>
           HTML
         end
 
@@ -693,8 +693,8 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
-       <option value="&lt;=">≤</option></select><input class="datagrid-dynamic-value" name="report[condition][]" type="text" id="report_condition">
+          <select class="datagrid-dynamic-field" name="report[condition][field]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><select class="datagrid-dynamic-operation" name="report[condition][operation]" id="report_condition"><option value="&gt;=">≥</option>
+       <option value="&lt;=">≤</option></select><input class="datagrid-dynamic-value" name="report[condition][value]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -706,8 +706,8 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <input class="datagrid-dynamic-field" name="report[condition][]" value="id" autocomplete="off" type="hidden" id="report_condition"><select class="datagrid-dynamic-operation" name="report[condition][]" id="report_condition"><option value="&gt;=">≥</option>
-       <option value="&lt;=">≤</option></select><input class="datagrid-dynamic-value" name="report[condition][]" type="text" id="report_condition">
+          <input class="datagrid-dynamic-field" name="report[condition][field]" value="id" autocomplete="off" type="hidden" id="report_condition"><select class="datagrid-dynamic-operation" name="report[condition][operation]" id="report_condition"><option value="&gt;=">≥</option>
+       <option value="&lt;=">≤</option></select><input class="datagrid-dynamic-value" name="report[condition][value]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }
@@ -718,7 +718,7 @@ class MyTemplate
         end
         let(:expected_html) do
           <<-HTML
-          <select class="datagrid-dynamic-field" name="report[condition][]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><input class="datagrid-dynamic-operation" name="report[condition][]" value="=" autocomplete="off" type="hidden" id="report_condition"><input class="datagrid-dynamic-value" name="report[condition][]" type="text" id="report_condition">
+          <select class="datagrid-dynamic-field" name="report[condition][field]" id="report_condition"><option value="id">id</option><option value="name">name</option></select><input class="datagrid-dynamic-operation" name="report[condition][operation]" value="=" autocomplete="off" type="hidden" id="report_condition"><input class="datagrid-dynamic-value" name="report[condition][value]" type="text" id="report_condition">
           HTML
         end
         it { should equal_to_dom(expected_html) }

From 113536fa813681fac3b9c2da51e680eb17974b69 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 7 Nov 2024 16:59:29 +0100
Subject: [PATCH 047/157] Update migration guide

---
 VERSION2.md | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/VERSION2.md b/VERSION2.md
index ecc5db3..563721e 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -42,6 +42,31 @@ grid.id # V1: [1, nil]
         # V2: (1..nil)
 ```
 
+Version 2 makes an effort to make the transition as smooth as possible to you:
+
+* Old Array format will be converted to new Range format
+* Serialization/Deserialization of Range is help correctly
+
+``` ruby
+grid.id = 1..5
+grid.id # => 1..5
+
+grid.id = "1..5"
+grid.id # => 1..5
+
+grid.id = [nil, 5]
+grid.id # => ..5
+
+grid.id = nil..nil
+grid id # => nil
+
+grid.id = 3..7
+# Simulate serialization/deserialization when interacting with
+# jobs queue or database storage
+grid = UsersGrid.new(ActiveSupport::JSON.load(grid.attributes.to_json))
+grid.id # => 3..7
+```
+
 ## Modern CSS classes naming conventions
 
 Built-in generated CSS classes renamed to match modern CSS naming conventions
@@ -67,7 +92,7 @@ and avoid collisions with other libraries:
 
 ### Example
 
-The difference in layout generation from  v1 to v2
+The difference in layout generation from v1 to v2.
 
 ``` ruby
 datagrid_form_for(@grid)

From ab79ed9101b3dffc5298d0dd7e935aeb8c8624df Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 10:38:10 +0100
Subject: [PATCH 048/157] Remove SASS

---
 VERSION2.md                                  | 105 ++++++------
 app/assets/stylesheets/datagrid.css          | 171 +++++++++++++++++++
 app/assets/stylesheets/datagrid.sass         | 130 --------------
 app/views/datagrid/_enum_checkboxes.html.erb |   4 +-
 app/views/datagrid/_form.html.erb            |   5 +-
 app/views/datagrid/_head.html.erb            |   2 +-
 app/views/datagrid/_row.html.erb             |   2 +-
 lib/datagrid/core.rb                         |   8 +-
 spec/datagrid/core_spec.rb                   |  24 ++-
 spec/datagrid/form_builder_spec.rb           |  18 +-
 spec/datagrid/helper_spec.rb                 |  94 +++++-----
 11 files changed, 315 insertions(+), 248 deletions(-)
 create mode 100644 app/assets/stylesheets/datagrid.css
 delete mode 100644 app/assets/stylesheets/datagrid.sass

diff --git a/VERSION2.md b/VERSION2.md
index 563721e..326c041 100644
--- a/VERSION2.md
+++ b/VERSION2.md
@@ -241,9 +241,8 @@ Version 2:
 
 ``` html
 <div class="datagrid-filter"
-         data-datagrid-filter="category"
-         data-datagrid-filter-type="string"
-         data-datagrid-filter-checkboxes="false"
+         data-filter="category"
+         data-type="string"
 >
   <label for="form_for_grid_category">Category</label>
   <input type="text"
@@ -274,16 +273,16 @@ Version 2:
 
 ``` html
 <tr>
-    <th data-datagrid-column="name">Name</th>
-    <th data-datagrid-column="category">Category</th>
+    <th data-column="name">Name</th>
+    <th data-column="category">Category</th>
 </tr>
 <tr>
-    <td data-datagrid-column="name">John</th>
-    <td data-datagrid-column="category">Worker</th>
+    <td data-column="name">John</th>
+    <td data-column="category">Worker</th>
 </tr>
 <tr>
-    <td data-datagrid-column="name">Mike</th>
-    <td data-datagrid-column="category">Manager</th>
+    <td data-column="name">Mike</th>
+    <td data-column="category">Manager</th>
 </tr>
 ```
 
@@ -294,14 +293,51 @@ column(:name, class: 'short-column')
 ```
 
 ``` html
-<th class="short-column" data-datagrid-column="name">Name</th>
+<th class="short-column" data-column="name">Name</th>
 ...
-<td class="short-column" data-datagrid-column="name">John</td>
+<td class="short-column" data-column="name">John</td>
 ```
 
 If you want to change this behavior completely,
 modify [built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
 
+## Inherit Datagrid::Base
+
+`include Datagrid` causes method name space to be clamsy.
+Version 2 introduces a difference between the class
+that needs to be inherited and high level namespace (just like most gems do):
+
+``` ruby
+class ApplicationGrid < Datagrid::Base
+end
+```
+
+## ApplicationGrid base class
+
+Previously recommended base class `BaseGrid` is incosistent
+with Rails naming conventionsa.
+It was renamed to `ApplicationGrid` instead:
+
+``` ruby
+# app/grids/application_grid.rb
+class ApplicationGrid < Datagrid::Base
+  def self.timestamp_column(name, *args, &block)
+    column(name, *args) do |model|
+      value = block ? block.call(model) : model.public_send(name)
+      value&.strftime("%Y-%m-%d")
+    end
+  end
+end
+
+# app/grids/users_grid.rb
+class UsersGrid < ApplicationGrid
+  scope { User }
+
+  column(:name)
+  timestamp_column(:created_at)
+end
+```
+
 ## All changes in built-in partials
 
 Version 2 built-in partials are trying to expose
@@ -328,15 +364,19 @@ index 9f48319..f114c17 100644
  <%- end -%>
  <%- end -%>
 diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
-index 7e175c1..84cf58e 100644
+index 7e175c1..88a39f9 100644
 --- a/app/views/datagrid/_form.html.erb
 +++ b/app/views/datagrid/_form.html.erb
-@@ -1,12 +1,12 @@
+@@ -1,12 +1,16 @@
 -<%= form_for grid, options do |f| -%>
 +<%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
    <% grid.filters.each do |filter| %>
 -    <div class="datagrid-filter filter">
-+    <div class="datagrid-filter <%= filter.default_html_classes.join(' ') %>">
++    <div class="datagrid-filter"
++         data-datagrid-filter="<%= filter.name %>"
++         data-type="<%= filter.type %>"
++         data-datagrid-filter-checkboxes="<%= filter.enum_checkboxes? %>"
++    >
        <%= f.datagrid_label filter %>
        <%= f.datagrid_filter filter %>
      </div>
@@ -427,40 +467,3 @@ index 8708c05..0b5ff24 100644
 +  <%= I18n.t("datagrid.table.no_columns") %>
  <% end %>
 ```
-
-## Inherit Datagrid::Base
-
-`include Datagrid` causes method name space to be clamsy.
-Version 2 introduces a difference between the class
-that needs to be inherited and high level namespace (just like most gems do):
-
-``` ruby
-class ApplicationGrid < Datagrid::Base
-end
-```
-
-## ApplicationGrid base class
-
-Previously recommended base class `BaseGrid` is incosistent
-with Rails naming conventionsa.
-It was renamed to `ApplicationGrid` instead:
-
-``` ruby
-# app/grids/application_grid.rb
-class ApplicationGrid < Datagrid::Base
-  def self.timestamp_column(name, *args, &block)
-    column(name, *args) do |model|
-      value = block ? block.call(model) : model.public_send(name)
-      value&.strftime("%Y-%m-%d")
-    end
-  end
-end
-
-# app/grids/users_grid.rb
-class UsersGrid < ApplicationGrid
-  scope { User }
-
-  column(:name)
-  timestamp_column(:created_at)
-end
-```
diff --git a/app/assets/stylesheets/datagrid.css b/app/assets/stylesheets/datagrid.css
new file mode 100644
index 0000000..fee4ee5
--- /dev/null
+++ b/app/assets/stylesheets/datagrid.css
@@ -0,0 +1,171 @@
+table.datagrid-table {
+  background-color: transparent;
+  border-collapse: collapse;
+  max-width: 100%;
+}
+
+table.datagrid-table th {
+  background-color: #eee;
+  text-align: left;
+}
+
+table.datagrid-table td,
+table.datagrid-table th {
+  border: 1px solid #d6d6d6;
+  padding: 5px 10px;
+}
+
+.datagrid-order-control-asc,
+.datagrid-order-control-desc {
+  text-decoration: none;
+  font-weight: normal;
+}
+
+.datagrid-order-active-asc,
+.datagrid-order-active-desc {
+  background-color: #fff7d5;
+}
+
+.datagrid-order-active-asc a.datagrid-order-control-asc,
+.datagrid-order-active-desc a.datagrid-order-control-desc {
+  font-weight: bold;
+  color: #d00;
+}
+
+.datagrid-no-results {
+  text-align: center;
+}
+
+.datagrid-form {
+  background-color: #f0f0f0;
+  border-radius: 5px;
+  padding: 20px;
+}
+
+.datagrid-filter {
+  margin: 10px;
+}
+
+.datagrid-filter::before,
+.datagrid-filter::after {
+  content: '';
+  display: table;
+}
+
+.datagrid-filter::after {
+  clear: both;
+}
+
+.datagrid-filter label {
+  width: 150px;
+  float: left;
+}
+
+.datagrid-filter a {
+  float: left;
+}
+
+.datagrid-filter input {
+  border: 2px solid #ccc;
+  border-radius: 4px;
+  float: left;
+  padding: 5px 12px;
+  width: 300px;
+}
+
+.datagrid-filter input.datagrid-range-from,
+.datagrid-filter input.datagrid-range-to {
+  width: 83px;
+}
+
+.datagrid-filter select {
+  float: left;
+  width: 300px
+}
+
+.datagrid-filter select[multiple] {
+  border: 2px solid #ccc;
+  border-radius: 5px;
+  height: 100px;
+}
+
+.datagrid-filter .datagrid-dynamic-field {
+  width: 178px;
+}
+
+.datagrid-filter .datagrid-dynamic-operation {
+  margin-left: 7px;
+  width: 50px;
+}
+
+.datagrid-filter .datagrid-dynamic-value {
+  margin: 10px 0 0 150px;
+  clear: both;
+}
+
+.datagrid-filter .datagrid-range-separator {
+  float: left;
+  margin: 6px 4px 0;
+}
+
+.datagrid-enum-checkboxes {
+  float: left;
+}
+
+.datagrid-enum-checkboxes label {
+  display: block;
+  float: none;
+  width: 100%;
+}
+
+input.datagrid-range-from, input.datagrid-range-to {
+  width: 144px !important
+}
+
+select.datagrid-dynamic-field {
+  width: 242px
+}
+
+.datagrid-enum-checkboxes input {
+  margin: 7px;
+  float: none;
+  width: auto;
+}
+
+.datagrid-actions {
+  padding-left: calc(150px + 10px);
+}
+
+.datagrid-actions input[type='submit'] {
+  background-color: #555;
+  border: none;
+  border-radius: 5px;
+  color: white;
+  cursor: pointer;
+  font-size: 14px;
+  font-weight: bold;
+  line-height: normal;
+  padding: 7px 15px;
+  vertical-align: middle;
+  display: inline-block;
+  zoom: 1;
+  *display: inline;
+}
+
+.datagrid-actions input[type='submit']:hover,
+.datagrid-actions input[type='submit']:focus {
+  background-color: #333;
+}
+
+.datagrid-actions input[type='submit']:active {
+  background-color: #000;
+}
+
+.datagrid-actions > a {
+  font-size: 14px;
+  padding: 7px 15px;
+  vertical-align: middle;
+  display: inline-block;
+  zoom: 1;
+  *display: inline;
+}
diff --git a/app/assets/stylesheets/datagrid.sass b/app/assets/stylesheets/datagrid.sass
deleted file mode 100644
index 0f25722..0000000
--- a/app/assets/stylesheets/datagrid.sass
+++ /dev/null
@@ -1,130 +0,0 @@
-$dg-form-label: 150px
-
-= clearfix
-  *zoom: 1
-
-  &:before,
-  &:after
-    display: table
-    content: ''
-
-  &:after
-    clear: both
-
-=inline-block
-  display: inline-block
-  zoom: 1
-  *display: inline
-
-table.datagrid-table
-  background-color: transparent
-  border-collapse: collapse
-  max-width: 100%
-
-  th
-    background-color: #eee
-    text-align: left
-
-  td,
-  th 
-    border: 1px solid #d6d6d6
-    padding: 5px 10px
-
-.datagrid-order
-  a.datagird-order-control-asc, a.datagird-order-control-desc
-    text-decoration: none
-    font-weight: normal
-
-.datagrid-order-active-asc, .datagrid-order-active-desc
-  background-color: #fff7d5
-
-  a.datagird-order-control-asc, a.datagird-order-control-desc
-    font-weight: bold
-    color: #d00
-
-.datagrid-no-results
-  text-align: center
-    
-.datagrid-form
-  background-color: #f0f0f0
-  border-radius: 5px
-  padding: 20px
-
-.datagrid-filter
-  margin: 10px
-  +clearfix
-
-  label
-    width: $dg-form-label
-    float: left
-  a
-    float: left
-
-  input
-    border: 2px solid #ccc
-    border-radius: 4px
-    float: left
-    padding: 5px 12px
-    width: 207px
-
-    &.datagrid-range-from, &.datagrid-range-to
-      width: 83px
-
-  select
-    float: left
-    width: 235px
-
-    &[multiple]
-      border: 2px solid #ccc
-      border-radius: 5px
-      height: 100px
-  .datagrid-dynamic-field
-    width: 178px
-  .datagrid-dynamic-operation
-    margin-left: 7px
-    width: 50px
-  .datagrid-dynamic-value
-    margin: 10px 0 0 $dg-form-label
-    clear: both
-
-  .datagrid-range-separator
-    float: left
-    margin: 6px 4px 0
-  &.datagrid-enum-checkboxes 
-    label
-      float: none
-      display: block
-      width: 100%
-      margin-left: 150px
-    input
-      margin: 7px
-
-
-.datagrid-actions
-  padding-left: $dg-form-label + 10
-
-  input[type='submit']
-    background-color: #555
-    border: none
-    border-radius: 5px
-    color: white
-    cursor: pointer
-    font-size: 14px
-    font-weight: bold
-    line-height: normal
-    padding: 7px 15px
-    vertical-align: middle
-    +inline-block
-
-    &:hover,
-    &:focus
-      background-color: #333
-
-    &:active
-      background-color: #000
-
-  > a
-    font-size: 14px
-    padding: 7px 15px
-    vertical-align: middle
-    +inline-block
diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
index f114c17..2d46139 100644
--- a/app/views/datagrid/_enum_checkboxes.html.erb
+++ b/app/views/datagrid/_enum_checkboxes.html.erb
@@ -2,10 +2,12 @@
 Indent in this file may cause extra space to appear.
 You can add indent if whitespace doesn't matter for you
 %>
+<div class="datagrid-enum-checkboxes">
 <%- elements.each do |value, text, checked| -%>
 <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
-<%= form.datagrid_label(filter.name, **options, for: id) do -%>
+<%= form.datagrid_label(filter.name, **options, for: id, class: 'datagrid-enum-checkbox-label') do -%>
 <%= form.datagrid_filter_input(filter.name, type: :checkbox, multiple: true, id: id, checked: checked, include_hidden: false, value: value.to_s) -%>
 <%= text -%>
 <%- end -%>
 <%- end -%>
+</div>
diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index 88a39f9..bd66d9f 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -1,9 +1,8 @@
 <%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
   <% grid.filters.each do |filter| %>
     <div class="datagrid-filter" 
-         data-datagrid-filter="<%= filter.name %>" 
-         data-datagrid-filter-type="<%= filter.type %>"
-         data-datagrid-filter-checkboxes="<%= filter.enum_checkboxes? %>"
+         data-filter="<%= filter.name %>" 
+         data-type="<%= filter.type %>"
     >
       <%= f.datagrid_label filter %>
       <%= f.datagrid_filter filter %>
diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index affccf4..c3264bb 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -1,6 +1,6 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <th class="<%= datagrid_column_classes(grid, column) %>" data-datagrid-column="<%= column.name %>">
+    <th class="<%= datagrid_column_classes(grid, column) %>" data-column="<%= column.name %>">
       <%= column.header %>
       <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
     </th>
diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
index b431ab7..20f3ffb 100644
--- a/app/views/datagrid/_row.html.erb
+++ b/app/views/datagrid/_row.html.erb
@@ -1,5 +1,5 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <td class="<%= datagrid_column_classes(grid, column) %>" data-datagrid-column="<%= column.name %>"><%= datagrid_value(grid, column, asset) %></td>
+    <td class="<%= datagrid_column_classes(grid, column) %>" data-column="<%= column.name %>"><%= datagrid_value(grid, column, asset) %></td>
   <% end %>
 </tr>
diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index 1578cbc..1a167c9 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -261,7 +261,13 @@ def reset
     protected
 
     def sanitize_for_mass_assignment(attributes)
-      forbidden_attributes_protection ? super : attributes
+      if forbidden_attributes_protection
+        super
+      elsif defined?(ActionController::Parameters) && attributes.is_a?(ActionController::Parameters)
+        attributes.permit!.to_h
+      else
+        attributes
+      end
     end
   end
 end
diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index 41fa720..c2ff630 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -206,13 +206,14 @@ class EqualTest < Datagrid::Base
     end
 
     it "permites all attributes by default" do
-      expect do
-        test_report(params) do
-          scope { Entry }
-          filter(:name)
-        end
-      end.to_not raise_error
+      grid = test_report(params) do
+        scope { Entry }
+        filter(:name)
+      end
+
+      expect(grid.name).to eq('one')
     end
+
     it "doesn't permit attributes when forbidden_attributes_protection is set" do
       expect do
         test_report(params) do
@@ -222,6 +223,7 @@ class EqualTest < Datagrid::Base
         end
       end.to raise_error(ActiveModel::ForbiddenAttributesError)
     end
+
     it "permits attributes when forbidden_attributes_protection is set and attributes are permitted" do
       expect do
         test_report(params.permit!) do
@@ -231,6 +233,16 @@ class EqualTest < Datagrid::Base
         end
       end.to_not raise_error
     end
+
+    it "supports hash attribute assignment" do
+      grid = test_report(
+        ActionController::Parameters.new(group_id: {from: 1, to:2})
+      ) do
+        scope { Entry }
+        filter(:group_id, :integer, range: true)
+      end
+      expect(grid.group_id).to eq(1..2)
+    end
   end
 
   describe ".query_param" do
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 27db321..5b64bb5 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -439,18 +439,20 @@ class MyTemplate
         let(:_category_filter_options) { { checkboxes: true } }
         it {
           should equal_to_dom(
-            '
-<label for="report_category_first">
+            <<-HTML
+<div class="datagrid-enum-checkboxes">
+<label for="report_category_first" class="datagrid-enum-checkbox-label">
 <input type="checkbox" id="report_category_first"
     value="first" name="report[category][]" />
 first
 </label>
-<label for="report_category_second">
+<label for="report_category_second" class="datagrid-enum-checkbox-label">
 <input type="checkbox" id="report_category_second"
     value="second" name="report[category][]" />
 second
 </label>
-'
+</div>
+            HTML
           )
         }
 
@@ -611,18 +613,20 @@ class MyTemplate
       let(:_filter) { :column_names }
       let(:expected_html) do
         <<~DOM
-          <label for="report_column_names_id">
+        <div class="datagrid-enum-checkboxes">
+          <label for="report_column_names_id" class="datagrid-enum-checkbox-label">
             <input id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">
             Id
           </label>
-          <label for="report_column_names_name">
+          <label for="report_column_names_name" class="datagrid-enum-checkbox-label">
             <input id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]"/>
             Name
           </label>
-          <label for="report_column_names_category">
+          <label for="report_column_names_category" class="datagrid-enum-checkbox-label">
             <input id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">
             Category
           </label>
+        </div>
         DOM
       end
 
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 8cb24fd..46f4498 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -58,12 +58,12 @@
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern({
-                                                    "table.datagrid-table tr th[data-datagrid-column=group] div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th[data-datagrid-column=group]" => /Group.*/,
-                                                    "table.datagrid-table tr th[data-datagrid-column=name] div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th[data-datagrid-column=name]" => /Name.*/,
-                                                    "table.datagrid-table tr td[data-datagrid-column=group]" => "Pop",
-                                                    "table.datagrid-table tr td[data-datagrid-column=name]" => "Star"
+                                                    "table.datagrid-table tr th[data-column=group] div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th[data-column=group]" => /Group.*/,
+                                                    "table.datagrid-table tr th[data-column=name] div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th[data-column=name]" => /Name.*/,
+                                                    "table.datagrid-table tr td[data-column=group]" => "Pop",
+                                                    "table.datagrid-table tr td[data-column=name]" => "Star"
                                                   })
     end
 
@@ -72,12 +72,12 @@
       datagrid_table = subject.datagrid_table(grid, [entry])
 
       expect(datagrid_table).to match_css_pattern({
-                                                    "table.datagrid-table tr th[data-datagrid-column=group] div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th[data-datagrid-column=group]" => /Group.*/,
-                                                    "table.datagrid-table tr th[data-datagrid-column=name] div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th[data-datagrid-column=name]" => /Name.*/,
-                                                    "table.datagrid-table tr td[data-datagrid-column=group]" => "Pop",
-                                                    "table.datagrid-table tr td[data-datagrid-column=name]" => "Star"
+                                                    "table.datagrid-table tr th[data-column=group] div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th[data-column=group]" => /Group.*/,
+                                                    "table.datagrid-table tr th[data-column=name] div.datagrid-order" => 1,
+                                                    "table.datagrid-table tr th[data-column=name]" => /Name.*/,
+                                                    "table.datagrid-table tr td[data-column=group]" => "Pop",
+                                                    "table.datagrid-table tr td[data-column=name]" => "Star"
                                                   })
     end
 
@@ -87,10 +87,10 @@
 
     it "should support columns option" do
       expect(subject.datagrid_table(grid, [entry], columns: [:name])).to match_css_pattern(
-        "table.datagrid-table th[data-datagrid-column=name]" => 1,
-        "table.datagrid-table td[data-datagrid-column=name]" => 1,
-        "table.datagrid-table th[data-datagrid-column=group]" => 0,
-        "table.datagrid-table td[data-datagrid-column=group]" => 0
+        "table.datagrid-table th[data-column=name]" => 1,
+        "table.datagrid-table td[data-column=name]" => 1,
+        "table.datagrid-table th[data-column=group]" => 0,
+        "table.datagrid-table td[data-column=group]" => 0
       )
     end
 
@@ -105,10 +105,10 @@
 
       it "should output only given column names" do
         expect(subject.datagrid_table(grid, [entry])).to match_css_pattern(
-          "table.datagrid-table th[data-datagrid-column=name]" => 1,
-          "table.datagrid-table td[data-datagrid-column=name]" => 1,
-          "table.datagrid-table th[data-datagrid-column=category]" => 0,
-          "table.datagrid-table td[data-datagrid-column=category]" => 0
+          "table.datagrid-table th[data-column=name]" => 1,
+          "table.datagrid-table td[data-column=name]" => 1,
+          "table.datagrid-table th[data-column=category]" => 0,
+          "table.datagrid-table td[data-column=category]" => 0
         )
       end
     end
@@ -156,8 +156,8 @@
       end
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
-          "table.datagrid-table th[data-datagrid-column=name]" => 1,
-          "table.datagrid-table td[data-datagrid-column=name]" => 2
+          "table.datagrid-table th[data-column=name]" => 1,
+          "table.datagrid-table td[data-column=name]" => 2
         )
       end
     end
@@ -172,8 +172,8 @@
       end
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
-          "table.datagrid-table th[data-datagrid-column=name]" => 1,
-          "table.datagrid-table td[data-datagrid-column=name]" => 2
+          "table.datagrid-table th[data-column=name]" => 1,
+          "table.datagrid-table td[data-column=name]" => 2
         )
       end
     end
@@ -186,7 +186,7 @@
         column(:name)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name].datagrid-order-active-asc" => "Star"
+        "tr td[data-column=name].datagrid-order-active-asc" => "Star"
       )
     end
     it "should add ordering classes to column" do
@@ -209,7 +209,7 @@
         column(:name)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name].datagrid-order-active-desc" => "Star"
+        "tr td[data-column=name].datagrid-order-active-desc" => "Star"
       )
     end
 
@@ -220,7 +220,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star"
       )
     end
 
@@ -232,7 +232,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name] span" => "Star"
+        "tr td[data-column=name] span" => "Star"
       )
     end
 
@@ -243,7 +243,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star"
       )
     end
 
@@ -256,7 +256,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star"
       )
     end
 
@@ -267,7 +267,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star"
       )
     end
 
@@ -277,7 +277,7 @@
         column(:name, html: ->(data) { content_tag :h1, data })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name] h1" => "Star"
+        "tr td[data-column=name] h1" => "Star"
       )
     end
 
@@ -289,7 +289,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name] em" => "STAR"
+        "tr td[data-column=name] em" => "STAR"
       )
     end
 
@@ -301,7 +301,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name] span" => "Star-Entry"
+        "tr td[data-column=name] span" => "Star-Entry"
       )
     end
 
@@ -313,7 +313,7 @@
         })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name] h1" => "Star-star"
+        "tr td[data-column=name] h1" => "Star-star"
       )
     end
 
@@ -325,7 +325,7 @@
         })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name] h1" => "Star-star-Entry"
+        "tr td[data-column=name] h1" => "Star-star-Entry"
       )
     end
 
@@ -339,7 +339,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name] h1" => "STAR-Star"
+        "tr td[data-column=name] h1" => "STAR-Star"
       )
     end
 
@@ -353,7 +353,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name] h1" => "STAR-Star-Entry"
+        "tr td[data-column=name] h1" => "STAR-Star-Entry"
       )
     end
 
@@ -364,10 +364,10 @@
         column(:category)
       end
       expect(subject.datagrid_rows(rp, [entry], columns: [:name])).to match_css_pattern(
-        "tr td[data-datagrid-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star"
       )
       expect(subject.datagrid_rows(rp, [entry], columns: [:name])).to match_css_pattern(
-        "tr td[data-datagrid-column=category]" => 0
+        "tr td[data-column=category]" => 0
       )
     end
 
@@ -378,7 +378,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-datagrid-column=name].my_class" => "Star"
+        "tr td[data-column=name].my_class" => "Star"
       )
     end
 
@@ -394,7 +394,7 @@
       end
       it "should ignore them" do
         expect(subject.datagrid_rows(grid, [entry])).to match_css_pattern(
-          "td[data-datagrid-column=name]" => 1
+          "td[data-column=name]" => 1
         )
       end
     end
@@ -402,7 +402,7 @@
     it "should escape html" do
       entry.update!(name: "<div>hello</div>")
       expect(subject.datagrid_rows(grid, [entry], columns: [:name])).to equal_to_dom(<<-HTML)
-       <tr><td class="" data-datagrid-column="name">&lt;div&gt;hello&lt;/div&gt;</td></tr>
+       <tr><td class="" data-column="name">&lt;div&gt;hello&lt;/div&gt;</td></tr>
       HTML
     end
 
@@ -412,7 +412,7 @@
         model.name.html_safe
       end
       expect(subject.datagrid_rows(grid, [entry], columns: [:safe_name])).to equal_to_dom(<<-HTML)
-       <tr><td class="" data-datagrid-column="safe_name"><div>hello</div></td></tr>
+       <tr><td class="" data-column="safe_name"><div>hello</div></td></tr>
       HTML
     end
   end
@@ -449,7 +449,7 @@ class FormForGrid < Datagrid::Base
       expect(subject.datagrid_form_for(object, url: "/grid")).to match_css_pattern(
         "form.datagrid-form[action='/grid']" => 1,
         "form input[name=utf8]" => 1,
-        "form .datagrid-filter[data-datagrid-filter=category][data-datagrid-filter-type=string] label" => "Category",
+        "form .datagrid-filter[data-filter=category][data-type=string] label" => "Category",
         "form .datagrid-filter input[name='form_for_grid[category]'][value=hello]" => 1,
         "form .datagrid-actions input[name=commit][value=Search]" => 1,
         "form .datagrid-actions a.datagrid-reset[href='/location']" => 1
@@ -571,7 +571,7 @@ def param_name
 
     it "converts to string using columns option" do
       r = subject.datagrid_row(grid, entry, columns: [:name]).to_s
-      expect(r).to match_css_pattern("tr td[data-datagrid-column=name]")
+      expect(r).to match_css_pattern("tr td[data-column=name]")
       expect(r).to_not match_css_pattern("tr td.category")
     end
   end
@@ -628,7 +628,7 @@ def param_name
         end
       end
       expect(subject.datagrid_header(grid)).to equal_to_dom(<<~HTML)
-        <tr><th class="datagrid-order-active-asc" data-datagrid-column="category">Category<div class="datagrid-order">
+        <tr><th class="datagrid-order-active-asc" data-column="category">Category<div class="datagrid-order">
         <a class="datagrid-order-control-asc" href="/location?grid%5Bdescending%5D=false&amp;grid%5Border%5D=category">&uarr;</a><a class="datagrid-order-control-desc" href="/location?grid%5Bdescending%5D=true&amp;grid%5Border%5D=category">&darr;</a>
         </div>
         </th></tr>

From ded516d5eba7e5876aa65bc95198d97a60d7a2b1 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 10:44:33 +0100
Subject: [PATCH 049/157] Make range input classes editable in partials

---
 app/views/datagrid/_range_filter.html.erb | 4 ++--
 lib/datagrid/form_builder.rb              | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
index 1b90dc8..3b8ca85 100644
--- a/app/views/datagrid/_range_filter.html.erb
+++ b/app/views/datagrid/_range_filter.html.erb
@@ -1,3 +1,3 @@
-<%= form.datagrid_filter_input(filter, **from_options) %>
+<%= form.datagrid_filter_input(filter, class: 'datagrid-range-from', **from_options) %>
 <span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
-<%= form.datagrid_filter_input(filter, **to_options) %>
+<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options) %>
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 865ab96..e58c623 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -179,8 +179,8 @@ def dynamic_filter_select(name, variants, select_options, html_options)
 
     def datagrid_range_filter(_type, filter, options = {})
       if filter.range?
-        from_options = datagrid_range_filter_options(object, filter, :from, options)
-        to_options = datagrid_range_filter_options(object, filter, :to, options)
+        from_options = datagrid_range_filter_options(object, filter, :from, **options)
+        to_options = datagrid_range_filter_options(object, filter, :to, **options)
         render_partial "range_filter", {
           from_options: from_options, to_options: to_options, filter: filter, form: self
         }
@@ -189,9 +189,9 @@ def datagrid_range_filter(_type, filter, options = {})
       end
     end
 
-    def datagrid_range_filter_options(object, filter, type, options)
+    def datagrid_range_filter_options(object, filter, type, **options)
       type_method_map = { from: :begin, to: :end }
-      options = add_html_classes(options, "datagrid-range-#{type}")
+      # options = add_html_classes(options, "datagrid-range-#{type}")
       options[:value] = object[filter.name]&.public_send(type_method_map[type])
       options[:name] = @template.field_name(object_name, filter.name, type)
       # In case of datagrid ranged filter

From 54a9a77815dabcc9989e46e1a2ff43b7379c1c5e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 10:58:35 +0100
Subject: [PATCH 050/157] Expose datagrid-range-from/to classes in partials

---
 app/views/datagrid/_form.html.erb | 5 +----
 lib/datagrid/form_builder.rb      | 1 -
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index bd66d9f..9b41ff2 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -1,9 +1,6 @@
 <%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
   <% grid.filters.each do |filter| %>
-    <div class="datagrid-filter" 
-         data-filter="<%= filter.name %>" 
-         data-type="<%= filter.type %>"
-    >
+    <div class="datagrid-filter" data-filter="<%= filter.name %>" data-type="<%= filter.type %>">
       <%= f.datagrid_label filter %>
       <%= f.datagrid_filter filter %>
     </div>
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index e58c623..1af46df 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -191,7 +191,6 @@ def datagrid_range_filter(_type, filter, options = {})
 
     def datagrid_range_filter_options(object, filter, type, **options)
       type_method_map = { from: :begin, to: :end }
-      # options = add_html_classes(options, "datagrid-range-#{type}")
       options[:value] = object[filter.name]&.public_send(type_method_map[type])
       options[:name] = @template.field_name(object_name, filter.name, type)
       # In case of datagrid ranged filter

From 4f5fd526967d3d5d3d58e572733635cdc142617c Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 12:05:46 +0100
Subject: [PATCH 051/157] Prettify CSS

---
 app/assets/stylesheets/datagrid.css | 23 ++++++-----------------
 1 file changed, 6 insertions(+), 17 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.css b/app/assets/stylesheets/datagrid.css
index fee4ee5..5201d17 100644
--- a/app/assets/stylesheets/datagrid.css
+++ b/app/assets/stylesheets/datagrid.css
@@ -7,6 +7,7 @@ table.datagrid-table {
 table.datagrid-table th {
   background-color: #eee;
   text-align: left;
+  vertical-align: top;
 }
 
 table.datagrid-table td,
@@ -61,10 +62,6 @@ table.datagrid-table th {
   float: left;
 }
 
-.datagrid-filter a {
-  float: left;
-}
-
 .datagrid-filter input {
   border: 2px solid #ccc;
   border-radius: 4px;
@@ -73,9 +70,8 @@ table.datagrid-table th {
   width: 300px;
 }
 
-.datagrid-filter input.datagrid-range-from,
-.datagrid-filter input.datagrid-range-to {
-  width: 83px;
+input.datagrid-range-from, input.datagrid-range-to {
+  width: 144px;
 }
 
 .datagrid-filter select {
@@ -89,21 +85,17 @@ table.datagrid-table th {
   height: 100px;
 }
 
-.datagrid-filter .datagrid-dynamic-field {
-  width: 178px;
-}
-
-.datagrid-filter .datagrid-dynamic-operation {
+select.datagrid-dynamic-operation {
   margin-left: 7px;
   width: 50px;
 }
 
-.datagrid-filter .datagrid-dynamic-value {
+.datagrid-dynamic-value {
   margin: 10px 0 0 150px;
   clear: both;
 }
 
-.datagrid-filter .datagrid-range-separator {
+.datagrid-range-separator {
   float: left;
   margin: 6px 4px 0;
 }
@@ -118,9 +110,6 @@ table.datagrid-table th {
   width: 100%;
 }
 
-input.datagrid-range-from, input.datagrid-range-to {
-  width: 144px !important
-}
 
 select.datagrid-dynamic-field {
   width: 242px

From f19c191ee4e58a41052fb2e48bee28777b93631c Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 12:09:42 +0100
Subject: [PATCH 052/157] Prettify CSS

---
 app/assets/stylesheets/datagrid.css | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.css b/app/assets/stylesheets/datagrid.css
index 5201d17..7083c37 100644
--- a/app/assets/stylesheets/datagrid.css
+++ b/app/assets/stylesheets/datagrid.css
@@ -1,3 +1,5 @@
+/* Table */
+
 table.datagrid-table {
   background-color: transparent;
   border-collapse: collapse;
@@ -37,6 +39,8 @@ table.datagrid-table th {
   text-align: center;
 }
 
+/* Form */
+
 .datagrid-form {
   background-color: #f0f0f0;
   border-radius: 5px;
@@ -104,13 +108,6 @@ select.datagrid-dynamic-operation {
   float: left;
 }
 
-.datagrid-enum-checkboxes label {
-  display: block;
-  float: none;
-  width: 100%;
-}
-
-
 select.datagrid-dynamic-field {
   width: 242px
 }
@@ -121,11 +118,17 @@ select.datagrid-dynamic-field {
   width: auto;
 }
 
+.datagrid-enum-checkboxes label {
+  display: block;
+  float: none;
+  width: 100%;
+}
+
 .datagrid-actions {
   padding-left: calc(150px + 10px);
 }
 
-.datagrid-actions input[type='submit'] {
+.datagrid-submit {
   background-color: #555;
   border: none;
   border-radius: 5px;
@@ -141,16 +144,16 @@ select.datagrid-dynamic-field {
   *display: inline;
 }
 
-.datagrid-actions input[type='submit']:hover,
-.datagrid-actions input[type='submit']:focus {
+.datagrid-submit:hover,
+.datagrid-submit:focus {
   background-color: #333;
 }
 
-.datagrid-actions input[type='submit']:active {
+.datagrid-submit:active {
   background-color: #000;
 }
 
-.datagrid-actions > a {
+.datagrid-reset {
   font-size: 14px;
   padding: 7px 15px;
   vertical-align: middle;

From 47369af8ff1ff84092806e952c2cd0da3a6ae6f2 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 12:23:15 +0100
Subject: [PATCH 053/157] Fully support pagy in scaffold generator

---
 lib/datagrid/scaffold.rb | 37 +++++++++++++++++++++++++++++++++----
 templates/index.html.erb |  4 ++--
 2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index b016c57..a14b381 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -21,7 +21,7 @@ def create_scaffold
       end
       template "index.html.erb", view_file
       route(generate_routing_namespace("resources :#{grid_controller_short_name}"))
-      gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate)
+      gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate) || defined?(::Pagy)
       in_root do
         {
           "css" => " *= require datagrid",
@@ -71,12 +71,22 @@ def grid_param_name
     def pagination_helper_code
       if defined?(::WillPaginate)
         "will_paginate(@grid.assets)"
+      elsif defined?(::Pagy)
+         "pagy_nav(@pagy)"
       else
         # Kaminari is default
         "paginate(@grid.assets)"
       end
     end
 
+    def table_helper_code
+      if defined?(::Pagy)
+        "datagrid_table @grid, @records"
+      else
+        "datagrid_table @grid"
+      end
+    end
+
     def base_grid_file
       "app/grids/application_grid.rb"
     end
@@ -88,9 +98,7 @@ def grid_route_name
     def index_action
       indent(<<~RUBY)
         def index
-          @grid = #{grid_class_name}.new(grid_params) do |scope|
-            scope.page(params[:page])
-          end
+#{index_body}
         end
 
         protected
@@ -101,6 +109,27 @@ def grid_params
       RUBY
     end
 
+    def index_body
+
+      if defined?(::Pagy)
+        <<~RUBY
+        def index
+          @grid = #{grid_class_name}.new(grid_params)
+          @pagy, @assets = pagy(@grid.assets)
+        end
+        RUBY
+      else
+        <<~RUBY
+        def index
+          @grid = #{grid_class_name}.new(grid_params) do |scope|
+            scope.page(params[:page])
+          end
+        end
+        RUBY
+      end
+
+    end
+
     protected
 
     def generate_routing_namespace(code)
diff --git a/templates/index.html.erb b/templates/index.html.erb
index 9ef276d..105b49f 100644
--- a/templates/index.html.erb
+++ b/templates/index.html.erb
@@ -1,5 +1,5 @@
-<%%= datagrid_form_for @grid, method: :get, url: <%= grid_route_name %> %>
+<%%= datagrid_form_for @grid, url: <%= grid_route_name %> %>
 
 <%%= <%=pagination_helper_code%> %>
-<%%= datagrid_table @grid %>
+<%%= <%=table_helper_code%> %>
 <%%= <%=pagination_helper_code%> %>

From 83d875f78c5c61679d29c6a30414b8fdb9baf792 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 13:29:17 +0100
Subject: [PATCH 054/157] Improve scaffold

---
 lib/datagrid/filters.rb        |  4 ++-
 lib/datagrid/scaffold.rb       | 66 ++++++++++++++++++----------------
 spec/datagrid/scaffold_spec.rb |  8 +++--
 templates/base.rb.erb          |  6 ++++
 4 files changed, 50 insertions(+), 34 deletions(-)

diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index b573e9f..de15078 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -144,7 +144,9 @@ def filters_inspection
     def initialize(...)
       self.filters_array = self.class.filters_array.clone
       filters_array.each do |filter|
-        self[filter.name] = filter.default(self)
+        if value = filter.default(self)
+          self[filter.name] = value
+        end
       end
       super
     end
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index a14b381..9fae2a0 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -9,7 +9,7 @@ class Scaffold < Rails::Generators::NamedBase
     include Rails::Generators::ResourceHelpers
 
     check_class_collision suffix: "Grid"
-    source_root File.expand_path("/Users/bogdan/makabu/my/datagrid/lib/datagrid/scaffold.rb/../../../templates")
+    source_root File.expand_path("#{__FILE__}/../../../templates")
 
     def create_scaffold
       template "base.rb.erb", base_grid_file unless file_exists?(base_grid_file)
@@ -17,9 +17,9 @@ def create_scaffold
       if file_exists?(grid_controller_file)
         inject_into_file grid_controller_file, index_action, after: /class .*#{grid_controller_class_name}.*\n/
       else
-        template "controller.rb.erb", grid_controller_file
+        create_file grid_controller_file, controller_code
       end
-      template "index.html.erb", view_file
+      create_file view_file, view_code
       route(generate_routing_namespace("resources :#{grid_controller_short_name}"))
       gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate) || defined?(::Pagy)
       in_root do
@@ -95,39 +95,45 @@ def grid_route_name
       "#{controller_class_name.underscore.gsub('/', '_')}_path"
     end
 
-    def index_action
-      indent(<<~RUBY)
-        def index
-#{index_body}
-        end
-
-        protected
-
-        def grid_params
-          params.fetch(:#{grid_param_name}, {}).permit!
-        end
-      RUBY
-    end
-
-    def index_body
-
+    def index_code
       if defined?(::Pagy)
-        <<~RUBY
-        def index
-          @grid = #{grid_class_name}.new(grid_params)
-          @pagy, @assets = pagy(@grid.assets)
-        end
+        <<-RUBY
+    @grid = #{grid_class_name}.new(grid_params)
+    @pagy, @assets = pagy(@grid.assets)
         RUBY
       else
-        <<~RUBY
-        def index
-          @grid = #{grid_class_name}.new(grid_params) do |scope|
-            scope.page(params[:page])
-          end
-        end
+        <<-RUBY
+    @grid = #{grid_class_name}.new(grid_params) do |scope|
+      scope.page(params[:page])
+    end
         RUBY
       end
+    end
+
+    def controller_code
+      <<~RUBY
+class #{grid_controller_class_name} < ApplicationController
+  def index
+#{index_code.rstrip}
+  end
+
+  protected
+
+  def grid_params
+    params.fetch(:#{grid_param_name}, {}).permit!
+  end
+end
+RUBY
+    end
+
+    def view_code
+      indent(<<~ERB)
+<%= datagrid_form_for @grid, url: #{grid_route_name} %>
 
+<%= #{pagination_helper_code} %>
+<%= #{table_helper_code} %>
+<%= #{pagination_helper_code} %>
+ERB
     end
 
     protected
diff --git a/spec/datagrid/scaffold_spec.rb b/spec/datagrid/scaffold_spec.rb
index 6c1bd16..3e37561 100644
--- a/spec/datagrid/scaffold_spec.rb
+++ b/spec/datagrid/scaffold_spec.rb
@@ -24,9 +24,10 @@
     end
   end
 
-  describe ".index_action" do
+  describe ".controller_code" do
     it "works" do
-      expect(subject.index_action).to eq(<<-RUBY)
+      expect(subject.controller_code).to eq(<<-RUBY)
+class UsersController < ApplicationController
   def index
     @grid = UsersGrid.new(grid_params) do |scope|
       scope.page(params[:page])
@@ -38,7 +39,8 @@ def index
   def grid_params
     params.fetch(:users_grid, {}).permit!
   end
-      RUBY
+end
+RUBY
     end
   end
 end
diff --git a/templates/base.rb.erb b/templates/base.rb.erb
index c5e5801..f580d0c 100644
--- a/templates/base.rb.erb
+++ b/templates/base.rb.erb
@@ -38,4 +38,10 @@ class ApplicationGrid < Datagrid::Base
       value ? "Yes" : "No"
     end
   end
+
+  # Uncomment to shorten URL query string for all grids.
+  # May cause collisions if multiple grids are used on the same page.
+  # def param_name
+  #   'grid'
+  # end
 end

From 755c21cbebec2a027b793813c2eacdefa7d89e8d Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 14:11:30 +0100
Subject: [PATCH 055/157] Fix dynamic filter when assigned a hash with string
 keys

---
 lib/datagrid/filters/dynamic_filter.rb       | 93 +++++++++++---------
 spec/datagrid/filters/dynamic_filter_spec.rb | 24 +++--
 2 files changed, 71 insertions(+), 46 deletions(-)

diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index 77c6507..febb8f6 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -31,12 +31,7 @@ def initialize(*)
       end
 
       def parse_values(filter)
-
-        if filter.is_a?(Array)
-          field, operation, value = filter
-          filter = { field:, operation:, value: type_cast(field, value)}
-        end
-        filter ? FilterValue.new(filter) : nil
+        filter ? FilterValue.new(grid_class, filter) : nil
       end
 
       def unapplicable_value?(filter)
@@ -101,48 +96,39 @@ def default_select
         }
       end
 
-      def type_cast(field, value)
-        type = column_type(field)
-        return nil if value.blank?
-
-        case type
-        when :string
-          value.to_s
-        when :integer
-          value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_i : nil
-        when :float
-          value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_f : nil
-        when :date, :timestamp
-          Datagrid::Utils.parse_date(value)
-        when :boolean
-          Datagrid::Utils.booleanize(value)
-        when nil
-          value
-        else
-          raise NotImplementedError, "unknown column type: #{type.inspect}"
-        end
-      end
-
       def column_type(field)
         grid_class.driver.normalized_column_type(grid_class.scope, field)
       end
 
-      class FilterValue < Hash
-        def initialize(object = nil)
-          super()
-          update(object) if object
-        end
+      class FilterValue
+        attr_accessor :field, :operation, :value
 
-        def field
-          self[:field]
-        end
+        def initialize(grid_class, object = nil)
+          super()
 
-        def operation
-          self[:operation]
+          case object
+          when Hash
+            object = object.symbolize_keys
+            self.field = object[:field]
+            self.operation = object[:operation]
+            self.value = object[:value]
+          when Array
+            self.field = object[0]
+            self.operation = object[1]
+            self.value = object[2]
+          else
+            raise ArgumentError, object.inspect
+          end
+          if grid_class
+            type = grid_class.driver.normalized_column_type(
+              grid_class.scope, field
+            )
+            self.value = type_cast(type, value)
+          end
         end
 
-        def value
-          self[:value]
+        def inspect
+          {field: field, operation: operation, value: value}
         end
 
         def to_ary
@@ -152,6 +138,33 @@ def to_ary
         def to_a
           [field, operation, value]
         end
+
+        def to_h
+          {field: field, operation: operation, value: value}
+        end
+
+        protected
+
+        def type_cast(type, value)
+          return nil if value.blank?
+
+          case type
+          when :string
+            value.to_s
+          when :integer
+            value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_i : nil
+          when :float
+            value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_f : nil
+          when :date, :timestamp
+            Datagrid::Utils.parse_date(value)
+          when :boolean
+            Datagrid::Utils.booleanize(value)
+          when nil
+            value
+          else
+            raise NotImplementedError, "unknown column type: #{type.inspect}"
+          end
+        end
       end
     end
   end
diff --git a/spec/datagrid/filters/dynamic_filter_spec.rb b/spec/datagrid/filters/dynamic_filter_spec.rb
index 0c4a6cb..f373f08 100644
--- a/spec/datagrid/filters/dynamic_filter_spec.rb
+++ b/spec/datagrid/filters/dynamic_filter_spec.rb
@@ -62,26 +62,26 @@
 
   it "should nullify incorrect value for integer" do
     report.condition = [:group_id, "<=", "aa"]
-    expect(report.condition).to eq(
+    expect(report.condition.to_h).to eq(
       {field: :group_id, operation: "<=", value: nil}
     )
   end
 
   it "should nullify incorrect value for date" do
     report.condition = [:shipping_date, "<=", "aa"]
-    expect(report.condition).to eq({
+    expect(report.condition.to_h).to eq({
       field: :shipping_date, operation: "<=", value: nil
     })
   end
 
   it "should nullify incorrect value for datetime" do
     report.condition = [:created_at, "<=", "aa"]
-    expect(report.condition).to eq({field: :created_at, operation: "<=", value: nil})
+    expect(report.condition.to_h).to eq({field: :created_at, operation: "<=", value: nil})
   end
 
   it "should support date comparation operation by timestamp column" do
     report.condition = [:created_at, "<=", "1986-08-05"]
-    expect(report.condition).to eq({field: :created_at, operation: "<=", value: Date.parse("1986-08-05")})
+    expect(report.condition.to_h).to eq({field: :created_at, operation: "<=", value: Date.parse("1986-08-05")})
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-04 01:01:01")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:00")))
@@ -91,7 +91,7 @@
 
   it "should support date = operation by timestamp column" do
     report.condition = [:created_at, "=", "1986-08-05"]
-    expect(report.condition).to eq({field: :created_at, operation: "=", value: Date.parse("1986-08-05")})
+    expect(report.condition.to_h).to eq({field: :created_at, operation: "=", value: Date.parse("1986-08-05")})
     expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-04 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:01")))
@@ -102,7 +102,7 @@
 
   it "should support date =~ operation by timestamp column" do
     report.condition = [:created_at, "=~", "1986-08-05"]
-    expect(report.condition).to eq({field: :created_at, operation: "=~", value: Date.parse("1986-08-05")})
+    expect(report.condition.to_h).to eq({field: :created_at, operation: "=~", value: Date.parse("1986-08-05")})
     expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-04 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:01")))
@@ -179,4 +179,16 @@
       report.assets
     end.to raise_error(Datagrid::FilteringError)
   end
+
+  it "supports assignment of string keys hash" do
+    report.condition =  {
+      field: "shipping_date",
+      operation: "<>",
+      value: "1996-08-05",
+    }.stringify_keys
+
+    expect(report.condition.to_h).to eq({
+      field: 'shipping_date', operation: '<>', value: Date.parse('1996-08-05')
+    })
+  end
 end

From ed1ad9a1920aad6b213c06ae94bf75860f068233 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 8 Nov 2024 19:10:38 +0100
Subject: [PATCH 056/157] Extract views diff to separated file

---
 VERSION2.md => version-2/Readme.markdown | 126 +----------------------
 version-2/views.diff                     | 120 +++++++++++++++++++++
 2 files changed, 122 insertions(+), 124 deletions(-)
 rename VERSION2.md => version-2/Readme.markdown (63%)
 create mode 100644 version-2/views.diff

diff --git a/VERSION2.md b/version-2/Readme.markdown
similarity index 63%
rename from VERSION2.md
rename to version-2/Readme.markdown
index 326c041..d9522fe 100644
--- a/VERSION2.md
+++ b/version-2/Readme.markdown
@@ -343,127 +343,5 @@ end
 Version 2 built-in partials are trying to expose
 as much UI as possible for user modification.
 
-Here is a complete diff for built-in partials between V1 and V2:
-
-TODO update
-
-``` diff
-diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
-index 9f48319..f114c17 100644
---- a/app/views/datagrid/_enum_checkboxes.html.erb
-+++ b/app/views/datagrid/_enum_checkboxes.html.erb
-@@ -4,8 +4,8 @@ You can add indent if whitespace doesn't matter for you
- %>
- <%- elements.each do |value, text, checked| -%>
- <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
--<%= form.label filter.name, options.merge(for: id) do -%>
--<%= form.check_box(filter.name, {multiple: true, id: id, checked: checked, include_hidden: false}, value.to_s, nil) -%>
-+<%= form.datagrid_label(filter.name, **options, for: id) do -%>
-+<%= form.datagrid_filter_input(filter.name, type: :checkbox, multiple: true, id: id, checked: checked, include_hidden: false, value: value.to_s) -%>
- <%= text -%>
- <%- end -%>
- <%- end -%>
-diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
-index 7e175c1..88a39f9 100644
---- a/app/views/datagrid/_form.html.erb
-+++ b/app/views/datagrid/_form.html.erb
-@@ -1,12 +1,16 @@
--<%= form_for grid, options do |f| -%>
-+<%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
-   <% grid.filters.each do |filter| %>
--    <div class="datagrid-filter filter">
-+    <div class="datagrid-filter"
-+         data-datagrid-filter="<%= filter.name %>"
-+         data-type="<%= filter.type %>"
-+         data-datagrid-filter-checkboxes="<%= filter.enum_checkboxes? %>"
-+    >
-       <%= f.datagrid_label filter %>
-       <%= f.datagrid_filter filter %>
-     </div>
-   <% end %>
-   <div class="datagrid-actions">
--    <%= f.submit I18n.t("datagrid.form.search").html_safe, class: "datagrid-submit" %>
--    <%= link_to I18n.t('datagrid.form.reset').html_safe, url_for(grid.to_param => {}), class: "datagrid-reset" %>
-+    <%= f.submit I18n.t("datagrid.form.search"), class: "datagrid-submit" %>
-+    <%= link_to I18n.t('datagrid.form.reset'), url_for(grid.to_param => {}), class: "datagrid-reset" %>
-   </div>
- <% end -%>
-diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
-index e939128..affccf4 100644
---- a/app/views/datagrid/_head.html.erb
-+++ b/app/views/datagrid/_head.html.erb
-@@ -1,6 +1,6 @@
- <tr>
-   <% grid.html_columns(*options[:columns]).each do |column| %>
--    <th class="<%= datagrid_column_classes(grid, column) %>">
-+    <th class="<%= datagrid_column_classes(grid, column) %>" data-datagrid-column="<%= column.name %>">
-       <%= column.header %>
-       <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
-     </th>
-diff --git a/app/views/datagrid/_order_for.html.erb b/app/views/datagrid/_order_for.html.erb
-index 1545a8e..1c33c37 100644
---- a/app/views/datagrid/_order_for.html.erb
-+++ b/app/views/datagrid/_order_for.html.erb
-@@ -1,10 +1,10 @@
--<div class="order">
-+<div class="datagrid-order">
-   <%= link_to(
--      I18n.t("datagrid.table.order.asc").html_safe,
-+      I18n.t("datagrid.table.order.asc"),
-       datagrid_order_path(grid, column, false),
--      class: "asc") %>
-+      class: "datagrid-order-control-asc") %>
-   <%= link_to(
--      I18n.t("datagrid.table.order.desc").html_safe,
-+      I18n.t("datagrid.table.order.desc"),
-       datagrid_order_path(grid, column, true),
--      class: "desc") %>
-+      class: "datagrid-order-control-desc") %>
- </div>
-diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
-index 7a8a123..1b90dc8 100644
---- a/app/views/datagrid/_range_filter.html.erb
-+++ b/app/views/datagrid/_range_filter.html.erb
-@@ -1,3 +1,3 @@
- <%= form.datagrid_filter_input(filter, **from_options) %>
--<span class="separator <%= filter.type %>"><%= I18n.t('datagrid.filters.range.separator') %></span>
-+<span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
- <%= form.datagrid_filter_input(filter, **to_options) %>
-diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
-index f54d21c..b431ab7 100644
---- a/app/views/datagrid/_row.html.erb
-+++ b/app/views/datagrid/_row.html.erb
-@@ -1,5 +1,5 @@
- <tr>
-   <% grid.html_columns(*options[:columns]).each do |column| %>
--    <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_value(grid, column, asset) %></td>
-+    <td class="<%= datagrid_column_classes(grid, column) %>" data-datagrid-column="<%= column.name %>"><%= datagrid_value(grid, column, asset) %></td>
-   <% end %>
- </tr>
-diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
-index 8708c05..0b5ff24 100644
---- a/app/views/datagrid/_table.html.erb
-+++ b/app/views/datagrid/_table.html.erb
-@@ -5,7 +5,7 @@ Local variables:
- * options - passed options Hash
- %>
- <% if grid.html_columns(*options[:columns]).any? %>
--  <%= content_tag :table, options[:html] do %>
-+  <%= content_tag :table, class: 'datagrid-table', **options.fetch(:html, {}) do %>
-     <thead>
-       <%= datagrid_header(grid, options) %>
-     </thead>
-@@ -13,10 +13,10 @@ Local variables:
-       <% if assets.any? %>
-         <%= datagrid_rows(grid, assets, **options) %>
-       <% else %>
--        <tr><td class="noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
-+        <tr><td class="datagrid-no-results" colspan="100%"><%= I18n.t('datagrid.no_results') %></td></tr>
-       <% end %>
-     </tbody>
-   <% end %>
- <% else -%>
--  <%= I18n.t("datagrid.table.no_columns").html_safe %>
-+  <%= I18n.t("datagrid.table.no_columns") %>
- <% end %>
-```
+Here is a complete [diff for built-in partials between V1 and V2](./views.diff)
+
diff --git a/version-2/views.diff b/version-2/views.diff
new file mode 100644
index 0000000..ed70538
--- /dev/null
+++ b/version-2/views.diff
@@ -0,0 +1,120 @@
+diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
+index 9f48319..2d46139 100644
+--- a/app/views/datagrid/_enum_checkboxes.html.erb
++++ b/app/views/datagrid/_enum_checkboxes.html.erb
+@@ -2,10 +2,12 @@
+ Indent in this file may cause extra space to appear.
+ You can add indent if whitespace doesn't matter for you
+ %>
++<div class="datagrid-enum-checkboxes">
+ <%- elements.each do |value, text, checked| -%>
+ <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
+-<%= form.label filter.name, options.merge(for: id) do -%>
+-<%= form.check_box(filter.name, {multiple: true, id: id, checked: checked, include_hidden: false}, value.to_s, nil) -%>
++<%= form.datagrid_label(filter.name, **options, for: id, class: 'datagrid-enum-checkbox-label') do -%>
++<%= form.datagrid_filter_input(filter.name, type: :checkbox, multiple: true, id: id, checked: checked, include_hidden: false, value: value.to_s) -%>
+ <%= text -%>
+ <%- end -%>
+ <%- end -%>
++</div>
+diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
+index 7e175c1..9b41ff2 100644
+--- a/app/views/datagrid/_form.html.erb
++++ b/app/views/datagrid/_form.html.erb
+@@ -1,12 +1,12 @@
+-<%= form_for grid, options do |f| -%>
++<%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
+   <% grid.filters.each do |filter| %>
+-    <div class="datagrid-filter filter">
++    <div class="datagrid-filter" data-filter="<%= filter.name %>" data-type="<%= filter.type %>">
+       <%= f.datagrid_label filter %>
+       <%= f.datagrid_filter filter %>
+     </div>
+   <% end %>
+   <div class="datagrid-actions">
+-    <%= f.submit I18n.t("datagrid.form.search").html_safe, class: "datagrid-submit" %>
+-    <%= link_to I18n.t('datagrid.form.reset').html_safe, url_for(grid.to_param => {}), class: "datagrid-reset" %>
++    <%= f.submit I18n.t("datagrid.form.search"), class: "datagrid-submit" %>
++    <%= link_to I18n.t('datagrid.form.reset'), url_for(grid.to_param => {}), class: "datagrid-reset" %>
+   </div>
+ <% end -%>
+diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
+index e939128..c3264bb 100644
+--- a/app/views/datagrid/_head.html.erb
++++ b/app/views/datagrid/_head.html.erb
+@@ -1,6 +1,6 @@
+ <tr>
+   <% grid.html_columns(*options[:columns]).each do |column| %>
+-    <th class="<%= datagrid_column_classes(grid, column) %>">
++    <th class="<%= datagrid_column_classes(grid, column) %>" data-column="<%= column.name %>">
+       <%= column.header %>
+       <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
+     </th>
+diff --git a/app/views/datagrid/_order_for.html.erb b/app/views/datagrid/_order_for.html.erb
+index 1545a8e..1c33c37 100644
+--- a/app/views/datagrid/_order_for.html.erb
++++ b/app/views/datagrid/_order_for.html.erb
+@@ -1,10 +1,10 @@
+-<div class="order">
++<div class="datagrid-order">
+   <%= link_to(
+-      I18n.t("datagrid.table.order.asc").html_safe,
++      I18n.t("datagrid.table.order.asc"),
+       datagrid_order_path(grid, column, false),
+-      class: "asc") %>
++      class: "datagrid-order-control-asc") %>
+   <%= link_to(
+-      I18n.t("datagrid.table.order.desc").html_safe,
++      I18n.t("datagrid.table.order.desc"),
+       datagrid_order_path(grid, column, true),
+-      class: "desc") %>
++      class: "datagrid-order-control-desc") %>
+ </div>
+diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
+index 7a8a123..3b8ca85 100644
+--- a/app/views/datagrid/_range_filter.html.erb
++++ b/app/views/datagrid/_range_filter.html.erb
+@@ -1,3 +1,3 @@
+-<%= form.datagrid_filter_input(filter, **from_options) %>
+-<span class="separator <%= filter.type %>"><%= I18n.t('datagrid.filters.range.separator') %></span>
+-<%= form.datagrid_filter_input(filter, **to_options) %>
++<%= form.datagrid_filter_input(filter, class: 'datagrid-range-from', **from_options) %>
++<span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
++<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options) %>
+diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
+index f54d21c..20f3ffb 100644
+--- a/app/views/datagrid/_row.html.erb
++++ b/app/views/datagrid/_row.html.erb
+@@ -1,5 +1,5 @@
+ <tr>
+   <% grid.html_columns(*options[:columns]).each do |column| %>
+-    <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_value(grid, column, asset) %></td>
++    <td class="<%= datagrid_column_classes(grid, column) %>" data-column="<%= column.name %>"><%= datagrid_value(grid, column, asset) %></td>
+   <% end %>
+ </tr>
+diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
+index 8708c05..0b5ff24 100644
+--- a/app/views/datagrid/_table.html.erb
++++ b/app/views/datagrid/_table.html.erb
+@@ -5,7 +5,7 @@ Local variables:
+ * options - passed options Hash
+ %>
+ <% if grid.html_columns(*options[:columns]).any? %>
+-  <%= content_tag :table, options[:html] do %>
++  <%= content_tag :table, class: 'datagrid-table', **options.fetch(:html, {}) do %>
+     <thead>
+       <%= datagrid_header(grid, options) %>
+     </thead>
+@@ -13,10 +13,10 @@ Local variables:
+       <% if assets.any? %>
+         <%= datagrid_rows(grid, assets, **options) %>
+       <% else %>
+-        <tr><td class="noresults" colspan="100%"><%= I18n.t('datagrid.no_results').html_safe %></td></tr>
++        <tr><td class="datagrid-no-results" colspan="100%"><%= I18n.t('datagrid.no_results') %></td></tr>
+       <% end %>
+     </tbody>
+   <% end %>
+ <% else -%>
+-  <%= I18n.t("datagrid.table.no_columns").html_safe %>
++  <%= I18n.t("datagrid.table.no_columns") %>
+ <% end %>

From 1a0fcbd7bcb36bad1de25268bac1cc88eac5a543 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 9 Nov 2024 10:05:26 +0100
Subject: [PATCH 057/157] Form versions

---
 version-2/form-v1.html | 26 ++++++++++++++++++++++++++
 version-2/form-v2.html | 27 +++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)
 create mode 100644 version-2/form-v1.html
 create mode 100644 version-2/form-v2.html

diff --git a/version-2/form-v1.html b/version-2/form-v1.html
new file mode 100644
index 0000000..5e8241a
--- /dev/null
+++ b/version-2/form-v1.html
@@ -0,0 +1,26 @@
+<form class="datagrid-form partial_default_grid" id="new_g" action="/users" accept-charset="UTF-8" method="get">
+  <input name="utf8" type="hidden" value="✓" autocomplete="off" />
+
+  <div class="datagrid-filter filter">
+    <label for="g_id">Id</label>
+    <input class="id integer_filter from" multiple type="text" name="g[id][]" />
+    <span class="separator integer"> - </span>
+    <input class="id integer_filter to" multiple type="text" name="g[id][]" />
+  </div>
+
+  <div class="datagrid-filter filter">
+    <label for="g_group_id">Group</label>
+    <label class="group_id enum_filter checkboxes" for="g_group_id_1">
+      <input id="g_group_id_1" type="checkbox" value="1" name="g[group_id][]" />1
+    </label>
+    <label class="group_id enum_filter checkboxes" for="g_group_id_2">
+      <input id="g_group_id_2" type="checkbox" value="2" name="g[group_id][]" />2
+    </label>
+  </div>
+
+  <div class="datagrid-actions">
+    <input type="submit" name="commit" value="Search"
+        class="datagrid-submit" data-disable-with="Search" />
+    <a class="datagrid-reset" href="/location">Reset</a>
+  </div>
+</form>
diff --git a/version-2/form-v2.html b/version-2/form-v2.html
new file mode 100644
index 0000000..03a40a3
--- /dev/null
+++ b/version-2/form-v2.html
@@ -0,0 +1,27 @@
+<form class="datagrid-form" id="new_g" action="/users" accept-charset="UTF-8" method="get">
+  <input name="utf8" type="hidden" value="✓" autocomplete="off" />
+
+  <div class="datagrid-filter" data-filter="id" data-type="integer">
+    <label for="g_id">Id</label>
+    <input step="1" class="datagrid-range-from" name="g[id][from]" type="number" />
+    <span class="datagrid-range-separator"> - </span>
+    <input step="1" class="datagrid-range-to" name="g[id][to]" type="number" />
+  </div>
+
+  <div class="datagrid-filter" data-filter="group_id" data-type="enum">
+    <label for="g_group_id">Group</label>
+    <div class="datagrid-enum-checkboxes">
+      <label class="datagrid-enum-checkbox-label" for="g_group_id_1">
+        <input id="g_group_id_1" value="1" type="checkbox" name="g[group_id][]" />1
+      </label>
+      <label class="datagrid-enum-checkbox-label" for="g_group_id_2">
+        <input id="g_group_id_2" type="checkbox" value="2" name="g[group_id][]" />2
+      </label>
+    </div>
+  </div>
+  <div class="datagrid-actions">
+    <input type="submit" name="commit" value="Search" 
+        class="datagrid-submit" data-disable-with="Search" />
+    <a class="datagrid-reset" href="/location">Reset</a>
+  </div>
+</form>

From c4414258dbc166d171ebb25c4be292e4d3c55a42 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 9 Nov 2024 10:05:39 +0100
Subject: [PATCH 058/157] Check order with direction

---
 lib/datagrid/ordering.rb | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/lib/datagrid/ordering.rb b/lib/datagrid/ordering.rb
index 963d7c4..695a812 100644
--- a/lib/datagrid/ordering.rb
+++ b/lib/datagrid/ordering.rb
@@ -52,9 +52,11 @@ def order_column
     end
 
     # @param column [String, Datagrid::Columns::Column]
+    # @param desc [nil, Boolean] confirm order direction as well if specified
     # @return [Boolean] true if given grid is ordered by given column.
-    def ordered_by?(column)
-      order_column == column_by_name(column)
+    def ordered_by?(column, desc = nil)
+      order_column == column_by_name(column) &&
+        (desc.nil? || (desc ? descending? : !descending?))
     end
 
     private

From f1ee16160faa319126adc34f1fe35c177a3989f0 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 9 Nov 2024 11:41:54 +0100
Subject: [PATCH 059/157] Lint

---
 .rubocop.yml                                  |  15 ++
 datagrid.gemspec                              |   6 +-
 lib/datagrid.rb                               |   3 +-
 lib/datagrid/base.rb                          |   2 +
 lib/datagrid/column_names_attribute.rb        |   2 +-
 lib/datagrid/columns.rb                       |  14 +-
 lib/datagrid/drivers/active_record.rb         |   2 +-
 lib/datagrid/drivers/mongoid.rb               |   2 +-
 lib/datagrid/drivers/sequel.rb                |   2 +-
 lib/datagrid/filters.rb                       |   6 +-
 lib/datagrid/filters/base_filter.rb           |   6 +-
 lib/datagrid/filters/boolean_filter.rb        |   2 +-
 lib/datagrid/filters/date_filter.rb           |   2 +-
 lib/datagrid/filters/date_time_filter.rb      |   2 +-
 lib/datagrid/filters/dynamic_filter.rb        |  28 ++--
 lib/datagrid/filters/enum_filter.rb           |   2 +-
 .../filters/extended_boolean_filter.rb        |   2 +-
 lib/datagrid/filters/float_filter.rb          |   2 +-
 lib/datagrid/filters/integer_filter.rb        |   2 +-
 lib/datagrid/filters/ranged_filter.rb         |  19 ++-
 lib/datagrid/form_builder.rb                  |  28 ++--
 lib/datagrid/ordering.rb                      |   2 +-
 lib/datagrid/renderer.rb                      |  28 ++--
 lib/datagrid/scaffold.rb                      |  40 +++---
 spec/datagrid/columns_spec.rb                 |  24 ++--
 spec/datagrid/core_spec.rb                    |   6 +-
 spec/datagrid/drivers/active_record_spec.rb   |  10 +-
 spec/datagrid/drivers/mongo_mapper_spec.rb    |   6 +-
 spec/datagrid/drivers/mongoid_spec.rb         |   6 +-
 spec/datagrid/drivers/sequel_spec.rb          |   6 +-
 spec/datagrid/filters/date_filter_spec.rb     |  10 +-
 spec/datagrid/filters/dynamic_filter_spec.rb  |  20 +--
 spec/datagrid/filters/integer_filter_spec.rb  |   2 +-
 spec/datagrid/filters_spec.rb                 |   5 +-
 spec/datagrid/form_builder_spec.rb            | 134 +++++++++---------
 spec/datagrid/helper_spec.rb                  | 113 +++++++--------
 spec/datagrid/scaffold_spec.rb                |  30 ++--
 spec/datagrid_spec.rb                         |   4 +-
 spec/spec_helper.rb                           |  40 +++---
 spec/support/mongoid.rb                       |   1 -
 40 files changed, 323 insertions(+), 313 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index 4421b7b..8ff7392 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -2,6 +2,21 @@ AllCops:
   NewCops: enable
 Style/StringLiterals:
   EnforcedStyle: double_quotes
+Style/TrailingCommaInArguments:
+  EnforcedStyleForMultiline: consistent_comma
+Style/TrailingCommaInArrayLiteral:
+  EnforcedStyleForMultiline: consistent_comma
+Style/TrailingCommaInHashLiteral:
+  EnforcedStyleForMultiline: consistent_comma
+Style/RegexpLiteral:
+  EnforcedStyle: percent_r
+Layout/FirstHashElementIndentation:
+  EnforcedStyle: consistent
+Layout/FirstArrayElementIndentation:
+  EnforcedStyle: consistent
+Layout/ArgumentAlignment:
+  EnforcedStyle: with_fixed_indentation
+
 Metrics/BlockLength:
   Enabled: false
 Metrics/MethodLength:
diff --git a/datagrid.gemspec b/datagrid.gemspec
index 49add80..a5e48e3 100644
--- a/datagrid.gemspec
+++ b/datagrid.gemspec
@@ -12,13 +12,13 @@ Gem::Specification.new do |s|
   s.email = "agresso@gmail.com"
   s.extra_rdoc_files = [
     "LICENSE.txt",
-    "Readme.markdown"
+    "Readme.markdown",
   ]
   s.files = [
     "LICENSE.txt",
     "CHANGELOG.md",
     "Readme.markdown",
-    "datagrid.gemspec"
+    "datagrid.gemspec",
   ]
   s.files += `git ls-files | grep -E '^(app|lib|templates)'`.split("\n")
   s.homepage = "https://github.com/bogdan/datagrid"
@@ -30,7 +30,7 @@ Gem::Specification.new do |s|
     "documentation_uri" => "#{s.homepage}/wiki",
     "changelog_uri" => "#{s.homepage}/blob/master/CHANGELOG.md",
     "source_code_uri" => s.homepage,
-    "rubygems_mfa_required" => "true"
+    "rubygems_mfa_required" => "true",
   }
 
   s.add_dependency "railties", ">= 6.1"
diff --git a/lib/datagrid.rb b/lib/datagrid.rb
index 80ec058..a63a21b 100644
--- a/lib/datagrid.rb
+++ b/lib/datagrid.rb
@@ -6,7 +6,6 @@
 
 # @main README.md
 module Datagrid
-
   # @!visibility private
   def self.included(base)
     Utils.warn_once("Including Datagrid is deprecated. Inherit Datagrid::Base instead.")
@@ -25,6 +24,6 @@ class ArgumentError < ::ArgumentError; end
   class ColumnUnavailableError < StandardError; end
 end
 
-require 'datagrid/base'
+require "datagrid/base"
 require "datagrid/scaffold"
 I18n.load_path << File.expand_path("datagrid/locale/en.yml", __dir__)
diff --git a/lib/datagrid/base.rb b/lib/datagrid/base.rb
index 6d86f97..f904d16 100644
--- a/lib/datagrid/base.rb
+++ b/lib/datagrid/base.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   extend ActiveSupport::Autoload
 
diff --git a/lib/datagrid/column_names_attribute.rb b/lib/datagrid/column_names_attribute.rb
index 48041ae..7cb5ef1 100644
--- a/lib/datagrid/column_names_attribute.rb
+++ b/lib/datagrid/column_names_attribute.rb
@@ -33,7 +33,7 @@ def column_names_filter(**options)
           select: :optional_columns_select,
           multiple: true,
           dummy: true,
-          **options
+          **options,
         )
       end
     end
diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index 360cd0f..dca4638 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -201,8 +201,8 @@ def find_column_by_name(columns, name)
     def assets
       append_column_preload(
         driver.append_column_queries(
-          super, columns.select(&:query)
-        )
+          super, columns.select(&:query),
+        ),
       )
     end
 
@@ -277,7 +277,7 @@ def to_csv(*column_names, **options)
       CSV.generate(
         headers: header(*column_names),
         write_headers: true,
-        **options
+        **options,
       ) do |csv|
         each_with_batches do |asset|
           csv << row_for(asset, *column_names)
@@ -294,7 +294,7 @@ def to_csv(*column_names, **options)
     #   grid.columns(:id, :category) # => id and category column
     def columns(*column_names, data: false, html: false)
       self.class.filter_columns(
-        columns_array, *column_names, data: data, html: html
+        columns_array, *column_names, data: data, html: html,
       ).select do |column|
         column.enabled?(self)
       end
@@ -480,9 +480,9 @@ def cache_key(asset)
       end
     rescue NotImplementedError
       raise Datagrid::ConfigurationError,
-            <<~MSG
-              #{self} is setup to use cache. But there was appropriate cache key found for #{asset.inspect}.
-            MSG
+        <<~MSG
+          #{self} is setup to use cache. But there was appropriate cache key found for #{asset.inspect}.
+        MSG
     end
 
     def map_with_batches(&block)
diff --git a/lib/datagrid/drivers/active_record.rb b/lib/datagrid/drivers/active_record.rb
index 64f5949..282dccd 100644
--- a/lib/datagrid/drivers/active_record.rb
+++ b/lib/datagrid/drivers/active_record.rb
@@ -98,7 +98,7 @@ def normalized_column_type(scope, field)
           %i[float decimal] => :float,
           [:date] => :date,
           %i[datetime timestamp] => :timestamp,
-          [:boolean] => :boolean
+          [:boolean] => :boolean,
         }.each do |keys, value|
           return value if keys.include?(builtin_type)
         end
diff --git a/lib/datagrid/drivers/mongoid.rb b/lib/datagrid/drivers/mongoid.rb
index e75b674..25c97fa 100644
--- a/lib/datagrid/drivers/mongoid.rb
+++ b/lib/datagrid/drivers/mongoid.rb
@@ -73,7 +73,7 @@ def normalized_column_type(scope, field)
 
           [Float] => :float,
 
-          [Integer] => :integer
+          [Integer] => :integer,
         }.each do |keys, value|
           return value if keys.include?(type)
         end
diff --git a/lib/datagrid/drivers/sequel.rb b/lib/datagrid/drivers/sequel.rb
index a84766e..20ea28b 100644
--- a/lib/datagrid/drivers/sequel.rb
+++ b/lib/datagrid/drivers/sequel.rb
@@ -67,7 +67,7 @@ def normalized_column_type(scope, field)
           %i[float decimal] => :float,
           [:date] => :date,
           [:datetime] => :timestamp,
-          [:boolean] => :boolean
+          [:boolean] => :boolean,
         }.each do |keys, value|
           return value if keys.include?(type)
         end
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index de15078..6ed0047 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -27,7 +27,7 @@ module Filters
       integer: Filters::IntegerFilter,
       enum: Filters::EnumFilter,
       float: Filters::FloatFilter,
-      dynamic: Filters::DynamicFilter
+      dynamic: Filters::DynamicFilter,
     }.freeze
 
     # @!visibility private
@@ -144,7 +144,7 @@ def filters_inspection
     def initialize(...)
       self.filters_array = self.class.filters_array.clone
       filters_array.each do |filter|
-        if value = filter.default(self)
+        if (value = filter.default(self))
           self[filter.name] = value
         end
       end
@@ -219,7 +219,7 @@ def find_select_filter(filter)
         type = FILTER_TYPES.invert[filter.class].inspect
         raise(
           ::Datagrid::ArgumentError,
-          "#{self.class.name}##{filter.name} with type #{type} can not have select options"
+          "#{self.class.name}##{filter.name} with type #{type} can not have select options",
         )
       end
       filter
diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index 7d373aa..69e7854 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -11,7 +11,7 @@ class FilteringError < StandardError
 module Datagrid
   module Filters
     class BaseFilter
-      class_attribute :default_input_options, instance_writer: false, default: {type: 'text'}
+      class_attribute :default_input_options, instance_writer: false, default: { type: "text" }
 
       attr_accessor :grid_class, :options, :block, :name
 
@@ -41,7 +41,7 @@ def apply(grid_object, scope, value)
         unless grid_object.driver.match?(result)
           raise(
             Datagrid::FilteringError,
-            "Filter #{name.inspect} unapplicable: result no longer match #{grid_object.driver.class}."
+            "Filter #{name.inspect} unapplicable: result no longer match #{grid_object.driver.class}.",
           )
         end
 
@@ -57,7 +57,7 @@ def parse_values(value)
           end
         elsif value.is_a?(Array)
           raise Datagrid::ArgumentError,
-                "#{grid_class}##{name} filter can not accept Array argument. Use :multiple option."
+            "#{grid_class}##{name} filter can not accept Array argument. Use :multiple option."
         else
           parse(value)
         end
diff --git a/lib/datagrid/filters/boolean_filter.rb b/lib/datagrid/filters/boolean_filter.rb
index d54eea3..72b6d15 100644
--- a/lib/datagrid/filters/boolean_filter.rb
+++ b/lib/datagrid/filters/boolean_filter.rb
@@ -5,7 +5,7 @@
 module Datagrid
   module Filters
     class BooleanFilter < Datagrid::Filters::BaseFilter
-      self.default_input_options = {type: 'checkbox' }
+      self.default_input_options = { type: "checkbox" }
 
       def parse(value)
         Datagrid::Utils.booleanize(value)
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index 3fe5428..44bd0c5 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -7,7 +7,7 @@ module Filters
     class DateFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
-      self.default_input_options = { type: 'date' }
+      self.default_input_options = { type: "date" }
 
       def apply(grid_object, scope, value)
         value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
diff --git a/lib/datagrid/filters/date_time_filter.rb b/lib/datagrid/filters/date_time_filter.rb
index 37effa0..09d2bed 100644
--- a/lib/datagrid/filters/date_time_filter.rb
+++ b/lib/datagrid/filters/date_time_filter.rb
@@ -7,7 +7,7 @@ module Filters
     class DateTimeFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
-      self.default_input_options = {type: 'datetime-local' }
+      self.default_input_options = { type: "datetime-local" }
 
       def parse(value)
         Datagrid::Utils.parse_datetime(value)
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index febb8f6..c9bb9be 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -15,7 +15,7 @@ class DynamicFilter < Datagrid::Filters::BaseFilter
         EQUAL_OPERATION,
         LIKE_OPERATION,
         MORE_EQUAL_OPERATION,
-        LESS_EQUAL_OPERATION
+        LESS_EQUAL_OPERATION,
       ].freeze
       AVAILABLE_OPERATIONS = %w[= =~ >= <=].freeze
 
@@ -48,7 +48,7 @@ def default_filter_where(scope, filter)
 
         unless operations.include?(operation)
           raise Datagrid::FilteringError,
-                "Unknown operation: #{operation.inspect}. Available operations: #{operations.join(' ')}"
+            "Unknown operation: #{operation.inspect}. Available operations: #{operations.join(' ')}"
         end
 
         case operation
@@ -70,7 +70,7 @@ def default_filter_where(scope, filter)
           driver.less_equal(scope, field, value)
         else
           raise Datagrid::FilteringError,
-                "Unknown operation: #{operation.inspect}. Use filter block argument to implement operation"
+            "Unknown operation: #{operation.inspect}. Use filter block argument to implement operation"
         end
       end
 
@@ -91,7 +91,7 @@ def default_select
           grid.driver.column_names(grid.scope).map do |name|
             # Mongodb/Rails problem:
             # '_id'.humanize returns ''
-            [name.gsub(/^_/, "").humanize.strip, name]
+            [name.gsub(%r{^_}, "").humanize.strip, name]
           end
         }
       end
@@ -119,16 +119,16 @@ def initialize(grid_class, object = nil)
           else
             raise ArgumentError, object.inspect
           end
-          if grid_class
-            type = grid_class.driver.normalized_column_type(
-              grid_class.scope, field
-            )
-            self.value = type_cast(type, value)
-          end
+          return unless grid_class
+
+          type = grid_class.driver.normalized_column_type(
+            grid_class.scope, field,
+          )
+          self.value = type_cast(type, value)
         end
 
         def inspect
-          {field: field, operation: operation, value: value}
+          { field: field, operation: operation, value: value }
         end
 
         def to_ary
@@ -140,7 +140,7 @@ def to_a
         end
 
         def to_h
-          {field: field, operation: operation, value: value}
+          { field: field, operation: operation, value: value }
         end
 
         protected
@@ -152,9 +152,9 @@ def type_cast(type, value)
           when :string
             value.to_s
           when :integer
-            value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_i : nil
+            value.is_a?(Numeric) || value =~ %r{^\d} ?  value.to_i : nil
           when :float
-            value.is_a?(Numeric) || value =~ /^\d/ ?  value.to_f : nil
+            value.is_a?(Numeric) || value =~ %r{^\d} ?  value.to_f : nil
           when :date, :timestamp
             Datagrid::Utils.parse_date(value)
           when :boolean
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 4133d07..7cacacb 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -7,7 +7,7 @@ module Filters
     class EnumFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::SelectOptions
 
-      self.default_input_options = {type: 'select' }
+      self.default_input_options = { type: "select" }
 
       def initialize(*args)
         super
diff --git a/lib/datagrid/filters/extended_boolean_filter.rb b/lib/datagrid/filters/extended_boolean_filter.rb
index e2bbc6b..b8a9b29 100644
--- a/lib/datagrid/filters/extended_boolean_filter.rb
+++ b/lib/datagrid/filters/extended_boolean_filter.rb
@@ -9,7 +9,7 @@ class ExtendedBooleanFilter < Datagrid::Filters::EnumFilter
       TRUTH_VALUES = [true, "true", "y", "yes"].freeze
       FALSE_VALUES = [false, "false", "n", "no"].freeze
 
-      self.default_input_options = {type: 'select' }
+      self.default_input_options = { type: "select" }
 
       def initialize(report, attribute, options = {}, &block)
         options[:select] = -> { boolean_select }
diff --git a/lib/datagrid/filters/float_filter.rb b/lib/datagrid/filters/float_filter.rb
index 0257746..4cafa9f 100644
--- a/lib/datagrid/filters/float_filter.rb
+++ b/lib/datagrid/filters/float_filter.rb
@@ -6,7 +6,7 @@ module Filters
     class FloatFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
-      self.default_input_options = {type: 'number', step: 'any' }
+      self.default_input_options = { type: "number", step: "any" }
 
       def parse(value)
         return nil if value.blank?
diff --git a/lib/datagrid/filters/integer_filter.rb b/lib/datagrid/filters/integer_filter.rb
index fd64bb2..3029df6 100644
--- a/lib/datagrid/filters/integer_filter.rb
+++ b/lib/datagrid/filters/integer_filter.rb
@@ -7,7 +7,7 @@ module Filters
     class IntegerFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
-      self.default_input_options = {type: 'number', step: '1' }
+      self.default_input_options = { type: "number", step: "1" }
 
       def parse(value)
         return nil if value.blank?
diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index abe73de..a18cb18 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -3,18 +3,17 @@
 module Datagrid
   module Filters
     module RangedFilter
-      SERIALIZED_RANGE =  /\A(.*)\.{2,3}(.*)\z/
+      SERIALIZED_RANGE = %r{\A(.*)\.{2,3}(.*)\z}.freeze
 
       def parse_values(value)
-        unless range?
-          return super
-        end
+        return super unless range?
+
         case value
         when String
-          if value == '..' || value == '...'
+          if ["..", "..."].include?(value)
             nil
-          elsif match = value.match(SERIALIZED_RANGE)
-            to_range(match.captures[0], match.captures[1], value.include?('...'))
+          elsif (match = value.match(SERIALIZED_RANGE))
+            to_range(match.captures[0], match.captures[1], value.include?("..."))
           else
             super
           end
@@ -56,9 +55,7 @@ def to_range(from, to, exclusive = false)
         return nil unless to || from
 
         # If wrong range is given - reverse it to be always valid
-        if from && to && from > to
-          from, to = to, from
-        end
+        from, to = to, from if from && to && from > to
         exclusive ? from...to : from..to
       end
 
@@ -69,7 +66,7 @@ def parse_array(result)
         case result.size
         when 0
           nil
-        when 1,2
+        when 1, 2
           to_range(first, last)
         else
           raise ArgumentError, "Can not create a range from array of more than two elements"
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 1af46df..34a921a 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -32,13 +32,11 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       type = options.delete(:type)&.to_sym
       if %i[datetime-local date].include?(type)
         if options.key?(:value) && options[:value].nil? &&
-            # https://github.com/rails/rails/pull/53387
-            options[:value] = ""
-        end
-      else
-        if options[:value]
-          options[:value] = filter.format(options[:value])
+           # https://github.com/rails/rails/pull/53387
+           (options[:value] = "")
         end
+      elsif options[:value]
+        options[:value] = filter.format(options[:value])
       end
       case type
       when :"datetime-local"
@@ -60,7 +58,7 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
           {
             include_blank: filter.include_blank,
             prompt: filter.prompt,
-            include_hidden: false
+            include_hidden: false,
           },
           multiple: filter.multiple?,
           **options,
@@ -106,8 +104,8 @@ def datagrid_enum_filter(filter, options = {}, &block)
             elements: elements,
             form: self,
             filter: filter,
-            options: options
-          }
+            options: options,
+          },
         )
       else
         datagrid_filter_input(filter, **options, type: :select, &block)
@@ -140,10 +138,10 @@ def datagrid_dynamic_filter(filter, options = {})
           include_blank: filter.include_blank,
           prompt: filter.prompt,
           include_hidden: false,
-          selected: field
+          selected: field,
         },
         **add_html_classes(options, "datagrid-dynamic-field"),
-        name: @template.field_name(object_name, filter.name, 'field')
+        name: @template.field_name(object_name, filter.name, "field"),
       )
       operation_input = dynamic_filter_select(
         filter.name, filter.operations_select,
@@ -151,16 +149,16 @@ def datagrid_dynamic_filter(filter, options = {})
           include_blank: false,
           include_hidden: false,
           prompt: false,
-          selected: operation
+          selected: operation,
         },
         **add_html_classes(options, "datagrid-dynamic-operation"),
-        name: @template.field_name(object_name, filter.name, 'operation')
+        name: @template.field_name(object_name, filter.name, "operation"),
       )
       value_input = datagrid_filter_input(
         filter.name,
         **add_html_classes(options, "datagrid-dynamic-value"),
         value: value,
-        name: @template.field_name(object_name, filter.name, 'value')
+        name: @template.field_name(object_name, filter.name, "value"),
       )
       [field_input, operation_input, value_input].join("\n").html_safe
     end
@@ -182,7 +180,7 @@ def datagrid_range_filter(_type, filter, options = {})
         from_options = datagrid_range_filter_options(object, filter, :from, **options)
         to_options = datagrid_range_filter_options(object, filter, :to, **options)
         render_partial "range_filter", {
-          from_options: from_options, to_options: to_options, filter: filter, form: self
+          from_options: from_options, to_options: to_options, filter: filter, form: self,
         }
       else
         datagrid_filter_input(filter, **options)
diff --git a/lib/datagrid/ordering.rb b/lib/datagrid/ordering.rb
index 695a812..8b8ebc2 100644
--- a/lib/datagrid/ordering.rb
+++ b/lib/datagrid/ordering.rb
@@ -110,7 +110,7 @@ def reverse_order(assets)
       driver.reverse_order(assets)
     rescue NotImplementedError
       self.class.order_unsupported(order_column.name,
-                                   "Your ORM do not support reverse order: please specify :order_desc option manually")
+        "Your ORM do not support reverse order: please specify :order_desc option manually",)
     end
 
     def apply_block_order(assets, order)
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index 8c62935..2253b01 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -27,25 +27,25 @@ def form_for(grid, options = {})
 
     def table(grid, assets, **options)
       _render_partial("table", options[:partials],
-                      {
-                        grid: grid,
-                        options: options,
-                        assets: assets
-                      })
+        {
+          grid: grid,
+          options: options,
+          assets: assets,
+        },)
     end
 
     def header(grid, options = {})
       options[:order] = true unless options.key?(:order)
 
       _render_partial("head", options[:partials],
-                      { grid: grid, options: options })
+        { grid: grid, options: options },)
     end
 
     def rows(grid, assets = grid.assets, **options, &block)
       @template.safe_join(
         assets.map do |asset|
           row(grid, asset, **options, &block)
-        end.to_a
+        end.to_a,
       )
     end
 
@@ -57,7 +57,7 @@ def row(grid, asset, **options, &block)
 
     def order_for(grid, column, options = {})
       _render_partial("order_for", options[:partials],
-                      { grid: grid, column: column })
+        { grid: grid, column: column },)
     end
 
     def order_path(grid, column, descending, request)
@@ -65,7 +65,7 @@ def order_path(grid, column, descending, request)
       query = request&.query_parameters || {}
       ActionDispatch::Http::URL.path_for(
         path: request&.path || "/",
-        params: query.merge(grid.query_params(order: column.name, descending: descending))
+        params: query.merge(grid.query_params(order: column.name, descending: descending)),
       )
     end
 
@@ -74,7 +74,7 @@ def order_path(grid, column, descending, request)
     def _render_partial(partial_name, partials_path, locals = {})
       @template.render({
         partial: File.join(partials_path || "datagrid", partial_name),
-        locals: locals
+        locals: locals,
       })
     end
   end
@@ -118,10 +118,10 @@ def each(&block)
 
       def to_s
         @renderer.send(:_render_partial, "row", options[:partials], {
-                         grid: grid,
-                         options: options,
-                         asset: asset
-                       })
+          grid: grid,
+          options: options,
+          asset: asset,
+        },)
       end
 
       protected
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index 9fae2a0..d0f69a0 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -15,7 +15,7 @@ def create_scaffold
       template "base.rb.erb", base_grid_file unless file_exists?(base_grid_file)
       template "grid.rb.erb", "app/grids/#{grid_class_name.underscore}.rb"
       if file_exists?(grid_controller_file)
-        inject_into_file grid_controller_file, index_action, after: /class .*#{grid_controller_class_name}.*\n/
+        inject_into_file grid_controller_file, index_action, after: %r{class .*#{grid_controller_class_name}.*\n}
       else
         create_file grid_controller_file, controller_code
       end
@@ -26,11 +26,11 @@ def create_scaffold
         {
           "css" => " *= require datagrid",
           "css.sass" => " *= require datagrid",
-          "css.scss" => " *= require datagrid"
+          "css.scss" => " *= require datagrid",
         }.each do |extension, string|
           file = "app/assets/stylesheets/application.#{extension}"
           if file_exists?(file)
-            inject_into_file file, "#{string}\n", { before: /.*require_self/ } # before all
+            inject_into_file file, "#{string}\n", { before: %r{.*require_self} } # before all
           end
         end
       end
@@ -45,7 +45,7 @@ def grid_class_name
     end
 
     def grid_base_class
-      file_exists?('app/grids/base_grid.rb') ? 'BaseGrid' : 'ApplicationGrid'
+      file_exists?("app/grids/base_grid.rb") ? "BaseGrid" : "ApplicationGrid"
     end
 
     def grid_controller_class_name
@@ -72,7 +72,7 @@ def pagination_helper_code
       if defined?(::WillPaginate)
         "will_paginate(@grid.assets)"
       elsif defined?(::Pagy)
-         "pagy_nav(@pagy)"
+        "pagy_nav(@pagy)"
       else
         # Kaminari is default
         "paginate(@grid.assets)"
@@ -112,28 +112,28 @@ def index_code
 
     def controller_code
       <<~RUBY
-class #{grid_controller_class_name} < ApplicationController
-  def index
-#{index_code.rstrip}
-  end
+        class #{grid_controller_class_name} < ApplicationController
+          def index
+        #{index_code.rstrip}
+          end
 
-  protected
+          protected
 
-  def grid_params
-    params.fetch(:#{grid_param_name}, {}).permit!
-  end
-end
-RUBY
+          def grid_params
+            params.fetch(:#{grid_param_name}, {}).permit!
+          end
+        end
+      RUBY
     end
 
     def view_code
       indent(<<~ERB)
-<%= datagrid_form_for @grid, url: #{grid_route_name} %>
+        <%= datagrid_form_for @grid, url: #{grid_route_name} %>
 
-<%= #{pagination_helper_code} %>
-<%= #{table_helper_code} %>
-<%= #{pagination_helper_code} %>
-ERB
+        <%= #{pagination_helper_code} %>
+        <%= #{table_helper_code} %>
+        <%= #{pagination_helper_code} %>
+      ERB
     end
 
     protected
diff --git a/spec/datagrid/columns_spec.rb b/spec/datagrid/columns_spec.rb
index 4b83f48..3e313f0 100644
--- a/spec/datagrid/columns_spec.rb
+++ b/spec/datagrid/columns_spec.rb
@@ -19,7 +19,7 @@
         category: "first",
         access_level: "admin",
         pet: "rottweiler",
-        shipping_date: Date.new(2013, 8, 1)
+        shipping_date: Date.new(2013, 8, 1),
       )
     end
 
@@ -126,9 +126,9 @@ class Report27 < Datagrid::Base
 
     it "should generate table data" do
       expect(subject.data).to eq([
-                                   subject.header,
-                                   subject.row_for(entry)
-                                 ])
+        subject.header,
+        subject.row_for(entry),
+      ])
     end
 
     it "supports dynamic header" do
@@ -142,17 +142,17 @@ class Report27 < Datagrid::Base
 
     it "should generate hash for given asset" do
       expect(subject.hash_for(entry)).to eq({
-                                              group: "Pop",
-                                              name: "Star",
-                                              access_level: "admin",
-                                              pet: "ROTTWEILER",
-                                              shipping_date: date
-                                            })
+        group: "Pop",
+        name: "Star",
+        access_level: "admin",
+        pet: "ROTTWEILER",
+        shipping_date: date,
+      })
     end
 
     it "should support csv export" do
       expect(subject.to_csv).to eq(
-        "Shipping date,Group,Name,Access level,Pet\n#{date},Pop,Star,admin,ROTTWEILER\n"
+        "Shipping date,Group,Name,Access level,Pet\n#{date},Pop,Star,admin,ROTTWEILER\n",
       )
     end
 
@@ -162,7 +162,7 @@ class Report27 < Datagrid::Base
 
     it "should support csv export options" do
       expect(subject.to_csv(col_sep: ";")).to eq(
-        "Shipping date;Group;Name;Access level;Pet\n#{date};Pop;Star;admin;ROTTWEILER\n"
+        "Shipping date;Group;Name;Access level;Pet\n#{date};Pop;Star;admin;ROTTWEILER\n",
       )
     end
   end
diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index c2ff630..f7cea42 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -106,7 +106,7 @@ class InspectTest < Datagrid::Base
 
       grid = InspectTest.new(created_at: %w[2014-01-01 2014-08-05], descending: true, order: "name")
       expect(grid.inspect).to eq(
-        "#<InspectTest order: :name, descending: true, created_at: Wed, 01 Jan 2014..Tue, 05 Aug 2014>"
+        "#<InspectTest order: :name, descending: true, created_at: Wed, 01 Jan 2014..Tue, 05 Aug 2014>",
       )
     end
   end
@@ -211,7 +211,7 @@ class EqualTest < Datagrid::Base
         filter(:name)
       end
 
-      expect(grid.name).to eq('one')
+      expect(grid.name).to eq("one")
     end
 
     it "doesn't permit attributes when forbidden_attributes_protection is set" do
@@ -236,7 +236,7 @@ class EqualTest < Datagrid::Base
 
     it "supports hash attribute assignment" do
       grid = test_report(
-        ActionController::Parameters.new(group_id: {from: 1, to:2})
+        ActionController::Parameters.new(group_id: { from: 1, to: 2 }),
       ) do
         scope { Entry }
         filter(:group_id, :integer, range: true)
diff --git a/spec/datagrid/drivers/active_record_spec.rb b/spec/datagrid/drivers/active_record_spec.rb
index fbc6c06..1dd671a 100644
--- a/spec/datagrid/drivers/active_record_spec.rb
+++ b/spec/datagrid/drivers/active_record_spec.rb
@@ -19,8 +19,8 @@
 
   it "should support append_column_queries" do
     scope = subject.append_column_queries(Entry.where({}),
-                                          [Datagrid::Columns::Column.new(test_report_class, :sum_group_id,
-                                                                         "sum(entries.group_id)")])
+      [Datagrid::Columns::Column.new(test_report_class, :sum_group_id,
+        "sum(entries.group_id)",)],)
     expect(scope.to_sql.strip).to eq('SELECT "entries".*, sum(entries.group_id) AS sum_group_id FROM "entries"')
   end
 
@@ -49,18 +49,18 @@
     end
     it "includes object created in proper range" do
       expect(subject).to include(
-        Entry.create!(group: Group.create!(created_at: 7.days.ago))
+        Entry.create!(group: Group.create!(created_at: 7.days.ago)),
       )
     end
 
     it "excludes object created before the range" do
       expect(subject).to_not include(
-        Entry.create!(created_at: 7.days.ago, group: Group.create!(created_at: 11.days.ago))
+        Entry.create!(created_at: 7.days.ago, group: Group.create!(created_at: 11.days.ago)),
       )
     end
     it "excludes object created after the range" do
       expect(subject).to_not include(
-        Entry.create!(created_at: 7.days.ago, group: Group.create!(created_at: 4.days.ago))
+        Entry.create!(created_at: 7.days.ago, group: Group.create!(created_at: 4.days.ago)),
       )
     end
   end
diff --git a/spec/datagrid/drivers/mongo_mapper_spec.rb b/spec/datagrid/drivers/mongo_mapper_spec.rb
index 2deec97..750f02d 100644
--- a/spec/datagrid/drivers/mongo_mapper_spec.rb
+++ b/spec/datagrid/drivers/mongo_mapper_spec.rb
@@ -15,7 +15,7 @@
     describe "api" do
       subject do
         MongoMapperGrid.new(
-          defined?(_attributes) ? _attributes : {}
+          defined?(_attributes) ? _attributes : {},
         )
       end
 
@@ -23,14 +23,14 @@
         MongoMapperEntry.create!(
           group_id: 2,
           name: "Main First",
-          disabled: false
+          disabled: false,
         )
       end
       let!(:second) do
         MongoMapperEntry.create!(
           group_id: 3,
           name: "Main Second",
-          disabled: true
+          disabled: true,
         )
       end
 
diff --git a/spec/datagrid/drivers/mongoid_spec.rb b/spec/datagrid/drivers/mongoid_spec.rb
index 1f9646e..d68958d 100644
--- a/spec/datagrid/drivers/mongoid_spec.rb
+++ b/spec/datagrid/drivers/mongoid_spec.rb
@@ -13,7 +13,7 @@
   describe "api" do
     subject do
       MongoidGrid.new(
-        defined?(_attributes) ? _attributes : {}
+        defined?(_attributes) ? _attributes : {},
       )
     end
 
@@ -21,14 +21,14 @@
       MongoidEntry.create!(
         group_id: 2,
         name: "Main First",
-        disabled: false
+        disabled: false,
       )
     end
     let!(:second) do
       MongoidEntry.create!(
         group_id: 3,
         name: "Main Second",
-        disabled: true
+        disabled: true,
       )
     end
 
diff --git a/spec/datagrid/drivers/sequel_spec.rb b/spec/datagrid/drivers/sequel_spec.rb
index 5b79963..93c0636 100644
--- a/spec/datagrid/drivers/sequel_spec.rb
+++ b/spec/datagrid/drivers/sequel_spec.rb
@@ -13,7 +13,7 @@
   describe "api" do
     subject do
       SequelGrid.new(
-        defined?(_attributes) ? _attributes : {}
+        defined?(_attributes) ? _attributes : {},
       )
     end
 
@@ -21,14 +21,14 @@
       SequelEntry.create(
         group_id: 2,
         name: "Main First",
-        disabled: false
+        disabled: false,
       )
     end
     let!(:second) do
       SequelEntry.create(
         group_id: 3,
         name: "Main Second",
-        disabled: true
+        disabled: true,
       )
     end
 
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 9d09a52..3aa08c9 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -37,7 +37,7 @@
     e3 = Entry.create!(created_at: 1.day.ago)
     from = 5.days.ago
     to = 3.days.ago
-    report = test_report(created_at: {from: , to:}) do
+    report = test_report(created_at: { from: from, to: to }) do
       scope { Entry }
       filter(:created_at, :date, range: true)
     end
@@ -47,13 +47,13 @@
     expect(report.assets).not_to include(e3)
     report.created_at = {}
     expect(report.created_at).to eq(nil)
-    report.created_at = {from: nil, to: nil}
+    report.created_at = { from: nil, to: nil }
     expect(report.created_at).to eq(nil)
-    report.created_at = {from: Date.today, to: nil}
+    report.created_at = { from: Date.today, to: nil }
     expect(report.created_at).to eq(Date.today..nil)
-    report.created_at = {from: nil, to: Date.today}
+    report.created_at = { from: nil, to: Date.today }
     expect(report.created_at).to eq(nil..Date.today)
-    report.created_at = {from: Time.now, to: Time.now}
+    report.created_at = { from: Time.now, to: Time.now }
     expect(report.created_at).to eq(Date.today..Date.today)
   end
 
diff --git a/spec/datagrid/filters/dynamic_filter_spec.rb b/spec/datagrid/filters/dynamic_filter_spec.rb
index f373f08..eae16f6 100644
--- a/spec/datagrid/filters/dynamic_filter_spec.rb
+++ b/spec/datagrid/filters/dynamic_filter_spec.rb
@@ -63,25 +63,25 @@
   it "should nullify incorrect value for integer" do
     report.condition = [:group_id, "<=", "aa"]
     expect(report.condition.to_h).to eq(
-      {field: :group_id, operation: "<=", value: nil}
+      { field: :group_id, operation: "<=", value: nil },
     )
   end
 
   it "should nullify incorrect value for date" do
     report.condition = [:shipping_date, "<=", "aa"]
     expect(report.condition.to_h).to eq({
-      field: :shipping_date, operation: "<=", value: nil
+      field: :shipping_date, operation: "<=", value: nil,
     })
   end
 
   it "should nullify incorrect value for datetime" do
     report.condition = [:created_at, "<=", "aa"]
-    expect(report.condition.to_h).to eq({field: :created_at, operation: "<=", value: nil})
+    expect(report.condition.to_h).to eq({ field: :created_at, operation: "<=", value: nil })
   end
 
   it "should support date comparation operation by timestamp column" do
     report.condition = [:created_at, "<=", "1986-08-05"]
-    expect(report.condition.to_h).to eq({field: :created_at, operation: "<=", value: Date.parse("1986-08-05")})
+    expect(report.condition.to_h).to eq({ field: :created_at, operation: "<=", value: Date.parse("1986-08-05") })
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-04 01:01:01")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:00")))
@@ -91,7 +91,7 @@
 
   it "should support date = operation by timestamp column" do
     report.condition = [:created_at, "=", "1986-08-05"]
-    expect(report.condition.to_h).to eq({field: :created_at, operation: "=", value: Date.parse("1986-08-05")})
+    expect(report.condition.to_h).to eq({ field: :created_at, operation: "=", value: Date.parse("1986-08-05") })
     expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-04 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:01")))
@@ -102,7 +102,7 @@
 
   it "should support date =~ operation by timestamp column" do
     report.condition = [:created_at, "=~", "1986-08-05"]
-    expect(report.condition.to_h).to eq({field: :created_at, operation: "=~", value: Date.parse("1986-08-05")})
+    expect(report.condition.to_h).to eq({ field: :created_at, operation: "=~", value: Date.parse("1986-08-05") })
     expect(report.assets).not_to include(Entry.create!(created_at: Time.parse("1986-08-04 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 23:59:59")))
     expect(report.assets).to include(Entry.create!(created_at: Time.parse("1986-08-05 00:00:01")))
@@ -129,7 +129,7 @@
       scope { Entry }
       filter(
         :condition, :dynamic, allow_nil: true, allow_blank: true,
-        operations: [">=", "<="]
+        operations: [">=", "<="],
       ) do |(field, operation, value), scope|
         if value.blank?
           scope.where(disabled: false)
@@ -153,7 +153,7 @@
     grid = test_report do
       scope { Entry }
       filter(
-        :condition, :dynamic, operations: ["=", "!="]
+        :condition, :dynamic, operations: ["=", "!="],
       ) do |filter, scope|
         if filter.operation == "!="
           scope.where("#{filter.field} != ?", filter.value)
@@ -181,14 +181,14 @@
   end
 
   it "supports assignment of string keys hash" do
-    report.condition =  {
+    report.condition = {
       field: "shipping_date",
       operation: "<>",
       value: "1996-08-05",
     }.stringify_keys
 
     expect(report.condition.to_h).to eq({
-      field: 'shipping_date', operation: '<>', value: Date.parse('1996-08-05')
+      field: "shipping_date", operation: "<>", value: Date.parse("1996-08-05"),
     })
   end
 end
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index 9ea1183..913249d 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -104,7 +104,7 @@
       scope { Entry }
       filter(:group_id, :integer, multiple: true)
     end
-    expect(report.group_id).to eq([1,2])
+    expect(report.group_id).to eq([1, 2])
     expect(report.assets).to include(entry1)
     expect(report.assets).to include(entry2)
     expect(report.assets).not_to include(entry3)
diff --git a/spec/datagrid/filters_spec.rb b/spec/datagrid/filters_spec.rb
index 16d126e..457c75f 100644
--- a/spec/datagrid/filters_spec.rb
+++ b/spec/datagrid/filters_spec.rb
@@ -46,7 +46,6 @@
     class ModelWithoutTable < ActiveRecord::Base; end
     expect(ModelWithoutTable).not_to be_table_exists
     class TheReport < Datagrid::Base
-
       scope { ModelWithoutTable }
 
       filter(:name)
@@ -229,7 +228,7 @@ class InheritedReport < TranslatedReport
       filters = {
         id: [1, 2],
         name: [["a", 1], ["b", 2]],
-        category: { a: 1, b: 2 }
+        category: { a: 1, b: 2 },
       }
       grid = test_report do
         scope { Entry }
@@ -268,7 +267,7 @@ class TestGrid < Datagrid::Base
       end
 
       expect(NsInspect::TestGrid.inspect).to eq(
-        "NsInspect::TestGrid(id: integer, name: string, current_user: default)"
+        "NsInspect::TestGrid(id: integer, name: string, current_user: default)",
       )
     end
 
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 5b64bb5..e8bfee9 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -36,7 +36,7 @@ class MyTemplate
       let(:_filter) { :name }
       it {
         should equal_to_dom(
-          '<input type="text" name="report[name]" id="report_name"/>'
+          '<input type="text" name="report[name]" id="report_name"/>',
         )
       }
     end
@@ -50,7 +50,7 @@ class MyTemplate
       end
       it {
         should equal_to_dom(
-          '<input type="number" step="1" name="report[group_id]" id="report_group_id"/>'
+          '<input type="number" step="1" name="report[group_id]" id="report_group_id"/>',
         )
       }
 
@@ -58,7 +58,7 @@ class MyTemplate
         let(:view_options) { { partials: "anything" } }
         it {
           should equal_to_dom(
-            '<input type="number" step="1" name="report[group_id]" id="report_group_id"/>'
+            '<input type="number" step="1" name="report[group_id]" id="report_group_id"/>',
           )
         }
       end
@@ -74,7 +74,7 @@ class MyTemplate
       end
       it {
         should equal_to_dom(
-          '<input type="date" name="report[created_at]" id="report_created_at"/>'
+          '<input type="date" name="report[created_at]" id="report_created_at"/>',
         )
       }
       context "when special date format specified" do
@@ -87,7 +87,7 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input value="2012-01-02" type="date"
-                name="report[created_at]" id="report_created_at"/>'
+                name="report[created_at]" id="report_created_at"/>',
           )
         }
       end
@@ -98,12 +98,12 @@ class MyTemplate
         let(:_grid) do
           test_report do
             scope { Entry }
-            filter(:created_at, :date, input_options: { type: 'text' })
+            filter(:created_at, :date, input_options: { type: "text" })
           end
         end
         it {
           should equal_to_dom(
-            '<input type="text" name="report[created_at]" id="report_created_at"/>'
+            '<input type="text" name="report[created_at]" id="report_created_at"/>',
           )
         }
       end
@@ -117,7 +117,7 @@ class MyTemplate
         end
         it {
           should equal_to_dom(
-            '<textarea name="report[name]" id="report_name"/>'
+            '<textarea name="report[name]" id="report_name"/>',
           )
         }
       end
@@ -133,7 +133,7 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input type="text" value="2024-01-01 09:25:15 +0100"
-              name="report[created_at]" id="report_created_at"/>'
+              name="report[created_at]" id="report_created_at"/>',
           )
         }
 
@@ -143,7 +143,7 @@ class MyTemplate
           end
           it {
             should equal_to_dom(
-              '<input type="text" name="report[created_at]" id="report_created_at"/>'
+              '<input type="text" name="report[created_at]" id="report_created_at"/>',
             )
           }
         end
@@ -160,7 +160,7 @@ class MyTemplate
         it {
           should equal_to_dom(
             '<input type="date" value="2024-01-01"
-                name="report[created_at]" id="report_created_at"/>'
+                name="report[created_at]" id="report_created_at"/>',
           )
         }
       end
@@ -183,7 +183,7 @@ class MyTemplate
                 type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2" id="to_hello" class="datagrid-range-to"
-                type="number" step="1" name="report[group_id][to]"/>'
+                type="number" step="1" name="report[group_id][to]"/>',
           )
         }
       end
@@ -195,7 +195,7 @@ class MyTemplate
                 type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to"
-                type="number" step="1" name="report[group_id][to]"/>'
+                type="number" step="1" name="report[group_id][to]"/>',
           )
         }
         it { should be_html_safe }
@@ -206,7 +206,7 @@ class MyTemplate
           should equal_to_dom(
             '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="10" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>'
+            '<input value="10" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>',
           )
         }
         it { should be_html_safe }
@@ -218,7 +218,7 @@ class MyTemplate
           should equal_to_dom(
             '<input value="1" class="datagrid-range-from" type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="2" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>'
+            '<input value="2" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>',
           )
         }
       end
@@ -228,7 +228,7 @@ class MyTemplate
         let(:_range) { nil }
         it {
           should equal_to_dom(
-            "custom_range_partial"
+            "custom_range_partial",
           )
         }
       end
@@ -240,7 +240,7 @@ class MyTemplate
           should equal_to_dom(
             '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]">
             <span class="datagrid-range-separator"> - </span>
-            <input class="datagrid-range-to" type="number" step="1" name="report[group_id][to]">'
+            <input class="datagrid-range-to" type="number" step="1" name="report[group_id][to]">',
           )
         }
       end
@@ -261,7 +261,7 @@ class MyTemplate
               type="number" step="any" name="report[rating][from]"/>' \
           '<span class="datagrid-range-separator"> - </span>' \
           '<input value="2.5" class="datagrid-range-to"
-              type="number" step="any" name="report[rating][to]"/>'
+              type="number" step="any" name="report[rating][to]"/>',
         )
       }
     end
@@ -280,7 +280,7 @@ class MyTemplate
           should equal_to_dom(
             '<input value="2012-01-03" class="datagrid-range-from" type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" type="date" name="report[created_at][to]" value=""/>'
+            '<input class="datagrid-range-to" type="date" name="report[created_at][to]" value=""/>',
           )
         }
         it { should be_html_safe }
@@ -298,7 +298,7 @@ class MyTemplate
                 type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2013-02-02" class="datagrid-range-to"
-                type="date" name="report[created_at][to]"/>'
+                type="date" name="report[created_at][to]"/>',
           )
         }
       end
@@ -310,7 +310,7 @@ class MyTemplate
                 type="date" value="" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-03" class="datagrid-range-to"
-                type="date"  name="report[created_at][to]"/>'
+                type="date"  name="report[created_at][to]"/>',
           )
         }
         it { should be_html_safe }
@@ -324,7 +324,7 @@ class MyTemplate
                 type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-02" class="datagrid-range-to"
-                type="date" name="report[created_at][to]"/>'
+                type="date" name="report[created_at][to]"/>',
           )
         }
       end
@@ -339,7 +339,7 @@ class MyTemplate
           should equal_to_dom(
             '<input class="datagrid-range-from" type="date" value="" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" type="date" value="" name="report[created_at][to]"/>'
+            '<input class="datagrid-range-to" type="date" value="" name="report[created_at][to]"/>',
           )
         }
       end
@@ -359,7 +359,7 @@ class MyTemplate
           %(<select name="report[category]" id="report_category">
        <option value="" label=" "></option>
        <option value="first">first</option>
-       <option value="second">second</option></select>)
+       <option value="second">second</option></select>),
         )
       }
 
@@ -373,7 +373,7 @@ class MyTemplate
           should equal_to_dom(
             %(<select name="report[category]" id="report_category">
           <option value="" label=" "></option>
-          <option value="block_value">block option</option></select>)
+          <option value="block_value">block option</option></select>),
           )
         }
       end
@@ -386,7 +386,7 @@ class MyTemplate
             %(<select name="report[category]" id="report_category">
        <option value="" label=" "></option>
        <option selected value="first">first</option>
-       <option value="second">second</option></select>)
+       <option value="second">second</option></select>),
           )
         }
       end
@@ -396,7 +396,7 @@ class MyTemplate
           should equal_to_dom(
             '<select name="report[category]" id="report_category">
          <option value="first">first</option>
-         <option value="second">second</option></select>'
+         <option value="second">second</option></select>',
           )
         }
       end
@@ -407,7 +407,7 @@ class MyTemplate
             '<select name="report[category]" id="report_category">
          <option value="">Choose plz</option>
          <option value="first">first</option>
-         <option value="second">second</option></select>'
+         <option value="second">second</option></select>',
           )
         }
       end
@@ -419,7 +419,7 @@ class MyTemplate
             '<select name="report[category]" id="report_category">
             <option value="">My Prompt</option>
          <option value="first">first</option>
-         <option value="second">second</option></select>'
+         <option value="second">second</option></select>',
           )
         }
       end
@@ -431,7 +431,7 @@ class MyTemplate
             '<select class="custom-class" name="report[category]" id="report_category">
             <option value="" label=" "></option>
          <option value="first">first</option>
-         <option value="second">second</option></select>'
+         <option value="second">second</option></select>',
           )
         }
       end
@@ -439,19 +439,19 @@ class MyTemplate
         let(:_category_filter_options) { { checkboxes: true } }
         it {
           should equal_to_dom(
-            <<-HTML
-<div class="datagrid-enum-checkboxes">
-<label for="report_category_first" class="datagrid-enum-checkbox-label">
-<input type="checkbox" id="report_category_first"
-    value="first" name="report[category][]" />
-first
-</label>
-<label for="report_category_second" class="datagrid-enum-checkbox-label">
-<input type="checkbox" id="report_category_second"
-    value="second" name="report[category][]" />
-second
-</label>
-</div>
+            <<~HTML,
+              <div class="datagrid-enum-checkboxes">
+              <label for="report_category_first" class="datagrid-enum-checkbox-label">
+              <input type="checkbox" id="report_category_first"
+                  value="first" name="report[category][]" />
+              first
+              </label>
+              <label for="report_category_second" class="datagrid-enum-checkbox-label">
+              <input type="checkbox" id="report_category_second"
+                  value="second" name="report[category][]" />
+              second
+              </label>
+              </div>
             HTML
           )
         }
@@ -476,7 +476,7 @@ class MyTemplate
           # hidden is important when default is set to true
           %(<input name="report[disabled]" type="hidden" value="0" autocomplete="off">
           <input type="checkbox" value="1"
-             checked name="report[disabled]" id="report_disabled">)
+             checked name="report[disabled]" id="report_disabled">),
         )
       }
     end
@@ -493,7 +493,7 @@ class MyTemplate
           %(<select name="report[disabled]" id="report_disabled">
           <option value="" label=" "></option>
           <option value="YES">Yes</option>
-          <option value="NO">No</option></select>)
+          <option value="NO">No</option></select>),
         )
       }
     end
@@ -521,7 +521,7 @@ class MyTemplate
 
         it {
           should equal_to_dom(
-            '<input value="one,two" type="text" name="report[name]" id="report_name">'
+            '<input value="one,two" type="text" name="report[name]" id="report_name">',
           )
         }
       end
@@ -535,7 +535,7 @@ class MyTemplate
             :name, :enum,
             include_blank: false,
             multiple: false,
-            select: []
+            select: [],
           )
         end
       end
@@ -552,7 +552,7 @@ class MyTemplate
       let(:_filter) { :group_id }
       it {
         should equal_to_dom(
-          '<input type="number" step="any" name="report[group_id]" id="report_group_id"/>'
+          '<input type="number" step="any" name="report[group_id]" id="report_group_id"/>',
         )
       }
     end
@@ -613,20 +613,20 @@ class MyTemplate
       let(:_filter) { :column_names }
       let(:expected_html) do
         <<~DOM
-        <div class="datagrid-enum-checkboxes">
-          <label for="report_column_names_id" class="datagrid-enum-checkbox-label">
-            <input id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">
-            Id
-          </label>
-          <label for="report_column_names_name" class="datagrid-enum-checkbox-label">
-            <input id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]"/>
-            Name
-          </label>
-          <label for="report_column_names_category" class="datagrid-enum-checkbox-label">
-            <input id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">
-            Category
-          </label>
-        </div>
+          <div class="datagrid-enum-checkboxes">
+            <label for="report_column_names_id" class="datagrid-enum-checkbox-label">
+              <input id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">
+              Id
+            </label>
+            <label for="report_column_names_name" class="datagrid-enum-checkbox-label">
+              <input id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]"/>
+              Name
+            </label>
+            <label for="report_column_names_category" class="datagrid-enum-checkbox-label">
+              <input id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">
+              Category
+            </label>
+          </div>
         DOM
       end
 
@@ -740,27 +740,27 @@ class MyTemplate
     end
     it "should generate label for filter" do
       expect(view.datagrid_label(:created_at)).to equal_to_dom(
-        '<label class="js-date-selector" for="report_created_at">Created at</label>'
+        '<label class="js-date-selector" for="report_created_at">Created at</label>',
       )
     end
     it "should generate label for filter" do
       expect(view.datagrid_label(:name)).to equal_to_dom(
-        '<label for="report_name">Name</label>'
+        '<label for="report_name">Name</label>',
       )
     end
     it "should pass options through to the helper" do
       expect(view.datagrid_label(:name, class: "foo")).to equal_to_dom(
-        '<label class="foo" for="report_name">Name</label>'
+        '<label class="foo" for="report_name">Name</label>',
       )
     end
     it "should support block" do
       expect(view.datagrid_label(:name, class: "foo") { "The Name" }).to equal_to_dom(
-        '<label class="foo" for="report_name">The Name</label>'
+        '<label class="foo" for="report_name">The Name</label>',
       )
     end
     it "should support explicit label" do
       expect(view.datagrid_label(:name, "The Name")).to equal_to_dom(
-        '<label for="report_name">The Name</label>'
+        '<label for="report_name">The Name</label>',
       )
     end
   end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 46f4498..771e2fa 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -25,7 +25,7 @@
   let(:group) { Group.create!(name: "Pop") }
   let!(:entry) do
     Entry.create!(
-      group: group, name: "Star", disabled: false, confirmed: false, category: "first"
+      group: group, name: "Star", disabled: false, confirmed: false, category: "first",
     )
   end
   let(:grid) { SimpleReport.new }
@@ -42,7 +42,7 @@
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern(
-        "table.datagrid-table tr td.datagrid-no-results" => 1
+        "table.datagrid-table tr td.datagrid-no-results" => 1,
       )
       expect(datagrid_table).to include(I18n.t("datagrid.no_results"))
     end
@@ -51,20 +51,20 @@
   describe ".datagrid_table" do
     it "should have grid class as html class on table" do
       expect(subject.datagrid_table(grid)).to match_css_pattern(
-        "table.datagrid-table" => 1
+        "table.datagrid-table" => 1,
       )
     end
     it "should return data table html" do
       datagrid_table = subject.datagrid_table(grid)
 
       expect(datagrid_table).to match_css_pattern({
-                                                    "table.datagrid-table tr th[data-column=group] div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th[data-column=group]" => /Group.*/,
-                                                    "table.datagrid-table tr th[data-column=name] div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th[data-column=name]" => /Name.*/,
-                                                    "table.datagrid-table tr td[data-column=group]" => "Pop",
-                                                    "table.datagrid-table tr td[data-column=name]" => "Star"
-                                                  })
+        "table.datagrid-table tr th[data-column=group] div.datagrid-order" => 1,
+        "table.datagrid-table tr th[data-column=group]" => %r{Group.*},
+        "table.datagrid-table tr th[data-column=name] div.datagrid-order" => 1,
+        "table.datagrid-table tr th[data-column=name]" => %r{Name.*},
+        "table.datagrid-table tr td[data-column=group]" => "Pop",
+        "table.datagrid-table tr td[data-column=name]" => "Star",
+      })
     end
 
     it "should support giving assets explicitly" do
@@ -72,17 +72,18 @@
       datagrid_table = subject.datagrid_table(grid, [entry])
 
       expect(datagrid_table).to match_css_pattern({
-                                                    "table.datagrid-table tr th[data-column=group] div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th[data-column=group]" => /Group.*/,
-                                                    "table.datagrid-table tr th[data-column=name] div.datagrid-order" => 1,
-                                                    "table.datagrid-table tr th[data-column=name]" => /Name.*/,
-                                                    "table.datagrid-table tr td[data-column=group]" => "Pop",
-                                                    "table.datagrid-table tr td[data-column=name]" => "Star"
-                                                  })
+        "table.datagrid-table tr th[data-column=group] div.datagrid-order" => 1,
+        "table.datagrid-table tr th[data-column=group]" => %r{Group.*},
+        "table.datagrid-table tr th[data-column=name] div.datagrid-order" => 1,
+        "table.datagrid-table tr th[data-column=name]" => %r{Name.*},
+        "table.datagrid-table tr td[data-column=group]" => "Pop",
+        "table.datagrid-table tr td[data-column=name]" => "Star",
+      })
     end
 
     it "should support no order given" do
-      expect(subject.datagrid_table(grid, [entry], order: false)).to match_css_pattern("table.datagrid-table th .datagrid-order" => 0)
+      expect(subject.datagrid_table(grid, [entry],
+        order: false,)).to match_css_pattern("table.datagrid-table th .datagrid-order" => 0)
     end
 
     it "should support columns option" do
@@ -90,7 +91,7 @@
         "table.datagrid-table th[data-column=name]" => 1,
         "table.datagrid-table td[data-column=name]" => 1,
         "table.datagrid-table th[data-column=group]" => 0,
-        "table.datagrid-table td[data-column=group]" => 0
+        "table.datagrid-table td[data-column=group]" => 0,
       )
     end
 
@@ -108,7 +109,7 @@
           "table.datagrid-table th[data-column=name]" => 1,
           "table.datagrid-table td[data-column=name]" => 1,
           "table.datagrid-table th[data-column=category]" => 0,
-          "table.datagrid-table td[data-column=category]" => 0
+          "table.datagrid-table td[data-column=category]" => 0,
         )
       end
     end
@@ -136,7 +137,7 @@
 
       it "renders namespaced table partial" do
         rendered_partial = subject.datagrid_table(
-          grid, [entry], partials: "client/datagrid"
+          grid, [entry], partials: "client/datagrid",
         )
         expect(rendered_partial).to include "Namespaced table partial."
         expect(rendered_partial).to include "Namespaced row partial."
@@ -157,7 +158,7 @@
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
           "table.datagrid-table th[data-column=name]" => 1,
-          "table.datagrid-table td[data-column=name]" => 2
+          "table.datagrid-table td[data-column=name]" => 2,
         )
       end
     end
@@ -173,7 +174,7 @@
       it "should render table" do
         expect(subject.datagrid_table(grid)).to match_css_pattern(
           "table.datagrid-table th[data-column=name]" => 1,
-          "table.datagrid-table td[data-column=name]" => 2
+          "table.datagrid-table td[data-column=name]" => 2,
         )
       end
     end
@@ -186,7 +187,7 @@
         column(:name)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name].datagrid-order-active-asc" => "Star"
+        "tr td[data-column=name].datagrid-order-active-asc" => "Star",
       )
     end
     it "should add ordering classes to column" do
@@ -197,9 +198,9 @@
       expect(
         subject.datagrid_rows(rp) do |row|
           subject.content_tag(:strong, row.name)
-        end
+        end,
       ).to match_css_pattern(
-        "strong" => "Star"
+        "strong" => "Star",
       )
     end
 
@@ -209,7 +210,7 @@
         column(:name)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name].datagrid-order-active-desc" => "Star"
+        "tr td[data-column=name].datagrid-order-active-desc" => "Star",
       )
     end
 
@@ -220,7 +221,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star",
       )
     end
 
@@ -232,7 +233,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name] span" => "Star"
+        "tr td[data-column=name] span" => "Star",
       )
     end
 
@@ -243,7 +244,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star",
       )
     end
 
@@ -256,7 +257,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star",
       )
     end
 
@@ -267,7 +268,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star",
       )
     end
 
@@ -277,7 +278,7 @@
         column(:name, html: ->(data) { content_tag :h1, data })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name] h1" => "Star"
+        "tr td[data-column=name] h1" => "Star",
       )
     end
 
@@ -289,7 +290,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name] em" => "STAR"
+        "tr td[data-column=name] em" => "STAR",
       )
     end
 
@@ -301,7 +302,7 @@
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name] span" => "Star-Entry"
+        "tr td[data-column=name] span" => "Star-Entry",
       )
     end
 
@@ -310,10 +311,10 @@
         scope { Entry }
         column(:name, html: lambda { |data, model|
           content_tag :h1, "#{data}-#{model.name.downcase}"
-        })
+        },)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name] h1" => "Star-star"
+        "tr td[data-column=name] h1" => "Star-star",
       )
     end
 
@@ -322,10 +323,10 @@
         scope { Entry }
         column(:name, html: lambda { |data, model, grid|
           content_tag :h1, "#{data}-#{model.name.downcase}-#{grid.assets.klass}"
-        })
+        },)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name] h1" => "Star-star-Entry"
+        "tr td[data-column=name] h1" => "Star-star-Entry",
       )
     end
 
@@ -334,12 +335,12 @@
         scope { Entry }
         column(:name, html: lambda { |data, model|
           content_tag :h1, "#{data}-#{model.name}"
-        }) do
+        },) do
           name.upcase
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name] h1" => "STAR-Star"
+        "tr td[data-column=name] h1" => "STAR-Star",
       )
     end
 
@@ -348,12 +349,12 @@
         scope { Entry }
         column(:name, html: lambda { |data, model, grid|
           content_tag :h1, "#{data}-#{model.name}-#{grid.assets.klass}"
-        }) do
+        },) do
           name.upcase
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name] h1" => "STAR-Star-Entry"
+        "tr td[data-column=name] h1" => "STAR-Star-Entry",
       )
     end
 
@@ -364,10 +365,10 @@
         column(:category)
       end
       expect(subject.datagrid_rows(rp, [entry], columns: [:name])).to match_css_pattern(
-        "tr td[data-column=name]" => "Star"
+        "tr td[data-column=name]" => "Star",
       )
       expect(subject.datagrid_rows(rp, [entry], columns: [:name])).to match_css_pattern(
-        "tr td[data-column=category]" => 0
+        "tr td[data-column=category]" => 0,
       )
     end
 
@@ -378,7 +379,7 @@
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name].my_class" => "Star"
+        "tr td[data-column=name].my_class" => "Star",
       )
     end
 
@@ -394,7 +395,7 @@
       end
       it "should ignore them" do
         expect(subject.datagrid_rows(grid, [entry])).to match_css_pattern(
-          "td[data-column=name]" => 1
+          "td[data-column=name]" => 1,
         )
       end
     end
@@ -435,9 +436,9 @@ class OrderedGrid < Datagrid::Base
   describe ".datagrid_form_for" do
     it "returns namespaced partial if partials options is passed" do
       rendered_form = subject.datagrid_form_for(grid, {
-                                                  url: "",
-                                                  partials: "client/datagrid"
-                                                })
+        url: "",
+        partials: "client/datagrid",
+      },)
       expect(rendered_form).to include "Namespaced form partial."
     end
     it "should render form and filter inputs" do
@@ -452,7 +453,7 @@ class FormForGrid < Datagrid::Base
         "form .datagrid-filter[data-filter=category][data-type=string] label" => "Category",
         "form .datagrid-filter input[name='form_for_grid[category]'][value=hello]" => 1,
         "form .datagrid-actions input[name=commit][value=Search]" => 1,
-        "form .datagrid-actions a.datagrid-reset[href='/location']" => 1
+        "form .datagrid-actions a.datagrid-reset[href='/location']" => 1,
       )
     end
     it "should support html classes for grid class with namespace" do
@@ -465,7 +466,7 @@ class TestGrid < Datagrid::Base
       expect(subject.datagrid_form_for(Ns22::TestGrid.new, url: "grid")).to match_css_pattern(
         "form.datagrid-form" => 1,
         "form.datagrid-form label[for=ns22_test_grid_id]" => 1,
-        "form.datagrid-form input#ns22_test_grid_id[name='ns22_test_grid[id]']" => 1
+        "form.datagrid-form input#ns22_test_grid_id[name='ns22_test_grid[id]']" => 1,
       )
     end
 
@@ -478,7 +479,7 @@ def param_name
         end
       end
       expect(subject.datagrid_form_for(ParamNameGrid81.new, url: "/grid")).to match_css_pattern(
-        "form.datagrid-form input[name='g[id]']" => 1
+        "form.datagrid-form input[name='g[id]']" => 1,
       )
     end
 
@@ -492,9 +493,9 @@ def param_name
         end
       end
       rendered_form = subject.datagrid_form_for(PartialDefaultGrid.new, {
-                                                  url: "",
-                                                  partials: "custom_form"
-                                                })
+        url: "",
+        partials: "custom_form",
+      },)
       expect(rendered_form).to include "form_partial_test"
     end
   end
diff --git a/spec/datagrid/scaffold_spec.rb b/spec/datagrid/scaffold_spec.rb
index 3e37561..0fcf45e 100644
--- a/spec/datagrid/scaffold_spec.rb
+++ b/spec/datagrid/scaffold_spec.rb
@@ -26,21 +26,21 @@
 
   describe ".controller_code" do
     it "works" do
-      expect(subject.controller_code).to eq(<<-RUBY)
-class UsersController < ApplicationController
-  def index
-    @grid = UsersGrid.new(grid_params) do |scope|
-      scope.page(params[:page])
-    end
-  end
-
-  protected
-
-  def grid_params
-    params.fetch(:users_grid, {}).permit!
-  end
-end
-RUBY
+      expect(subject.controller_code).to eq(<<~RUBY)
+        class UsersController < ApplicationController
+          def index
+            @grid = UsersGrid.new(grid_params) do |scope|
+              scope.page(params[:page])
+            end
+          end
+
+          protected
+
+          def grid_params
+            params.fetch(:users_grid, {}).permit!
+          end
+        end
+      RUBY
     end
   end
 end
diff --git a/spec/datagrid_spec.rb b/spec/datagrid_spec.rb
index f3fdea6..881cb57 100644
--- a/spec/datagrid_spec.rb
+++ b/spec/datagrid_spec.rb
@@ -16,13 +16,13 @@
       name: "Star",
       category: "first",
       disabled: false,
-      confirmed: false
+      confirmed: false,
     )
   end
 
   let!(:entry) do
     Entry.create!(
-      group: group, name: "Star", disabled: false, confirmed: false, category: "first"
+      group: group, name: "Star", disabled: false, confirmed: false, category: "first",
     )
   end
 
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index b8cb418..63df808 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -39,23 +39,23 @@ class DatagridTest < Rails::Application
 else
   begin
     Mongoid.load_configuration({
-                                 "clients" =>
-                                 {
-                                   "default" =>
-                                   {
-                                     "hosts" => ["localhost:27017"],
-                                     "database" => "datagrid_mongoid",
-                                     options: {
-                                       max_read_retries: 0,
-                                       retry_reads: false,
-                                       connect_timeout: 2,
-                                       wait_queue_timeout: 2,
-                                       server_selection_timeout: 2,
-                                       socket_timeout: 1
-                                     }
-                                   }
-                                 }
-                               })
+      "clients" =>
+      {
+        "default" =>
+        {
+          "hosts" => ["localhost:27017"],
+          "database" => "datagrid_mongoid",
+          options: {
+            max_read_retries: 0,
+            retry_reads: false,
+            connect_timeout: 2,
+            wait_queue_timeout: 2,
+            server_selection_timeout: 2,
+            socket_timeout: 1,
+          },
+        },
+      },
+    })
 
     Mongoid.client(:default).collections # check mongo connection
 
@@ -94,9 +94,9 @@ class DatagridTest < Rails::Application
 
 def action_view_template
   context = ActionView::LookupContext.new([
-                                            File.expand_path("../app/views", __dir__),
-                                            File.expand_path("support/test_partials", __dir__)
-                                          ], {})
+    File.expand_path("../app/views", __dir__),
+    File.expand_path("support/test_partials", __dir__),
+  ], {},)
   Datagrid::Engine.extend_modules
   template = ActionView::Base.with_empty_template_cache.new(context, {}, ActionController::Base.new)
   allow(template).to receive(:protect_against_forgery?).and_return(false)
diff --git a/spec/support/mongoid.rb b/spec/support/mongoid.rb
index 1fa6ec8..68625a4 100644
--- a/spec/support/mongoid.rb
+++ b/spec/support/mongoid.rb
@@ -17,7 +17,6 @@ class MongoidEntry
 end
 
 class MongoidGrid < Datagrid::Base
-
   scope do
     MongoidEntry
   end

From 4c91827542657d88fbc984e78a4683ff2548ce08 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 9 Nov 2024 12:28:27 +0100
Subject: [PATCH 060/157] Rework class attribute as method

---
 lib/datagrid/filters/base_filter.rb             | 6 ++++--
 lib/datagrid/filters/boolean_filter.rb          | 4 +++-
 lib/datagrid/filters/date_filter.rb             | 4 +++-
 lib/datagrid/filters/date_time_filter.rb        | 4 +++-
 lib/datagrid/filters/dynamic_filter.rb          | 6 ++++--
 lib/datagrid/filters/enum_filter.rb             | 6 ++++--
 lib/datagrid/filters/extended_boolean_filter.rb | 6 ++++--
 lib/datagrid/filters/float_filter.rb            | 4 +++-
 lib/datagrid/filters/integer_filter.rb          | 4 +++-
 9 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index 69e7854..262aed5 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -11,8 +11,6 @@ class FilteringError < StandardError
 module Datagrid
   module Filters
     class BaseFilter
-      class_attribute :default_input_options, instance_writer: false, default: { type: "text" }
-
       attr_accessor :grid_class, :options, :block, :name
 
       def initialize(grid_class, name, options = {}, &block)
@@ -26,6 +24,10 @@ def parse(value)
         raise NotImplementedError, "#parse(value) suppose to be overwritten"
       end
 
+      def default_input_options
+        { type: "text" }
+      end
+
       def unapplicable_value?(value)
         value.nil? ? !allow_nil? : value.blank? && !allow_blank?
       end
diff --git a/lib/datagrid/filters/boolean_filter.rb b/lib/datagrid/filters/boolean_filter.rb
index 72b6d15..6c16209 100644
--- a/lib/datagrid/filters/boolean_filter.rb
+++ b/lib/datagrid/filters/boolean_filter.rb
@@ -5,7 +5,9 @@
 module Datagrid
   module Filters
     class BooleanFilter < Datagrid::Filters::BaseFilter
-      self.default_input_options = { type: "checkbox" }
+      def default_input_options
+        { **super, type: "checkbox" }
+      end
 
       def parse(value)
         Datagrid::Utils.booleanize(value)
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index 44bd0c5..9793698 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -7,7 +7,9 @@ module Filters
     class DateFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
-      self.default_input_options = { type: "date" }
+      def default_input_options
+        { **super, type: "date" }
+      end
 
       def apply(grid_object, scope, value)
         value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
diff --git a/lib/datagrid/filters/date_time_filter.rb b/lib/datagrid/filters/date_time_filter.rb
index 09d2bed..55e4e44 100644
--- a/lib/datagrid/filters/date_time_filter.rb
+++ b/lib/datagrid/filters/date_time_filter.rb
@@ -7,7 +7,9 @@ module Filters
     class DateTimeFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
-      self.default_input_options = { type: "datetime-local" }
+      def default_input_options
+        { **super, type: "datetime-local" }
+      end
 
       def parse(value)
         Datagrid::Utils.parse_datetime(value)
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index c9bb9be..8ac9d2e 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -19,8 +19,6 @@ class DynamicFilter < Datagrid::Filters::BaseFilter
       ].freeze
       AVAILABLE_OPERATIONS = %w[= =~ >= <=].freeze
 
-      self.default_input_options = {}
-
       def initialize(*)
         super
         options[:select] ||= default_select
@@ -30,6 +28,10 @@ def initialize(*)
         options[:include_blank] = false
       end
 
+      def default_input_options
+        {**super, type: nil}
+      end
+
       def parse_values(filter)
         filter ? FilterValue.new(grid_class, filter) : nil
       end
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 7cacacb..faf7bd4 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -7,8 +7,6 @@ module Filters
     class EnumFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::SelectOptions
 
-      self.default_input_options = { type: "select" }
-
       def initialize(*args)
         super
         options[:multiple] = true if enum_checkboxes?
@@ -21,6 +19,10 @@ def parse(value)
         value
       end
 
+      def default_input_options
+        { **super, type: "select" }
+      end
+
       def strict
         options[:strict]
       end
diff --git a/lib/datagrid/filters/extended_boolean_filter.rb b/lib/datagrid/filters/extended_boolean_filter.rb
index b8a9b29..ad8efa8 100644
--- a/lib/datagrid/filters/extended_boolean_filter.rb
+++ b/lib/datagrid/filters/extended_boolean_filter.rb
@@ -9,8 +9,6 @@ class ExtendedBooleanFilter < Datagrid::Filters::EnumFilter
       TRUTH_VALUES = [true, "true", "y", "yes"].freeze
       FALSE_VALUES = [false, "false", "n", "no"].freeze
 
-      self.default_input_options = { type: "select" }
-
       def initialize(report, attribute, options = {}, &block)
         options[:select] = -> { boolean_select }
         super
@@ -21,6 +19,10 @@ def execute(value, scope, grid_object)
         super
       end
 
+      def default_input_options
+        { **super, type: "select" }
+      end
+
       def parse(value)
         value = value.downcase if value.is_a?(String)
         case value
diff --git a/lib/datagrid/filters/float_filter.rb b/lib/datagrid/filters/float_filter.rb
index 4cafa9f..2c9b164 100644
--- a/lib/datagrid/filters/float_filter.rb
+++ b/lib/datagrid/filters/float_filter.rb
@@ -6,7 +6,9 @@ module Filters
     class FloatFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
-      self.default_input_options = { type: "number", step: "any" }
+      def default_input_options
+        { **super, type: "number", step: "any" }
+      end
 
       def parse(value)
         return nil if value.blank?
diff --git a/lib/datagrid/filters/integer_filter.rb b/lib/datagrid/filters/integer_filter.rb
index 3029df6..79ff5f8 100644
--- a/lib/datagrid/filters/integer_filter.rb
+++ b/lib/datagrid/filters/integer_filter.rb
@@ -7,7 +7,9 @@ module Filters
     class IntegerFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::RangedFilter
 
-      self.default_input_options = { type: "number", step: "1" }
+      def default_input_options
+        { **super, type: "number", step: "1" }
+      end
 
       def parse(value)
         return nil if value.blank?

From efd092eaf8a2c4d026e2359930f1929b706303f1 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 9 Nov 2024 12:50:12 +0100
Subject: [PATCH 061/157] Simplify enum checkboxes partial

---
 app/views/datagrid/_enum_checkboxes.html.erb | 2 +-
 lib/datagrid/filters/enum_filter.rb          | 7 ++++++-
 lib/datagrid/form_builder.rb                 | 8 ++++++--
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
index 2d46139..f225cc6 100644
--- a/app/views/datagrid/_enum_checkboxes.html.erb
+++ b/app/views/datagrid/_enum_checkboxes.html.erb
@@ -6,7 +6,7 @@ You can add indent if whitespace doesn't matter for you
 <%- elements.each do |value, text, checked| -%>
 <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
 <%= form.datagrid_label(filter.name, **options, for: id, class: 'datagrid-enum-checkbox-label') do -%>
-<%= form.datagrid_filter_input(filter.name, type: :checkbox, multiple: true, id: id, checked: checked, include_hidden: false, value: value.to_s) -%>
+<%= form.datagrid_filter_input(filter.name, id: id, value: value) -%>
 <%= text -%>
 <%- end -%>
 <%- end -%>
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index faf7bd4..0c9e15e 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -20,7 +20,12 @@ def parse(value)
       end
 
       def default_input_options
-        { **super, type: "select" }
+        {
+          **super,
+          type: enum_checkboxes? ? "checkbox" : "select",
+          multiple: multiple?,
+          include_hidden: enum_checkboxes? ? false : nil,
+        }
       end
 
       def strict
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 34a921a..a53b79a 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -46,7 +46,11 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       when :textarea
         text_area filter.name, value: object.filter_value_as_string(filter), **options, &block
       when :checkbox
-        check_box filter.name, options, options.fetch(:value, 1)
+        value = options.fetch(:value, 1).to_s
+        if filter.enum_checkboxes? && enum_checkbox_checked?(filter, value)
+          options = {checked: true, **options}
+        end
+        check_box filter.name, options, value
       when :hidden
         hidden_field filter.name, **options
       when :number
@@ -113,7 +117,7 @@ def datagrid_enum_filter(filter, options = {}, &block)
     end
 
     def enum_checkbox_checked?(filter, option_value)
-      current_value = object.public_send(filter.name)
+      current_value = object.filter_value(filter)
       if current_value.respond_to?(:include?)
         # Typecast everything to string
         # to remove difference between String and Symbol

From d6695cfb5160819423d67fa784ac11039b2e0eeb Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 9 Nov 2024 19:21:57 +0100
Subject: [PATCH 062/157] Rework a test

---
 spec/datagrid/helper_spec.rb | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 771e2fa..c900e31 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -447,14 +447,19 @@ class FormForGrid < Datagrid::Base
         filter(:category, :string)
       end
       object = FormForGrid.new(category: "hello")
-      expect(subject.datagrid_form_for(object, url: "/grid")).to match_css_pattern(
-        "form.datagrid-form[action='/grid']" => 1,
-        "form input[name=utf8]" => 1,
-        "form .datagrid-filter[data-filter=category][data-type=string] label" => "Category",
-        "form .datagrid-filter input[name='form_for_grid[category]'][value=hello]" => 1,
-        "form .datagrid-actions input[name=commit][value=Search]" => 1,
-        "form .datagrid-actions a.datagrid-reset[href='/location']" => 1,
-      )
+      expect(subject.datagrid_form_for(object, url: "/grid")).to equal_to_dom(<<~HTML)
+ <form class="datagrid-form" id="new_form_for_grid" action="/grid" accept-charset="UTF-8" method="get">
+   <input name="utf8" type="hidden" value="&#x2713;" autocomplete="off" />
+      <div class="datagrid-filter" data-filter="category" data-type="string">
+        <label for="form_for_grid_category">Category</label>
+        <input value="hello" type="text" name="form_for_grid[category]" id="form_for_grid_category" />
+      </div>
+  <div class="datagrid-actions">
+    <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
+    <a class="datagrid-reset" href="/location">Reset</a>
+  </div>
+</form>
+      HTML
     end
     it "should support html classes for grid class with namespace" do
       module ::Ns22

From b273387e59e30903a33dfc51b8d9d1bfa0d9ad62 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 9 Nov 2024 21:55:18 +0100
Subject: [PATCH 063/157] Migrate from form_for to form_with

---
 app/views/datagrid/_form.html.erb             |  2 +-
 lib/datagrid/filters.rb                       |  4 +-
 lib/datagrid/helper.rb                        | 22 ++++-
 lib/datagrid/renderer.rb                      | 14 ++-
 lib/datagrid/scaffold.rb                      |  2 +-
 spec/datagrid/form_builder_spec.rb            |  9 +-
 spec/datagrid/helper_spec.rb                  | 87 ++++++++++++++++++-
 .../client/datagrid/_form.html.erb            |  2 +-
 .../test_partials/custom_form/_form.html.erb  |  2 +-
 templates/index.html.erb                      |  2 +-
 version-2/Readme.markdown                     | 30 ++++++-
 version-2/views.diff                          |  4 +-
 12 files changed, 162 insertions(+), 18 deletions(-)

diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
index 9b41ff2..fc4f4ae 100644
--- a/app/views/datagrid/_form.html.erb
+++ b/app/views/datagrid/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
+<%= form_with model: grid, html: {class: 'datagrid-form'}, scope: grid.param_name, method: :get, **options do |f| %>
   <% grid.filters.each do |filter| %>
     <div class="datagrid-filter" data-filter="<%= filter.name %>" data-type="<%= filter.type %>">
       <%= f.datagrid_label filter %>
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 6ed0047..c5fee88 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -83,9 +83,9 @@ def filter_by_name(attribute)
       # * <tt>:allow_nil</tt> - determines if the value can be nil
       # * <tt>:allow_blank</tt> - determines if the value can be blank
       # * <tt>:before</tt> - determines the position of this filter,
-      #   by adding it before the filter passed here (when using datagrid_form_for helper)
+      #   by adding it before the filter passed here (when using datagrid_form_with helper)
       # * <tt>:after</tt> - determines the position of this filter,
-      #   by adding it after the filter passed here (when using datagrid_form_for helper)
+      #   by adding it after the filter passed here (when using datagrid_form_with helper)
       # * <tt>:dummy</tt> - if true, this filter will not be applied automatically
       #   and will be just displayed in form. In case you may want to apply it manually.
       # * <tt>:if</tt> - specify the condition when the filter can be dislayed and used.
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index 76a2551..a7496db 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -97,16 +97,36 @@ def datagrid_order_for(grid, column, options = {})
       datagrid_renderer.order_for(grid, column, options)
     end
 
-    # Renders HTML for for grid with all filters inputs and lables defined in it
+    # Renders HTML for grid with all filters inputs and labels defined in it
+    #
+    # Supported options:
+    #
+    # * <tt>:partials</tt> - Path for form partial lookup.
+    #   Default: 'datagrid' results in using `app/views/datagrid/` partials.
+    #   Example: 'datagrid_admin' results in using `app/views/datagrid_admin` partials.
+    # * <tt>:model</tt> - Datagrid object to be rendedred.
+    # * All options supported by Rails <tt>form_with</tt> helper
+    # @param grid [Datagrid] grid object
+    # @return [String] form HTML tag markup
+    def datagrid_form_with(**options)
+      if block_given?
+        raise ArgumentError, 'datagrid_form_with block argument is invalid. Use form_with instead.'
+      end
+      datagrid_renderer.form_with(**options)
+    end
+
+    # Renders HTML for grid with all filters inputs and labels defined in it
     #
     # Supported options:
     #
     # * <tt>:partials</tt> - Path for form partial lookup.
     #   Default: 'datagrid'.
     # * All options supported by Rails <tt>form_for</tt> helper
+    # @deprecated Use {#datagrid_form_with} instead.
     # @param grid [Datagrid] grid object
     # @return [String] form HTML tag markup
     def datagrid_form_for(grid, options = {})
+      Datagrid::Utils.warn_once("datagrid_form_for is deprecated if favor of datagrid_form_with.")
       datagrid_renderer.form_for(grid, options)
     end
 
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index 2253b01..be9ca2e 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -25,13 +25,23 @@ def form_for(grid, options = {})
       _render_partial("form", options[:partials], { grid: grid, options: options })
     end
 
+    def form_with(**options)
+      grid = options[:model]
+      if grid&.filters&.empty?
+        raise ArgumentError, "Grid has no available filters"
+      end
+      _render_partial("form", options[:partials], { grid: options[:model], options: options })
+    end
+
     def table(grid, assets, **options)
-      _render_partial("table", options[:partials],
+      _render_partial(
+        "table", options[:partials],
         {
           grid: grid,
           options: options,
           assets: assets,
-        },)
+        },
+      )
     end
 
     def header(grid, options = {})
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/scaffold.rb
index d0f69a0..6f8a310 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/scaffold.rb
@@ -128,7 +128,7 @@ def grid_params
 
     def view_code
       indent(<<~ERB)
-        <%= datagrid_form_for @grid, url: #{grid_route_name} %>
+        <%= datagrid_form_with model: @grid, url: #{grid_route_name} %>
 
         <%= #{pagination_helper_code} %>
         <%= #{table_helper_code} %>
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index e8bfee9..cfc5dfe 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -16,7 +16,14 @@ class MyTemplate
     action_view_template
   end
 
-  let(:view) { ActionView::Helpers::FormBuilder.new(:report, _grid, template, view_options) }
+  let(:view) do
+    ActionView::Helpers::FormBuilder.new(
+      :report, _grid, template,
+      skip_default_ids: false,
+      **view_options,
+    )
+  end
+
   let(:view_options) { {} }
 
   describe ".datagrid_filter" do
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index c900e31..566b7b6 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -20,6 +20,8 @@
     allow(subject).to receive(:url_for) do |options|
       options.is_a?(String) ? options : ["/location", options.to_param.presence].compact.join("?")
     end
+    # Rails default since 5.x
+    ActionView::Helpers::FormHelper.form_with_generates_ids = true
   end
 
   let(:group) { Group.create!(name: "Pop") }
@@ -433,7 +435,14 @@ class OrderedGrid < Datagrid::Base
       HTML
     end
   end
+
   describe ".datagrid_form_for" do
+    around(:each) do |e|
+      silence_warnings do
+        e.run
+      end
+    end
+
     it "returns namespaced partial if partials options is passed" do
       rendered_form = subject.datagrid_form_for(grid, {
         url: "",
@@ -448,7 +457,7 @@ class FormForGrid < Datagrid::Base
       end
       object = FormForGrid.new(category: "hello")
       expect(subject.datagrid_form_for(object, url: "/grid")).to equal_to_dom(<<~HTML)
- <form class="datagrid-form" id="new_form_for_grid" action="/grid" accept-charset="UTF-8" method="get">
+ <form class="datagrid-form" action="/grid" accept-charset="UTF-8" data-remote="true" method="get">
    <input name="utf8" type="hidden" value="&#x2713;" autocomplete="off" />
       <div class="datagrid-filter" data-filter="category" data-type="string">
         <label for="form_for_grid_category">Category</label>
@@ -505,6 +514,82 @@ def param_name
     end
   end
 
+
+  describe ".datagrid_form_with" do
+    it "returns namespaced partial if partials options is passed" do
+      rendered_form = subject.datagrid_form_with(
+        model: grid,
+        url: "",
+        partials: "client/datagrid",
+      )
+      expect(rendered_form).to include "Namespaced form partial."
+    end
+    it "should render form and filter inputs" do
+      class FormWithGrid < Datagrid::Base
+        scope { Entry }
+        filter(:category, :string)
+      end
+      object = FormWithGrid.new(category: "hello")
+      expect(subject.datagrid_form_with(model: object, url: "/grid")).to equal_to_dom(<<~HTML)
+ <form class="datagrid-form" action="/grid" accept-charset="UTF-8" data-remote="true" method="get">
+   <input name="utf8" type="hidden" value="&#x2713;" autocomplete="off" />
+      <div class="datagrid-filter" data-filter="category" data-type="string">
+        <label for="form_with_grid_category">Category</label>
+        <input value="hello" type="text" name="form_with_grid[category]" id="form_with_grid_category" />
+      </div>
+  <div class="datagrid-actions">
+    <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
+    <a class="datagrid-reset" href="/location">Reset</a>
+  </div>
+</form>
+      HTML
+    end
+    it "should support html classes for grid class with namespace" do
+      module ::Ns23
+        class TestGrid < Datagrid::Base
+          scope { Entry }
+          filter(:id)
+        end
+      end
+      expect(subject.datagrid_form_with(model: Ns23::TestGrid.new, url: "grid")).to match_css_pattern(
+        "form.datagrid-form" => 1,
+        "form.datagrid-form label[for=ns23_test_grid_id]" => 1,
+        "form.datagrid-form input#ns23_test_grid_id[name='ns23_test_grid[id]']" => 1,
+      )
+    end
+
+    it "should have overridable param_name method" do
+      class ParamNameGrid82 < Datagrid::Base
+        scope { Entry }
+        filter(:id)
+        def param_name
+          "g"
+        end
+      end
+      expect(subject.datagrid_form_with(model: ParamNameGrid82.new, url: "/grid")).to match_css_pattern(
+        "form.datagrid-form input[name='g[id]']" => 1,
+      )
+    end
+
+    it "takes default partials if custom doesn't exist" do
+      class PartialDefaultGrid < Datagrid::Base
+        scope { Entry }
+        filter(:id, :integer, range: true)
+        filter(:group_id, :enum, multiple: true, checkboxes: true, select: [1, 2])
+        def param_name
+          "g"
+        end
+      end
+      rendered_form = subject.datagrid_form_with(
+        model: PartialDefaultGrid.new,
+        url: "",
+        partials: "custom_form",
+      )
+      expect(rendered_form).to include "form_partial_test"
+    end
+  end
+
+
   describe ".datagrid_row" do
     let(:grid) do
       test_report do
diff --git a/spec/support/test_partials/client/datagrid/_form.html.erb b/spec/support/test_partials/client/datagrid/_form.html.erb
index 29835a1..8a1f5a8 100644
--- a/spec/support/test_partials/client/datagrid/_form.html.erb
+++ b/spec/support/test_partials/client/datagrid/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_for grid, options do |f| -%>
+<%= form_with model: grid, method: :get, **options do |f| -%>
   <p>Namespaced form partial.</p>
   <% grid.filters.each do |filter| %>
     <div class="datagrid-filter filter">
diff --git a/spec/support/test_partials/custom_form/_form.html.erb b/spec/support/test_partials/custom_form/_form.html.erb
index 8b5351e..15077d2 100644
--- a/spec/support/test_partials/custom_form/_form.html.erb
+++ b/spec/support/test_partials/custom_form/_form.html.erb
@@ -1,4 +1,4 @@
-<%= form_for grid, options do |f| -%>
+<%= form_with model: grid, method: :get, **options do |f| -%>
   <p>form_partial_test</p>
   <% grid.filters.each do |filter| %>
     <%= f.datagrid_label filter %>
diff --git a/templates/index.html.erb b/templates/index.html.erb
index 105b49f..133754b 100644
--- a/templates/index.html.erb
+++ b/templates/index.html.erb
@@ -1,4 +1,4 @@
-<%%= datagrid_form_for @grid, url: <%= grid_route_name %> %>
+<%%= datagrid_form_with model: @grid, url: <%= grid_route_name %> %>
 
 <%%= <%=pagination_helper_code%> %>
 <%%= <%=table_helper_code%> %>
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index d9522fe..7bdc956 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -9,18 +9,34 @@ Version 2 addresses all that evolution.
 
 List of things introduces:
 
+1. Use `form_with` instead of `form_for`.
 1. Ruby infinite ranges for range filters.
 1. Modern modular CSS classes.
 1. HTML5 input types: number, date, datetime-local.
-1. Use Hash instead of Array for multiparameters attirubtes
-   to avoid [input names collision restriction](https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary)
-1. Native Rails Engines:
-   while supported, the library was not initially designed for it.
+1. Use Hash instead of Array for multiparameter attirubtes.
+1. Native Rails Engines.
+   * while supported, the library was not initially designed for it.
 1. HTML5 data attributes
 1. Inherit `Datagrid::Base` instead of `include Datagrid`
 1. `ApplicationGrid` is recommended base class instead of `BaseGrid`
 1. Remove SASS dependency
 
+## Use form\_with
+
+Rails [deprecates form\_for in favor of form\_with](https://guides.rubyonrails.org/form_helpers.html#using-form-tag-and-form-for).
+
+`datagrid_form_for` is now depreacted if favor of `datagrid_form_with`.
+However, `datagrid_form_for` would also use Rails `form_with` because they share the same view partial.
+
+TODO: update the wiki
+
+``` ruby
+# V1
+datagrid_form_for(@users_grid, url: users_path)
+# V2
+datagrid_form_with(model: @users_grid, url: users_path)
+```
+
 ## Infinite Ranges for range filters
 
 Ruby supports infinite ranges now,
@@ -345,3 +361,9 @@ as much UI as possible for user modification.
 
 Here is a complete [diff for built-in partials between V1 and V2](./views.diff)
 
+## Remove SASS dependency
+
+SASS is no longer a default choice when starting a rails project.
+Version 2 makes it more flexible by avoiding the dependency on any particular CSS framework.
+
+Inspect [a new built-in CSS file](../app/assets/datagrid.css).
diff --git a/version-2/views.diff b/version-2/views.diff
index ed70538..fc4123b 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -1,5 +1,5 @@
 diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
-index 9f48319..2d46139 100644
+index 9f48319..f225cc6 100644
 --- a/app/views/datagrid/_enum_checkboxes.html.erb
 +++ b/app/views/datagrid/_enum_checkboxes.html.erb
 @@ -2,10 +2,12 @@
@@ -12,7 +12,7 @@ index 9f48319..2d46139 100644
 -<%= form.label filter.name, options.merge(for: id) do -%>
 -<%= form.check_box(filter.name, {multiple: true, id: id, checked: checked, include_hidden: false}, value.to_s, nil) -%>
 +<%= form.datagrid_label(filter.name, **options, for: id, class: 'datagrid-enum-checkbox-label') do -%>
-+<%= form.datagrid_filter_input(filter.name, type: :checkbox, multiple: true, id: id, checked: checked, include_hidden: false, value: value.to_s) -%>
++<%= form.datagrid_filter_input(filter.name, id: id, value: value) -%>
  <%= text -%>
  <%- end -%>
  <%- end -%>

From 77017a7025fc838f37ac1bbe38aa295770a7e4ff Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 9 Nov 2024 21:57:10 +0100
Subject: [PATCH 064/157] Update views diff

---
 version-2/views.diff | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index fc4123b..e0a83c4 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -18,12 +18,12 @@ index 9f48319..f225cc6 100644
  <%- end -%>
 +</div>
 diff --git a/app/views/datagrid/_form.html.erb b/app/views/datagrid/_form.html.erb
-index 7e175c1..9b41ff2 100644
+index 7e175c1..fc4f4ae 100644
 --- a/app/views/datagrid/_form.html.erb
 +++ b/app/views/datagrid/_form.html.erb
 @@ -1,12 +1,12 @@
 -<%= form_for grid, options do |f| -%>
-+<%= form_for grid, html: {class: 'datagrid-form'}, **options do |f| -%>
++<%= form_with model: grid, html: {class: 'datagrid-form'}, scope: grid.param_name, method: :get, **options do |f| %>
    <% grid.filters.each do |filter| %>
 -    <div class="datagrid-filter filter">
 +    <div class="datagrid-filter" data-filter="<%= filter.name %>" data-type="<%= filter.type %>">

From cbcb8517fa94ebeb0bd7db6cc41029bfd3254b4d Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 09:17:21 +0100
Subject: [PATCH 065/157] Support rails from 7.0 and ruby from 3.1

---
 .github/workflows/ci.yml | 10 +++++-----
 Gemfile                  |  2 +-
 datagrid.gemspec         |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b235eb6..247a5aa 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,11 +8,11 @@ jobs:
       fail-fast: false
       matrix:
         # To keep matrix size down, only test highest and lowest rubies.
-        ruby: ["2.7", "3.3"]
-        rails: ["6.1", "7.0", "7.1", "7.2"]
-        exclude:
-          - ruby: "2.7"
-            rails: "7.2"
+        ruby: ["3.1", "3.3"]
+        rails: ["7.0", "7.1", "7.2", "8.0"]
+        # exclude:
+          # - ruby: "2.7"
+            # rails: "7.2"
     name: Ruby ${{ matrix.ruby }}, Rails ${{ matrix.rails }}
     runs-on: ubuntu-latest
     env:
diff --git a/Gemfile b/Gemfile
index c0c942e..55d01f1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -16,6 +16,7 @@ group :development do
   gem "rspec"
   gem "sequel"
   gem "sqlite3", "~> 1.7.0"
+  gem "rubocop", "~> 1.68"
 
   group :mongo do
     gem "bson"
@@ -23,4 +24,3 @@ group :development do
   end
 end
 
-gem "rubocop", "~> 1.68", group: :development
diff --git a/datagrid.gemspec b/datagrid.gemspec
index a5e48e3..1e9444c 100644
--- a/datagrid.gemspec
+++ b/datagrid.gemspec
@@ -33,5 +33,5 @@ Gem::Specification.new do |s|
     "rubygems_mfa_required" => "true",
   }
 
-  s.add_dependency "railties", ">= 6.1"
+  s.add_dependency "railties", ">= 7.0"
 end

From 296cc487df2743252119d7f7a9c512dd1d873730 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 09:19:44 +0100
Subject: [PATCH 066/157] Fix build

---
 spec/spec_helper.rb | 1 -
 1 file changed, 1 deletion(-)

diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 63df808..617d4ab 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -20,7 +20,6 @@
 require "mongoid"
 
 require "datagrid"
-require "debug"
 require "rspec"
 require "logger"
 

From 9673a83858c0cd32f6adbe012353b5a12402c016 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 09:37:16 +0100
Subject: [PATCH 067/157] Support TimeWithZone as an argument of datetime
 filter

---
 lib/datagrid/utils.rb              | 3 +++
 spec/datagrid/form_builder_spec.rb | 7 +++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index 43152ce..56a85a7 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -93,6 +93,9 @@ def parse_date(value)
       def parse_datetime(value)
         return nil if value.blank?
         return value if value.is_a?(Range)
+        if defined?(ActiveSupport::TimeWithZone) && value.is_a?(ActiveSupport::TimeWithZone)
+          return value
+        end
 
         if value.is_a?(String)
           Array(Datagrid.configuration.datetime_formats).each do |format|
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index cfc5dfe..edc871f 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -132,14 +132,17 @@ class MyTemplate
       context "datetime filter type is text" do
         let(:_filter) { :created_at }
         let(:_grid) do
-          test_report(created_at: Time.new(2024, 1, 1, 9, 25, 15)) do
+          created_at = ActiveSupport::TimeZone['UTC'].local(
+            2024, 1, 1, 9, 25, 15
+          )
+          test_report(created_at: ) do
             scope { Entry }
             filter(:created_at, :datetime, input_options: { type: "text" })
           end
         end
         it {
           should equal_to_dom(
-            '<input type="text" value="2024-01-01 09:25:15 +0100"
+            '<input type="text" value="2024-01-01 09:25:15 UTC"
               name="report[created_at]" id="report_created_at"/>',
           )
         }

From 30b89a8f2b440a7f54256a29efe74d7f6fd47e70 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 09:37:44 +0100
Subject: [PATCH 068/157] Support ruby 3.0

---
 .github/workflows/ci.yml | 2 +-
 datagrid.gemspec         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 247a5aa..f0304cc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,7 +8,7 @@ jobs:
       fail-fast: false
       matrix:
         # To keep matrix size down, only test highest and lowest rubies.
-        ruby: ["3.1", "3.3"]
+        ruby: ["3.0", "3.3"]
         rails: ["7.0", "7.1", "7.2", "8.0"]
         # exclude:
           # - ruby: "2.7"
diff --git a/datagrid.gemspec b/datagrid.gemspec
index 1e9444c..e6904a7 100644
--- a/datagrid.gemspec
+++ b/datagrid.gemspec
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
   s.files += `git ls-files | grep -E '^(app|lib|templates)'`.split("\n")
   s.homepage = "https://github.com/bogdan/datagrid"
   s.licenses = ["MIT"]
-  s.required_ruby_version = Gem::Requirement.new(">= 2.7")
+  s.required_ruby_version = Gem::Requirement.new(">= 3.0")
   s.metadata = {
     "homepage_uri" => s.homepage,
     "bug_tracker_uri" => "#{s.homepage}/issues",

From 9c1ad90211d5a49d08f241b71247bce522317b62 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 09:40:00 +0100
Subject: [PATCH 069/157] Fix ruby 3.0

---
 spec/datagrid/form_builder_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index edc871f..6279457 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -135,7 +135,7 @@ class MyTemplate
           created_at = ActiveSupport::TimeZone['UTC'].local(
             2024, 1, 1, 9, 25, 15
           )
-          test_report(created_at: ) do
+          test_report(created_at: created_at) do
             scope { Entry }
             filter(:created_at, :datetime, input_options: { type: "text" })
           end

From ade8780f839a5f0ea1a5ef53776cd926e533bed1 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 09:40:44 +0100
Subject: [PATCH 070/157] Rails 8 gemfile

---
 gemfiles/rails_8.0.gemfile | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 gemfiles/rails_8.0.gemfile

diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile
new file mode 100644
index 0000000..c18e1a6
--- /dev/null
+++ b/gemfiles/rails_8.0.gemfile
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+# This file was generated by Appraisal
+
+source "https://rubygems.org"
+
+group :development do
+  gem "appraisal"
+  gem "bump"
+  gem "csv"
+  gem "nokogiri"
+  gem "pry-byebug"
+  gem "rails", "~> 8.0.0"
+  gem "rspec"
+  gem "sequel"
+  gem "sqlite3", "~> 2.0.0"
+
+  group :mongo do
+    gem "bson"
+    gem "mongoid", github: "mongodb/mongoid"
+  end
+end
+
+gemspec path: "../"

From b66723834cf8a80be7d27566574965dfd8be4749 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 09:43:26 +0100
Subject: [PATCH 071/157] Fix build ruby 3.0 rails 7.2 8.0

---
 .github/workflows/ci.yml   | 8 +++++---
 gemfiles/rails_8.0.gemfile | 2 +-
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f0304cc..0913239 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -10,9 +10,11 @@ jobs:
         # To keep matrix size down, only test highest and lowest rubies.
         ruby: ["3.0", "3.3"]
         rails: ["7.0", "7.1", "7.2", "8.0"]
-        # exclude:
-          # - ruby: "2.7"
-            # rails: "7.2"
+        exclude:
+          - ruby: "3.0"
+            rails: "7.2"
+          - ruby: "3.0"
+            rails: "8.0"
     name: Ruby ${{ matrix.ruby }}, Rails ${{ matrix.rails }}
     runs-on: ubuntu-latest
     env:
diff --git a/gemfiles/rails_8.0.gemfile b/gemfiles/rails_8.0.gemfile
index c18e1a6..5185cba 100644
--- a/gemfiles/rails_8.0.gemfile
+++ b/gemfiles/rails_8.0.gemfile
@@ -13,7 +13,7 @@ group :development do
   gem "rails", "~> 8.0.0"
   gem "rspec"
   gem "sequel"
-  gem "sqlite3", "~> 2.0.0"
+  gem "sqlite3", "~> 2.1.0"
 
   group :mongo do
     gem "bson"

From bcde3151c76ebda55585eb770bed3dbd17a2c3e5 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 09:54:58 +0100
Subject: [PATCH 072/157] Cleanup

---
 spec/datagrid/filters/date_filter_spec.rb    | 13 ++++---------
 spec/datagrid/filters/dynamic_filter_spec.rb |  4 ++--
 2 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 3aa08c9..72fa450 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -32,19 +32,14 @@
   end
 
   it "supports hash argument" do
-    e1 = Entry.create!(created_at: 7.days.ago)
-    e2 = Entry.create!(created_at: 4.days.ago)
-    e3 = Entry.create!(created_at: 1.day.ago)
-    from = 5.days.ago
-    to = 3.days.ago
-    report = test_report(created_at: { from: from, to: to }) do
+    report = test_report do
       scope { Entry }
       filter(:created_at, :date, range: true)
     end
+    from = 5.days.ago
+    to = 3.days.ago
+    report.created_at = { from: from, to: to }
     expect(report.created_at).to eq(from.to_date..to.to_date)
-    expect(report.assets).not_to include(e1)
-    expect(report.assets).to include(e2)
-    expect(report.assets).not_to include(e3)
     report.created_at = {}
     expect(report.created_at).to eq(nil)
     report.created_at = { from: nil, to: nil }
diff --git a/spec/datagrid/filters/dynamic_filter_spec.rb b/spec/datagrid/filters/dynamic_filter_spec.rb
index eae16f6..c658ff8 100644
--- a/spec/datagrid/filters/dynamic_filter_spec.rb
+++ b/spec/datagrid/filters/dynamic_filter_spec.rb
@@ -139,8 +139,8 @@
       end
     end
 
-    # expect(grid.assets).to_not include(Entry.create!(disabled: true))
-    # expect(grid.assets).to include(Entry.create!(disabled: false))
+    expect(grid.assets).to_not include(Entry.create!(disabled: true))
+    expect(grid.assets).to include(Entry.create!(disabled: false))
 
     grid.condition = [:group_id, ">=", 3]
     expect(grid.assets).to include(Entry.create!(disabled: true, group_id: 4))

From 0a2077bad4681bf93b3e4641a772705e8af23818 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 11:42:12 +0100
Subject: [PATCH 073/157] Expose datagrid-order-active-asc/desc classes

---
 app/views/datagrid/_head.html.erb              | 16 ++++++++++++----
 app/views/datagrid/_row.html.erb               | 13 ++++++++++++-
 lib/datagrid/helper.rb                         | 14 ++++++++++----
 spec/datagrid/helper_spec.rb                   | 18 ++++++++++++++++++
 .../client/datagrid/_head.html.erb             |  4 ++--
 .../client/datagrid/_row.html.erb              |  2 +-
 6 files changed, 55 insertions(+), 12 deletions(-)

diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index c3264bb..a1bbd51 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -1,8 +1,16 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <th class="<%= datagrid_column_classes(grid, column) %>" data-column="<%= column.name %>">
-      <%= column.header %>
-      <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
-    </th>
+    <%= content_tag(
+      :th,
+      safe_join([column.header, datagrid_order_for(grid, column, **options)]),
+      class: {
+        # Adding html clases based on condition
+        # Consider maintaining consistency with datagrid/rows partial
+        "datagrid-order-active-asc": grid.ordered_by?(column, false),
+        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
+        column.html_class => column.html_class.present?,
+      },
+      "data-column": column.name
+    ) %>
   <% end %>
 </tr>
diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
index 20f3ffb..340aea6 100644
--- a/app/views/datagrid/_row.html.erb
+++ b/app/views/datagrid/_row.html.erb
@@ -1,5 +1,16 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <td class="<%= datagrid_column_classes(grid, column) %>" data-column="<%= column.name %>"><%= datagrid_value(grid, column, asset) %></td>
+    <%= content_tag(
+      :td,
+      datagrid_value(grid, column, asset),
+      class: {
+        # Adding html clases based on condition
+        # Consider maintaining consistency with datagrid/head partial
+        "datagrid-order-active-asc": grid.ordered_by?(column, false),
+        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
+        column.html_class => column.html_class.present?,
+      },
+      "data-column": column.name
+    ) %>
   <% end %>
 </tr>
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index a7496db..d55b8e6 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -93,8 +93,9 @@ def datagrid_rows(grid, assets = grid.assets, **options, &block)
     #
     # * <tt>:partials</tt> - Path for partials lookup.
     #   Default: 'datagrid'.
-    def datagrid_order_for(grid, column, options = {})
-      datagrid_renderer.order_for(grid, column, options)
+    def datagrid_order_for(grid, column, order: true, **options)
+      return "" unless column.supports_order? && order
+      datagrid_renderer.order_for(grid, column, **options)
     end
 
     # Renders HTML for grid with all filters inputs and labels defined in it
@@ -170,10 +171,15 @@ def datagrid_renderer
     end
 
     def datagrid_column_classes(grid, column)
+      Datagrid::Utils.warn_once(<<~MSG)
+      datagrid_column_classes is deprecated. Assign necessary classes manually.
+      Correspond to default datagrid/rows partial for example.)
+      MSG
+      column = grid.column_by_name(column)
       order_class = if grid.ordered_by?(column)
-                      grid.descending ? "datagrid-order-active-desc" : "datagrid-order-active-asc"
+                      ["ordered", grid.descending ? "desc" : "asc"]
                     end
-      [order_class, column.html_class].compact.join(" ")
+      [column.name, order_class, column.options[:class]].compact.join(" ")
     end
   end
 end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 566b7b6..8313432 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -726,4 +726,22 @@ def param_name
       HTML
     end
   end
+
+  describe ".datagrid_column_classes" do
+    it "is deprecated" do
+      grid = test_report(order: :name, descending: true) do
+        scope { Entry }
+        column(:name)
+        column(:group_id, class: 'short-column')
+      end
+      silence_warnings do
+        expect(subject.send(:datagrid_column_classes, grid, :name)).to eq(
+          "name ordered desc"
+        )
+        expect(subject.send(:datagrid_column_classes, grid, :group_id)).to eq(
+          "group_id short-column"
+        )
+      end
+    end
+  end
 end
diff --git a/spec/support/test_partials/client/datagrid/_head.html.erb b/spec/support/test_partials/client/datagrid/_head.html.erb
index eba7f60..e22a008 100644
--- a/spec/support/test_partials/client/datagrid/_head.html.erb
+++ b/spec/support/test_partials/client/datagrid/_head.html.erb
@@ -1,9 +1,9 @@
 <tr>
   <p>Namespaced head partial.</p>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <th class="<%= datagrid_column_classes(grid, column) %>">
+    <th>
       <%= column.header %>
-      <%= datagrid_order_for(grid, column, options) if column.order && options[:order]%>
+      <%= datagrid_order_for(grid, column, **options) if column.order && options[:order]%>
     </th>
   <% end %>
 </tr>
diff --git a/spec/support/test_partials/client/datagrid/_row.html.erb b/spec/support/test_partials/client/datagrid/_row.html.erb
index b35da9b..3ec8df3 100644
--- a/spec/support/test_partials/client/datagrid/_row.html.erb
+++ b/spec/support/test_partials/client/datagrid/_row.html.erb
@@ -1,6 +1,6 @@
 <tr>
   <p>Namespaced row partial.</p>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_value(grid, column, asset) %></td>
+    <td><%= datagrid_value(grid, column, asset) %></td>
   <% end %>
 </tr>

From c2a8e83983e04e738366605024133e29161a0bf0 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 11:58:46 +0100
Subject: [PATCH 074/157] Deprecate datagrid_order_for and related partial

---
 app/views/datagrid/_head.html.erb             | 19 +++++++++++++++++--
 app/views/datagrid/_order_for.html.erb        | 10 +++++-----
 lib/datagrid/helper.rb                        |  6 +++++-
 lib/tasks/datagrid_tasks.rake                 |  2 ++
 spec/datagrid/helper_spec.rb                  | 13 +++++++------
 .../client/datagrid/_head.html.erb            |  1 -
 version-2/Readme.markdown                     |  7 +++++++
 7 files changed, 43 insertions(+), 15 deletions(-)

diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index a1bbd51..b355528 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -2,7 +2,6 @@
   <% grid.html_columns(*options[:columns]).each do |column| %>
     <%= content_tag(
       :th,
-      safe_join([column.header, datagrid_order_for(grid, column, **options)]),
       class: {
         # Adding html clases based on condition
         # Consider maintaining consistency with datagrid/rows partial
@@ -11,6 +10,22 @@
         column.html_class => column.html_class.present?,
       },
       "data-column": column.name
-    ) %>
+    ) do %>
+      <%= column.header %>
+      <% if column.supports_order? && options[:order] -%>
+        <div class="datagrid-order">
+          <%= link_to(
+            I18n.t("datagrid.table.order.asc"),
+            datagrid_order_path(grid, column, false),
+            class: "datagrid-order-control-asc"
+          ) %>
+          <%= link_to(
+            I18n.t("datagrid.table.order.desc"),
+            datagrid_order_path(grid, column, true),
+            class: "datagrid-order-control-desc"
+          ) %>
+        </div>
+      <% end -%>
+    <% end -%>
   <% end %>
 </tr>
diff --git a/app/views/datagrid/_order_for.html.erb b/app/views/datagrid/_order_for.html.erb
index 1c33c37..1545a8e 100644
--- a/app/views/datagrid/_order_for.html.erb
+++ b/app/views/datagrid/_order_for.html.erb
@@ -1,10 +1,10 @@
-<div class="datagrid-order">
+<div class="order">
   <%= link_to(
-      I18n.t("datagrid.table.order.asc"),
+      I18n.t("datagrid.table.order.asc").html_safe,
       datagrid_order_path(grid, column, false),
-      class: "datagrid-order-control-asc") %>
+      class: "asc") %>
   <%= link_to(
-      I18n.t("datagrid.table.order.desc"),
+      I18n.t("datagrid.table.order.desc").html_safe,
       datagrid_order_path(grid, column, true),
-      class: "datagrid-order-control-desc") %>
+      class: "desc") %>
 </div>
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index d55b8e6..be58a10 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -94,7 +94,11 @@ def datagrid_rows(grid, assets = grid.assets, **options, &block)
     # * <tt>:partials</tt> - Path for partials lookup.
     #   Default: 'datagrid'.
     def datagrid_order_for(grid, column, order: true, **options)
-      return "" unless column.supports_order? && order
+      Datagrid::Utils.warn_once(<<~MSG)
+        datagrid_order_for is deprecated.
+        Put necessary code inline inside datagrid/head partial.
+        See built-in partial for example.
+      MSG
       datagrid_renderer.order_for(grid, column, **options)
     end
 
diff --git a/lib/tasks/datagrid_tasks.rake b/lib/tasks/datagrid_tasks.rake
index e1ca194..5c468ef 100644
--- a/lib/tasks/datagrid_tasks.rake
+++ b/lib/tasks/datagrid_tasks.rake
@@ -8,6 +8,8 @@ namespace :datagrid do
     destination_dir = (Rails.root + views_path).to_s
     pattern = "#{File.expand_path(File.dirname(__FILE__) + "/../../#{views_path}")}/*"
     Dir[pattern].each do |template|
+      # Deprecated partial
+      next if template.include?("/_order_for.")
       puts "* copy #{template} => #{destination_dir}"
       FileUtils.mkdir_p destination_dir
       FileUtils.cp template, destination_dir
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 8313432..f5d6bfb 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -144,7 +144,6 @@
         expect(rendered_partial).to include "Namespaced table partial."
         expect(rendered_partial).to include "Namespaced row partial."
         expect(rendered_partial).to include "Namespaced head partial."
-        expect(rendered_partial).to include "Namespaced order_for partial."
       end
     end
 
@@ -427,12 +426,14 @@ class OrderedGrid < Datagrid::Base
         column(:category)
       end
       object = OrderedGrid.new(descending: true, order: :category)
-      expect(subject.datagrid_order_for(object, object.column_by_name(:category))).to equal_to_dom(<<~HTML)
-        <div class="datagrid-order">
-        <a class="datagrid-order-control-asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">↑</a>
-        <a class="datagrid-order-control-desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">↓</a>
+      silence_warnings do
+        expect(subject.datagrid_order_for(object, object.column_by_name(:category))).to equal_to_dom(<<~HTML)
+        <div class="order">
+        <a class="asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
+        <a class="desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">&darr;</a>
         </div>
-      HTML
+        HTML
+      end
     end
   end
 
diff --git a/spec/support/test_partials/client/datagrid/_head.html.erb b/spec/support/test_partials/client/datagrid/_head.html.erb
index e22a008..1e25878 100644
--- a/spec/support/test_partials/client/datagrid/_head.html.erb
+++ b/spec/support/test_partials/client/datagrid/_head.html.erb
@@ -3,7 +3,6 @@
   <% grid.html_columns(*options[:columns]).each do |column| %>
     <th>
       <%= column.header %>
-      <%= datagrid_order_for(grid, column, **options) if column.order && options[:order]%>
     </th>
   <% end %>
 </tr>
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 7bdc956..cbb3bad 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -10,6 +10,7 @@ Version 2 addresses all that evolution.
 List of things introduces:
 
 1. Use `form_with` instead of `form_for`.
+1. Deprecated `datagrid_order_for`
 1. Ruby infinite ranges for range filters.
 1. Modern modular CSS classes.
 1. HTML5 input types: number, date, datetime-local.
@@ -37,6 +38,12 @@ datagrid_form_for(@users_grid, url: users_path)
 datagrid_form_with(model: @users_grid, url: users_path)
 ```
 
+## Deprecated datagrid\_order\_for
+
+`datagrid_order_for` helper serves no purpose and should not be used directly.
+The recommended way is to include your ordering code directly into `datagrid/head` partial.
+See default [head partial](../app/views/datagrid/_head.html.erb) for example.
+
 ## Infinite Ranges for range filters
 
 Ruby supports infinite ranges now,

From 1195f033db19d4cf657dedc1e4faf2c54539f083 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 12:03:07 +0100
Subject: [PATCH 075/157] Update views diff

---
 version-2/views.diff | 74 +++++++++++++++++++++++++++-----------------
 1 file changed, 46 insertions(+), 28 deletions(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index e0a83c4..f971d0e 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -39,37 +39,44 @@ index 7e175c1..fc4f4ae 100644
    </div>
  <% end -%>
 diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
-index e939128..c3264bb 100644
+index e939128..b355528 100644
 --- a/app/views/datagrid/_head.html.erb
 +++ b/app/views/datagrid/_head.html.erb
-@@ -1,6 +1,6 @@
+@@ -1,8 +1,31 @@
  <tr>
    <% grid.html_columns(*options[:columns]).each do |column| %>
 -    <th class="<%= datagrid_column_classes(grid, column) %>">
-+    <th class="<%= datagrid_column_classes(grid, column) %>" data-column="<%= column.name %>">
++    <%= content_tag(
++      :th,
++      class: {
++        # Adding html clases based on condition
++        # Consider maintaining consistency with datagrid/rows partial
++        "datagrid-order-active-asc": grid.ordered_by?(column, false),
++        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
++        column.html_class => column.html_class.present?,
++      },
++      "data-column": column.name
++    ) do %>
        <%= column.header %>
-       <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
-     </th>
-diff --git a/app/views/datagrid/_order_for.html.erb b/app/views/datagrid/_order_for.html.erb
-index 1545a8e..1c33c37 100644
---- a/app/views/datagrid/_order_for.html.erb
-+++ b/app/views/datagrid/_order_for.html.erb
-@@ -1,10 +1,10 @@
--<div class="order">
-+<div class="datagrid-order">
-   <%= link_to(
--      I18n.t("datagrid.table.order.asc").html_safe,
-+      I18n.t("datagrid.table.order.asc"),
-       datagrid_order_path(grid, column, false),
--      class: "asc") %>
-+      class: "datagrid-order-control-asc") %>
-   <%= link_to(
--      I18n.t("datagrid.table.order.desc").html_safe,
-+      I18n.t("datagrid.table.order.desc"),
-       datagrid_order_path(grid, column, true),
--      class: "desc") %>
-+      class: "datagrid-order-control-desc") %>
- </div>
+-      <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
+-    </th>
++      <% if column.supports_order? && options[:order] -%>
++        <div class="datagrid-order">
++          <%= link_to(
++            I18n.t("datagrid.table.order.asc"),
++            datagrid_order_path(grid, column, false),
++            class: "datagrid-order-control-asc"
++          ) %>
++          <%= link_to(
++            I18n.t("datagrid.table.order.desc"),
++            datagrid_order_path(grid, column, true),
++            class: "datagrid-order-control-desc"
++          ) %>
++        </div>
++      <% end -%>
++    <% end -%>
+   <% end %>
+ </tr>
 diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
 index 7a8a123..3b8ca85 100644
 --- a/app/views/datagrid/_range_filter.html.erb
@@ -82,14 +89,25 @@ index 7a8a123..3b8ca85 100644
 +<span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
 +<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options) %>
 diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
-index f54d21c..20f3ffb 100644
+index f54d21c..340aea6 100644
 --- a/app/views/datagrid/_row.html.erb
 +++ b/app/views/datagrid/_row.html.erb
-@@ -1,5 +1,5 @@
+@@ -1,5 +1,16 @@
  <tr>
    <% grid.html_columns(*options[:columns]).each do |column| %>
 -    <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_value(grid, column, asset) %></td>
-+    <td class="<%= datagrid_column_classes(grid, column) %>" data-column="<%= column.name %>"><%= datagrid_value(grid, column, asset) %></td>
++    <%= content_tag(
++      :td,
++      datagrid_value(grid, column, asset),
++      class: {
++        # Adding html clases based on condition
++        # Consider maintaining consistency with datagrid/head partial
++        "datagrid-order-active-asc": grid.ordered_by?(column, false),
++        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
++        column.html_class => column.html_class.present?,
++      },
++      "data-column": column.name
++    ) %>
    <% end %>
  </tr>
 diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb

From 6ac1194c5dadca0fb81b03f643797a54bf15774c Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 12:17:15 +0100
Subject: [PATCH 076/157] Update v2 guide

---
 version-2/Readme.markdown | 44 +++++++++++++++++----------------------
 1 file changed, 19 insertions(+), 25 deletions(-)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index cbb3bad..96f343e 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -11,7 +11,7 @@ List of things introduces:
 
 1. Use `form_with` instead of `form_for`.
 1. Deprecated `datagrid_order_for`
-1. Ruby infinite ranges for range filters.
+1. Ruby endless ranges for range filters.
 1. Modern modular CSS classes.
 1. HTML5 input types: number, date, datetime-local.
 1. Use Hash instead of Array for multiparameter attirubtes.
@@ -44,17 +44,17 @@ datagrid_form_with(model: @users_grid, url: users_path)
 The recommended way is to include your ordering code directly into `datagrid/head` partial.
 See default [head partial](../app/views/datagrid/_head.html.erb) for example.
 
-## Infinite Ranges for range filters
+## Endless ranges for range filters
 
-Ruby supports infinite ranges now,
-so there is no need to present infinite ranges as Hash or Array.
+Ruby supports endless ranges now,
+so there is no need to present endless ranges as Hash or Array.
 But it introduces a breaking changes to range filters in Datagrid:
 
 ``` ruby
 class UsersGrid < Datagrid::Base
   filter(:id, :integer, range: true) do |value, scope|
-    # V1 value: [1, nil]
-    # V2 value: 1..nil
+    # V1 value is  [1, nil]
+    # V2 value is 1..nil
     scope.where(id: value)
   end
 end
@@ -103,8 +103,8 @@ and avoid collisions with other libraries:
 | noresults    | datagrid-no-results                 |
 | datagrid     | datagrid-table                      |
 | order        | datagrid-order                      |
-| a.asc        | datagrid-order-control-asc          |
-| a.desc       | datagrid-order-control-desc         |
+| asc          | datagrid-order-control-asc          |
+| desc         | datagrid-order-control-desc         |
 | ordered.asc  | datagrid-order-active-asc           |
 | ordered.desc | datagrid-order-active-desc          |
 | field        | datagrid-dynamic-field              |
@@ -113,6 +113,9 @@ and avoid collisions with other libraries:
 | separator    | datagrid-range-separator            |
 | checkboxes   | datagrid-enum-checkboxes            |
 
+Diff for [built-in partials between V1 and V2](./views.diff)
+See [a new built-in CSS file](../app/assets/datagrid.css).
+
 ### Example
 
 The difference in layout generation from v1 to v2.
@@ -180,7 +183,7 @@ The default behavior can be changed back by using `input_options`:
 
 ``` ruby
 filter(:created_at, :date, range: true, input_options: {type: 'text'})
-filter(:salary, :integer, range: true, input_options: {type: 'text'})
+filter(:salary, :integer, range: true, input_options: {type: 'text', step: nil})
 ```
 
 Additionally, textarea inputs are now supported this way:
@@ -190,10 +193,9 @@ Additionally, textarea inputs are now supported this way:
 filter(:text, :string, input_options: {type: 'textarea'})
 ```
 
-## Names collision restriction
+## Prefer Hash instead of Array for multiparameter filter types
 
-HTML5 prohibits multiple inputs to have the same name.
-This is contradicts to Rails parameters convention that serializes multiple inputs with same name into array:
+Rails multiple input had been a problem [#325](https://github.com/bogdan/datagrid/issues/325).
 
 ``` html
 Date From:
@@ -250,6 +252,8 @@ instead of classes for meta information from backend.
 Therefor built-in partials now generate data attributes by default
 instead of classes for column names:
 
+Diff for [built-in partials between V1 and V2](./views.diff)
+
 ### Filters
 
 ``` html
@@ -263,10 +267,7 @@ instead of classes for column names:
 Version 2:
 
 ``` html
-<div class="datagrid-filter"
-         data-filter="category"
-         data-type="string"
->
+<div class="datagrid-filter" data-filter="category" data-type="string">
   <label for="form_for_grid_category">Category</label>
   <input type="text"
       name="form_for_grid[category]" id="form_for_grid_category" />
@@ -338,7 +339,7 @@ end
 ## ApplicationGrid base class
 
 Previously recommended base class `BaseGrid` is incosistent
-with Rails naming conventionsa.
+with Rails naming conventions.
 It was renamed to `ApplicationGrid` instead:
 
 ``` ruby
@@ -361,16 +362,9 @@ class UsersGrid < ApplicationGrid
 end
 ```
 
-## All changes in built-in partials
-
-Version 2 built-in partials are trying to expose
-as much UI as possible for user modification.
-
-Here is a complete [diff for built-in partials between V1 and V2](./views.diff)
-
 ## Remove SASS dependency
 
 SASS is no longer a default choice when starting a rails project.
 Version 2 makes it more flexible by avoiding the dependency on any particular CSS framework.
 
-Inspect [a new built-in CSS file](../app/assets/datagrid.css).
+See [a new built-in CSS file](../app/assets/datagrid.css).

From 74aa904ca3bc3a1718228fa87fa395d9d1d61b10 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 12:44:33 +0100
Subject: [PATCH 077/157] Generate local form when using datagrid_form_for

---
 lib/datagrid/renderer.rb     | 13 ++++++++++---
 spec/datagrid/helper_spec.rb |  2 +-
 version-2/Readme.markdown    |  4 +++-
 3 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index be9ca2e..8423cec 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -20,9 +20,16 @@ def format_value(grid, column, asset)
     end
 
     def form_for(grid, options = {})
-      options[:method] ||= :get
-      options[:as] ||= grid.param_name
-      _render_partial("form", options[:partials], { grid: grid, options: options })
+      _render_partial(
+        "form", options[:partials],
+        grid: grid,
+        options: {
+          method: :get,
+          as: grid.param_name,
+          local: true,
+          **options
+        },
+      )
     end
 
     def form_with(**options)
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index f5d6bfb..76c16fe 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -458,7 +458,7 @@ class FormForGrid < Datagrid::Base
       end
       object = FormForGrid.new(category: "hello")
       expect(subject.datagrid_form_for(object, url: "/grid")).to equal_to_dom(<<~HTML)
- <form class="datagrid-form" action="/grid" accept-charset="UTF-8" data-remote="true" method="get">
+ <form class="datagrid-form" action="/grid" accept-charset="UTF-8" method="get">
    <input name="utf8" type="hidden" value="&#x2713;" autocomplete="off" />
       <div class="datagrid-filter" data-filter="category" data-type="string">
         <label for="form_for_grid_category">Category</label>
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 96f343e..3e057ba 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -1,6 +1,6 @@
 # Datagrid Version 2
 
-Datagrid v1 was released Sep 19 2013 - more than 10 years ago.
+Datagrid v1 was released on Sep 19 2013 - more than 10 years ago.
 A lot of changes in best practices and available technology
 had happened during this period.
 It caused the library to be designed without support of those technologies
@@ -38,6 +38,8 @@ datagrid_form_for(@users_grid, url: users_path)
 datagrid_form_with(model: @users_grid, url: users_path)
 ```
 
+Built-in partial uses `form_with` no matter
+
 ## Deprecated datagrid\_order\_for
 
 `datagrid_order_for` helper serves no purpose and should not be used directly.

From ace61b34e6a9ab4ae732b67dbd58fec78444491f Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 21:11:20 +0100
Subject: [PATCH 078/157] Rollback changes in datagrid_order_for

---
 lib/datagrid/helper.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index be58a10..dc5e157 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -93,13 +93,13 @@ def datagrid_rows(grid, assets = grid.assets, **options, &block)
     #
     # * <tt>:partials</tt> - Path for partials lookup.
     #   Default: 'datagrid'.
-    def datagrid_order_for(grid, column, order: true, **options)
+    def datagrid_order_for(grid, column, options = {})
       Datagrid::Utils.warn_once(<<~MSG)
         datagrid_order_for is deprecated.
         Put necessary code inline inside datagrid/head partial.
         See built-in partial for example.
       MSG
-      datagrid_renderer.order_for(grid, column, **options)
+      datagrid_renderer.order_for(grid, column, options)
     end
 
     # Renders HTML for grid with all filters inputs and labels defined in it

From 5ead01260ac529454769f559727e5b59f113247c Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 21:18:12 +0100
Subject: [PATCH 079/157] Update migration guide

---
 version-2/Readme.markdown | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 3e057ba..f8e958b 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -115,6 +115,10 @@ and avoid collisions with other libraries:
 | separator    | datagrid-range-separator            |
 | checkboxes   | datagrid-enum-checkboxes            |
 
+All classes are now explicitly assinged inside datagrid partials.
+[Modify built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
+if you want to change them.
+
 Diff for [built-in partials between V1 and V2](./views.diff)
 See [a new built-in CSS file](../app/assets/datagrid.css).
 
@@ -254,10 +258,11 @@ instead of classes for meta information from backend.
 Therefor built-in partials now generate data attributes by default
 instead of classes for column names:
 
-Diff for [built-in partials between V1 and V2](./views.diff)
 
 ### Filters
 
+Version 1:
+
 ``` html
 <div class="datagrid-filter filter">
   <label for="form_for_grid_category">Category</label>
@@ -276,6 +281,8 @@ Version 2:
 </div>
 ```
 
+Diff for [built-in partials between V1 and V2](./views.diff)
+
 ### Columns
 
 Version 1:
@@ -318,14 +325,16 @@ If you still want to have an HTML class attached to a column use `class` column
 column(:name, class: 'short-column')
 ```
 
+Renders:
+
 ``` html
 <th class="short-column" data-column="name">Name</th>
 ...
 <td class="short-column" data-column="name">John</td>
 ```
 
-If you want to change this behavior completely,
-modify [built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
+[Modify built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
+if you want to change this behavior completely.
 
 ## Inherit Datagrid::Base
 

From a0c451d930212a463e63511dc0512d2d06fe4335 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 10 Nov 2024 21:57:21 +0100
Subject: [PATCH 080/157] Update doc

---
 Readme.markdown              | 10 ++++++----
 lib/datagrid/form_builder.rb |  5 +----
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/Readme.markdown b/Readme.markdown
index 98a9f80..924fa4c 100644
--- a/Readme.markdown
+++ b/Readme.markdown
@@ -1,11 +1,13 @@
 # Datagrid
 
-[![Build Status](https://github.com/bogdan/datagrid/workflows/CI/badge.svg?branch=master)](https://github.com/bogdan/datagrid/actions)
+Datagrid Version 2.0.0 is here.
+
+[Migration Guide](./version-2).
 
-[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fbogdan%2Fdatagrid.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fbogdan%2Fdatagrid?ref=badge_shield)
+[![Build Status](https://github.com/bogdan/datagrid/workflows/CI/badge.svg?branch=master)](https://github.com/bogdan/datagrid/actions)
 
 A really mighty and flexible ruby library that generates reports
-including admin panels, analytics and data representation:
+including admin panels, analytics and data browsers:
 
 * Filtering
 * Columns
@@ -31,7 +33,7 @@ including admin panels, analytics and data representation:
 
 ## Documentation
 
-* [Readme](/Readme.markdown) - this read-me for basic information.
+* Readme - this read-me for basic information.
 * [Wiki](https://github.com/bogdan/datagrid/wiki) - general reference on how to use the gem.
 * [Rdoc](https://rubydoc.info/gems/datagrid) - API reference.
 
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index a53b79a..aec1cc0 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -6,10 +6,7 @@ module Datagrid
   module FormBuilder
     # @param filter_or_attribute [Datagrid::Filters::BaseFilter, String, Symbol] filter object or filter name
     # @param options [Hash] options of rails form input helper
-    # @return [String] a form input html for the corresponding filter name
-    #   * <tt>select</tt> for enum, xboolean filter types
-    #   * <tt>check_box</tt> for boolean filter type
-    #   * <tt>text_field</tt> for other filter types
+    # @return [String] a form input html for the corresponding filter
     def datagrid_filter(filter_or_attribute, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
       send(filter.form_builder_helper_name, filter, **options, &block)

From fb96d98520a76dd1f341edb461a0c3d6fe7d7784 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 10:34:56 +0100
Subject: [PATCH 081/157] v2.0.0

---
 lib/datagrid/version.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/datagrid/version.rb b/lib/datagrid/version.rb
index 12d0cd3..9b62e71 100644
--- a/lib/datagrid/version.rb
+++ b/lib/datagrid/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 module Datagrid
-  VERSION = "1.8.4"
+  VERSION = "2.0.0"
 end

From 80d56f58d2a4805fa2904190acc69347aa899654 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 10:50:31 +0100
Subject: [PATCH 082/157] BaseFilter#default_scope? method

---
 lib/datagrid/filters/base_filter.rb       | 23 +++++++++++------------
 spec/datagrid/filters/base_filter_spec.rb | 16 ++++++++++++++++
 2 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index 262aed5..f7f3074 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -17,7 +17,7 @@ def initialize(grid_class, name, options = {}, &block)
         self.grid_class = grid_class
         self.name = name.to_sym
         self.options = options
-        self.block = block || default_filter_block
+        self.block = block
       end
 
       def parse(value)
@@ -39,7 +39,7 @@ def apply(grid_object, scope, value)
 
         return scope unless result
 
-        result = default_filter(value, scope, grid_object) if result == Datagrid::Filters::DEFAULT_FILTER_BLOCK
+        result = default_filter(value, scope) if result == Datagrid::Filters::DEFAULT_FILTER_BLOCK
         unless grid_object.driver.match?(result)
           raise(
             Datagrid::FilteringError,
@@ -116,13 +116,6 @@ def self.form_builder_helper_name
         :"datagrid_#{to_s.demodulize.underscore}"
       end
 
-      def default_filter_block
-        filter = self
-        lambda do |value, scope, grid|
-          filter.default_filter(value, scope, grid)
-        end
-      end
-
       def supports_range?
         self.class.ancestors.include?(::Datagrid::Filters::RangedFilter)
       end
@@ -150,6 +143,10 @@ def enum_checkboxes?
         false
       end
 
+      def default_scope?
+        !block
+      end
+
       protected
 
       def default_filter_where(scope, value)
@@ -157,10 +154,12 @@ def default_filter_where(scope, value)
       end
 
       def execute(value, scope, grid_object)
-        if block.arity == 1
+        if block&.arity == 1
           scope.instance_exec(value, &block)
-        else
+        elsif block
           Datagrid::Utils.apply_args(value, scope, grid_object, &block)
+        else
+          default_filter(value, scope)
         end
       end
 
@@ -185,7 +184,7 @@ def driver
         grid_class.driver
       end
 
-      def default_filter(value, scope, _grid)
+      def default_filter(value, scope)
         return nil if dummy?
 
         if !driver.scope_has_column?(scope, name) && scope.respond_to?(name, true)
diff --git a/spec/datagrid/filters/base_filter_spec.rb b/spec/datagrid/filters/base_filter_spec.rb
index fc60ca1..9a6340f 100644
--- a/spec/datagrid/filters/base_filter_spec.rb
+++ b/spec/datagrid/filters/base_filter_spec.rb
@@ -15,4 +15,20 @@ def name_default
     expect(report.assets).not_to include(Entry.create!(name: "world"))
     expect(report.assets).not_to include(Entry.create!(name: ""))
   end
+
+  describe "#default_scope?" do
+
+    it "identifies filters without custom block" do
+      grid = test_report do
+        scope { Entry }
+        filter(:id, :integer)
+        filter(:group_id, :integer) do |value, scope|
+          scope("group_id >= ?", value)
+        end
+      end
+
+      expect(grid.filter_by_name(:id)).to be_default_scope
+      expect(grid.filter_by_name(:group_id)).to_not be_default_scope
+    end
+  end
 end

From cb8d752889a0cae0fdc15219b1aefd351b2829f9 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 10:50:40 +0100
Subject: [PATCH 083/157] Finding deprecation scripts

---
 version-2/Readme.markdown              | 32 +++++++++++++++++---------
 version-2/deprecations.sh              | 16 +++++++++++++
 version-2/find_broken_range_filters.rb | 15 ++++++++++++
 3 files changed, 52 insertions(+), 11 deletions(-)
 create mode 100644 version-2/deprecations.sh
 create mode 100644 version-2/find_broken_range_filters.rb

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index f8e958b..236ffdf 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -40,11 +40,27 @@ datagrid_form_with(model: @users_grid, url: users_path)
 
 Built-in partial uses `form_with` no matter
 
+[Grep all deprecations](./deprecations.sh).
+
 ## Deprecated datagrid\_order\_for
 
 `datagrid_order_for` helper serves no purpose and should not be used directly.
 The recommended way is to include your ordering code directly into `datagrid/head` partial.
 See default [head partial](../app/views/datagrid/_head.html.erb) for example.
+[Grep all deprecations](./deprecations.sh).
+
+## Inherit Datagrid::Base
+
+`include Datagrid` causes method name space to be clamsy.
+Version 2 introduces a difference between the class
+that needs to be inherited and high level namespace (just like most gems do):
+
+``` ruby
+class ApplicationGrid < Datagrid::Base
+end
+```
+
+[Grep all deprecations](./deprecations.sh).
 
 ## Endless ranges for range filters
 
@@ -92,6 +108,11 @@ grid = UsersGrid.new(ActiveSupport::JSON.load(grid.attributes.to_json))
 grid.id # => 3..7
 ```
 
+This very likely breaks all `range: true` filters with custom block passed.
+All such filters can be seen with this script (works only for V2):
+
+[Search all broken range filters](./find_broken_range_filters.rb)
+
 ## Modern CSS classes naming conventions
 
 Built-in generated CSS classes renamed to match modern CSS naming conventions
@@ -336,17 +357,6 @@ Renders:
 [Modify built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
 if you want to change this behavior completely.
 
-## Inherit Datagrid::Base
-
-`include Datagrid` causes method name space to be clamsy.
-Version 2 introduces a difference between the class
-that needs to be inherited and high level namespace (just like most gems do):
-
-``` ruby
-class ApplicationGrid < Datagrid::Base
-end
-```
-
 ## ApplicationGrid base class
 
 Previously recommended base class `BaseGrid` is incosistent
diff --git a/version-2/deprecations.sh b/version-2/deprecations.sh
new file mode 100644
index 0000000..9671188
--- /dev/null
+++ b/version-2/deprecations.sh
@@ -0,0 +1,16 @@
+# Use datagrid_form_with
+git grep 'datagrid_form_for'
+
+# Inline content of datagrid/order_for partial
+git grep 'datagrid_order_for'
+
+# Put necessary classes manually
+git grep 'datagrid_column_classes' 
+
+# Inherit Datagrid::Base
+git grep 'include Datagrid' 
+
+# Rename to ApplicationGrid (optional)
+git grep 'BaseDatagrid'
+git grep 'BaseGrid'
+
diff --git a/version-2/find_broken_range_filters.rb b/version-2/find_broken_range_filters.rb
new file mode 100644
index 0000000..b8a988f
--- /dev/null
+++ b/version-2/find_broken_range_filters.rb
@@ -0,0 +1,15 @@
+# Important in development to have all classes in memory
+Rails.application.eager_load!
+
+raise "Use version 2" if Datagrid::VERSION < "2.0.0"
+
+included_classes = ObjectSpace.each_object(Class).select do |klass|
+  klass.included_modules.include?(Datagrid)
+end
+classes = [*included_classes, *Datagrid::Base.subclasses].uniq
+
+classes.flat_map(&:filters).select do |f|
+  f.respond_to?(:range?) && f.range? && f.block
+end.map do |f|
+  [f.grid_class, f.name].join("#")
+end

From dbe6600190682518eb506136111b4e86c424cef6 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 10:57:05 +0100
Subject: [PATCH 084/157] Fix script bug

---
 version-2/find_broken_range_filters.rb | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/version-2/find_broken_range_filters.rb b/version-2/find_broken_range_filters.rb
index b8a988f..989643d 100644
--- a/version-2/find_broken_range_filters.rb
+++ b/version-2/find_broken_range_filters.rb
@@ -6,7 +6,11 @@
 included_classes = ObjectSpace.each_object(Class).select do |klass|
   klass.included_modules.include?(Datagrid)
 end
-classes = [*included_classes, *Datagrid::Base.subclasses].uniq
+
+base_subclasses = ObjectSpace.each_object(Class).select do |klass|
+  klass < Datagrid::Base
+end
+classes = [*included_classes, *base_subclasses].uniq
 
 classes.flat_map(&:filters).select do |f|
   f.respond_to?(:range?) && f.range? && f.block

From 3059c07e9607abab7ff03a120b26664a7c8e36f7 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 11:18:46 +0100
Subject: [PATCH 085/157] Replace rake datagrid:copy_partials with rails g
 datagrid::views

---
 Readme.markdown                                |  6 +++---
 lib/datagrid.rb                                |  3 ++-
 lib/datagrid/{ => generators}/scaffold.rb      |  3 +++
 lib/datagrid/generators/views.rb               | 18 ++++++++++++++++++
 lib/tasks/datagrid_tasks.rake                  | 18 ------------------
 .../datagrid/{ => generators}/scaffold_spec.rb |  4 ++--
 version-2/Readme.markdown                      |  1 +
 version-2/deprecations.sh                      |  3 +++
 8 files changed, 32 insertions(+), 24 deletions(-)
 rename lib/datagrid/{ => generators}/scaffold.rb (98%)
 create mode 100644 lib/datagrid/generators/views.rb
 delete mode 100644 lib/tasks/datagrid_tasks.rake
 rename spec/datagrid/{ => generators}/scaffold_spec.rb (90%)

diff --git a/Readme.markdown b/Readme.markdown
index 924fa4c..c5a0729 100644
--- a/Readme.markdown
+++ b/Readme.markdown
@@ -183,12 +183,12 @@ route  resources :skills
 insert  app/assets/stylesheet/application.css
 ```
 
-#### Customize Built-in partials
+#### Customize Built-in views
 
-In order to get a control on datagrid built-in partials run:
+In order to get a control on datagrid built-in views run:
 
 ``` sh
-rake datagrid:copy_partials
+rails g datagrid::views
 ```
 
 #### Advanced frontend
diff --git a/lib/datagrid.rb b/lib/datagrid.rb
index a63a21b..74e19c9 100644
--- a/lib/datagrid.rb
+++ b/lib/datagrid.rb
@@ -25,5 +25,6 @@ class ColumnUnavailableError < StandardError; end
 end
 
 require "datagrid/base"
-require "datagrid/scaffold"
+require "datagrid/generators/scaffold"
+require "datagrid/generators/views"
 I18n.load_path << File.expand_path("datagrid/locale/en.yml", __dir__)
diff --git a/lib/datagrid/scaffold.rb b/lib/datagrid/generators/scaffold.rb
similarity index 98%
rename from lib/datagrid/scaffold.rb
rename to lib/datagrid/generators/scaffold.rb
index 6f8a310..0956e46 100644
--- a/lib/datagrid/scaffold.rb
+++ b/lib/datagrid/generators/scaffold.rb
@@ -4,6 +4,8 @@
 
 # @!visibility private
 module Datagrid
+  # @!visibility private
+  module Generators
   # @!visibility private
   class Scaffold < Rails::Generators::NamedBase
     include Rails::Generators::ResourceHelpers
@@ -167,4 +169,5 @@ def file_exists?(name)
       File.exist?(name)
     end
   end
+  end
 end
diff --git a/lib/datagrid/generators/views.rb b/lib/datagrid/generators/views.rb
new file mode 100644
index 0000000..0a85252
--- /dev/null
+++ b/lib/datagrid/generators/views.rb
@@ -0,0 +1,18 @@
+module Datagrid
+  module Generators
+    class Views < Rails::Generators::Base
+      source_root File.expand_path("../../../app/views/datagrid", __dir__)
+
+      desc "Copies Datagrid partials to your application."
+      def copy_views
+        Dir.glob(File.join(self.class.source_root, "**", "*")).each do |file_path|
+          relative_path = file_path.sub(self.class.source_root + "/", "")
+
+          next if relative_path == "_order_for.html.erb"
+
+          copy_file(relative_path, File.join("app/views/datagrid", relative_path))
+        end
+      end
+    end
+  end
+end
diff --git a/lib/tasks/datagrid_tasks.rake b/lib/tasks/datagrid_tasks.rake
deleted file mode 100644
index 5c468ef..0000000
--- a/lib/tasks/datagrid_tasks.rake
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-namespace :datagrid do
-  desc "Copy table partials into rails application"
-  task :copy_partials do
-    require "fileutils"
-    views_path = "app/views/datagrid"
-    destination_dir = (Rails.root + views_path).to_s
-    pattern = "#{File.expand_path(File.dirname(__FILE__) + "/../../#{views_path}")}/*"
-    Dir[pattern].each do |template|
-      # Deprecated partial
-      next if template.include?("/_order_for.")
-      puts "* copy #{template} => #{destination_dir}"
-      FileUtils.mkdir_p destination_dir
-      FileUtils.cp template, destination_dir
-    end
-  end
-end
diff --git a/spec/datagrid/scaffold_spec.rb b/spec/datagrid/generators/scaffold_spec.rb
similarity index 90%
rename from spec/datagrid/scaffold_spec.rb
rename to spec/datagrid/generators/scaffold_spec.rb
index 0fcf45e..d83f88d 100644
--- a/spec/datagrid/scaffold_spec.rb
+++ b/spec/datagrid/generators/scaffold_spec.rb
@@ -2,8 +2,8 @@
 
 require "spec_helper"
 
-describe Datagrid::Scaffold do
-  subject { Datagrid::Scaffold.new(["user"]) }
+describe Datagrid::Generators::Scaffold do
+  subject { Datagrid::Generators::Scaffold.new(["user"]) }
 
   describe ".pagination_helper_code" do
     it "uses kaminari by default" do
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 236ffdf..235d46d 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -21,6 +21,7 @@ List of things introduces:
 1. Inherit `Datagrid::Base` instead of `include Datagrid`
 1. `ApplicationGrid` is recommended base class instead of `BaseGrid`
 1. Remove SASS dependency
+1. Replace `rake datagrid:copy_partials` with `rails g datagrid:views`
 
 ## Use form\_with
 
diff --git a/version-2/deprecations.sh b/version-2/deprecations.sh
index 9671188..01a4341 100644
--- a/version-2/deprecations.sh
+++ b/version-2/deprecations.sh
@@ -10,6 +10,9 @@ git grep 'datagrid_column_classes'
 # Inherit Datagrid::Base
 git grep 'include Datagrid' 
 
+# Use rails g datagrid:views
+git grep 'datagrid:copy_partials'
+
 # Rename to ApplicationGrid (optional)
 git grep 'BaseDatagrid'
 git grep 'BaseGrid'

From 6020fd66759010f135e17d4920d3ddad2ee291e4 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 11:46:43 +0100
Subject: [PATCH 086/157] Set new rails default
 ActionView::Helpers::FormTagHelper.default_enforce_utf8 = false

---
 spec/datagrid/helper_spec.rb | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 76c16fe..47f70dc 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -20,8 +20,10 @@
     allow(subject).to receive(:url_for) do |options|
       options.is_a?(String) ? options : ["/location", options.to_param.presence].compact.join("?")
     end
-    # Rails default since 5.x
+
+    # Rails defaults since 6.x
     ActionView::Helpers::FormHelper.form_with_generates_ids = true
+    ActionView::Helpers::FormTagHelper.default_enforce_utf8 = false
   end
 
   let(:group) { Group.create!(name: "Pop") }
@@ -459,11 +461,10 @@ class FormForGrid < Datagrid::Base
       object = FormForGrid.new(category: "hello")
       expect(subject.datagrid_form_for(object, url: "/grid")).to equal_to_dom(<<~HTML)
  <form class="datagrid-form" action="/grid" accept-charset="UTF-8" method="get">
-   <input name="utf8" type="hidden" value="&#x2713;" autocomplete="off" />
-      <div class="datagrid-filter" data-filter="category" data-type="string">
-        <label for="form_for_grid_category">Category</label>
-        <input value="hello" type="text" name="form_for_grid[category]" id="form_for_grid_category" />
-      </div>
+  <div class="datagrid-filter" data-filter="category" data-type="string">
+    <label for="form_for_grid_category">Category</label>
+    <input value="hello" type="text" name="form_for_grid[category]" id="form_for_grid_category" />
+  </div>
   <div class="datagrid-actions">
     <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
     <a class="datagrid-reset" href="/location">Reset</a>
@@ -533,11 +534,10 @@ class FormWithGrid < Datagrid::Base
       object = FormWithGrid.new(category: "hello")
       expect(subject.datagrid_form_with(model: object, url: "/grid")).to equal_to_dom(<<~HTML)
  <form class="datagrid-form" action="/grid" accept-charset="UTF-8" data-remote="true" method="get">
-   <input name="utf8" type="hidden" value="&#x2713;" autocomplete="off" />
-      <div class="datagrid-filter" data-filter="category" data-type="string">
-        <label for="form_with_grid_category">Category</label>
-        <input value="hello" type="text" name="form_with_grid[category]" id="form_with_grid_category" />
-      </div>
+  <div class="datagrid-filter" data-filter="category" data-type="string">
+    <label for="form_with_grid_category">Category</label>
+    <input value="hello" type="text" name="form_with_grid[category]" id="form_with_grid_category" />
+  </div>
   <div class="datagrid-actions">
     <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
     <a class="datagrid-reset" href="/location">Reset</a>

From 18b97b3f854dfd978e8af8f4796e7ed1bcf3dc3f Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 11:51:12 +0100
Subject: [PATCH 087/157] Set form_with_generates_remote_forms to false as it
 is default value

---
 spec/datagrid/helper_spec.rb | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 47f70dc..a5c40b3 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -21,8 +21,9 @@
       options.is_a?(String) ? options : ["/location", options.to_param.presence].compact.join("?")
     end
 
-    # Rails defaults since 6.x
+    # Rails defaults since 6.1
     ActionView::Helpers::FormHelper.form_with_generates_ids = true
+    ActionView::Helpers::FormHelper.form_with_generates_remote_forms = false
     ActionView::Helpers::FormTagHelper.default_enforce_utf8 = false
   end
 
@@ -533,7 +534,7 @@ class FormWithGrid < Datagrid::Base
       end
       object = FormWithGrid.new(category: "hello")
       expect(subject.datagrid_form_with(model: object, url: "/grid")).to equal_to_dom(<<~HTML)
- <form class="datagrid-form" action="/grid" accept-charset="UTF-8" data-remote="true" method="get">
+ <form class="datagrid-form" action="/grid" accept-charset="UTF-8" method="get">
   <div class="datagrid-filter" data-filter="category" data-type="string">
     <label for="form_with_grid_category">Category</label>
     <input value="hello" type="text" name="form_with_grid[category]" id="form_with_grid_category" />

From 5fa36cb251dd588a2fcd090eec1b42e9330473a6 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 11:57:38 +0100
Subject: [PATCH 088/157] Update form-v2.html

---
 version-2/form-v2.html | 2 --
 1 file changed, 2 deletions(-)

diff --git a/version-2/form-v2.html b/version-2/form-v2.html
index 03a40a3..a67fd98 100644
--- a/version-2/form-v2.html
+++ b/version-2/form-v2.html
@@ -1,6 +1,4 @@
 <form class="datagrid-form" id="new_g" action="/users" accept-charset="UTF-8" method="get">
-  <input name="utf8" type="hidden" value="✓" autocomplete="off" />
-
   <div class="datagrid-filter" data-filter="id" data-type="integer">
     <label for="g_id">Id</label>
     <input step="1" class="datagrid-range-from" name="g[id][from]" type="number" />

From 5315eee5056eabb76e87c6467a24f4c020d23fa2 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 11:57:54 +0100
Subject: [PATCH 089/157] Update form-v1.html

---
 version-2/form-v1.html | 2 --
 1 file changed, 2 deletions(-)

diff --git a/version-2/form-v1.html b/version-2/form-v1.html
index 5e8241a..2df12dc 100644
--- a/version-2/form-v1.html
+++ b/version-2/form-v1.html
@@ -1,6 +1,4 @@
 <form class="datagrid-form partial_default_grid" id="new_g" action="/users" accept-charset="UTF-8" method="get">
-  <input name="utf8" type="hidden" value="✓" autocomplete="off" />
-
   <div class="datagrid-filter filter">
     <label for="g_id">Id</label>
     <input class="id integer_filter from" multiple type="text" name="g[id][]" />

From 0f9b3653654c6a9b9426055a2c40096efcc51ef0 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 11 Nov 2024 19:30:27 +0100
Subject: [PATCH 090/157] Additional doc

---
 lib/datagrid/core.rb      | 14 +++++++++++++-
 version-2/Readme.markdown | 18 ++++++++++++++++--
 2 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index 1a167c9..e11830a 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -137,6 +137,15 @@ def initialize(attributes = nil, &block)
     end
 
     # @return [{Symbol => Object}] grid attributes including filter values and ordering values
+    # @example
+    #   class UsersGrid < ApplicationGrid
+    #     scope { User }
+    #     filter(:first_name, :string)
+    #     filter(:last_name, :string)
+    #   end
+    #
+    #   grid = UsersGrid.new(first_name: 'John', last_name: 'Smith')
+    #   grid.attributes # => {first_name: 'John', last_name: 'Smith', order: nil, descending: nil}
     def attributes
       result = {}
       datagrid_attributes.each do |name|
@@ -148,10 +157,13 @@ def attributes
     # Updates datagrid attributes with a passed hash argument
     # @param attributes [{Symbol => Object}]
     # @example
-    #   grid = MyGrid.new
+    #   grid = UsersGrid.new
     #   grid.attributes = {first_name: 'John', last_name: 'Smith'}
     #   grid.first_name # => 'John'
     #   grid.last_name # => 'Smith'
+    def attributes=(value)
+      super
+    end
 
     # @return [Object] Any datagrid attribute value
     def [](attribute)
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 235d46d..1ab4c4c 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -193,7 +193,7 @@ TODO
 
 ## HTML5 input types
 
-Version 1 generated `<input type="text"/>` for every filter type.
+Version 1 generated `<input type="text"/>` for most filter types.
 Version 2 uses the appropriate input type for each filter type:
 
 | Type       | HTML Input Element                         |
@@ -214,6 +214,20 @@ filter(:created_at, :date, range: true, input_options: {type: 'text'})
 filter(:salary, :integer, range: true, input_options: {type: 'text', step: nil})
 ```
 
+You can disable HTML5 inputs with:
+
+``` ruby
+class ApplicationGrid < Datagrid::Base
+  def self.filter(name, type = :default, input_options: {}, **options)
+    if [:date, :datetime, :float, :integer].include?(type)
+      input_options[:type] ||= 'text'
+    end
+
+    super(name, type, input_options:, **options)
+  end
+end
+```
+
 Additionally, textarea inputs are now supported this way:
 
 ``` ruby
@@ -389,4 +403,4 @@ end
 SASS is no longer a default choice when starting a rails project.
 Version 2 makes it more flexible by avoiding the dependency on any particular CSS framework.
 
-See [a new built-in CSS file](../app/assets/datagrid.css).
+See [a new built-in CSS file](../app/assets/stylesheets/datagrid.css).

From d6ce92cbdba578c35b3ad436abbd4587320b88ab Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 12 Nov 2024 16:23:13 +0100
Subject: [PATCH 091/157] Update datagrid_spec.rb

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 spec/datagrid_spec.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/spec/datagrid_spec.rb b/spec/datagrid_spec.rb
index 881cb57..154623d 100644
--- a/spec/datagrid_spec.rb
+++ b/spec/datagrid_spec.rb
@@ -91,7 +91,7 @@
     end
   end
 
-  it "deprecates inclucsion of Datagrid module" do
+  it "deprecates inclusion of Datagrid module" do
     silence_warnings do
       class DeprecatedInclusion
         include Datagrid

From 0cd654383d58910befb78f582dde6682d6112eee Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 12 Nov 2024 16:23:22 +0100
Subject: [PATCH 092/157] Update Readme.markdown

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 version-2/Readme.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 1ab4c4c..ce1973a 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -27,7 +27,7 @@ List of things introduces:
 
 Rails [deprecates form\_for in favor of form\_with](https://guides.rubyonrails.org/form_helpers.html#using-form-tag-and-form-for).
 
-`datagrid_form_for` is now depreacted if favor of `datagrid_form_with`.
+`datagrid_form_for` is now deprecated if favor of `datagrid_form_with`.
 However, `datagrid_form_for` would also use Rails `form_with` because they share the same view partial.
 
 TODO: update the wiki

From a26b59f9dfd6d3e33e1a789d029e6c6eb5e55424 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 12 Nov 2024 16:23:38 +0100
Subject: [PATCH 093/157] Update Readme.markdown

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 version-2/Readme.markdown | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index ce1973a..d4d5d13 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -400,7 +400,7 @@ end
 
 ## Remove SASS dependency
 
-SASS is no longer a default choice when starting a rails project.
+SASS is no longer a default choice when starting a Ruby on Rails project.
 Version 2 makes it more flexible by avoiding the dependency on any particular CSS framework.
 
 See [a new built-in CSS file](../app/assets/stylesheets/datagrid.css).

From 5ae09eecd050728ca7bf89ae8f0d90955e4b1b0a Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 12 Nov 2024 20:26:35 +0100
Subject: [PATCH 094/157] Use Rails.deprecator

---
 CHANGELOG.md                 | 4 ++++
 lib/datagrid/utils.rb        | 8 +++++++-
 spec/datagrid/helper_spec.rb | 6 +++---
 spec/datagrid/utils_spec.rb  | 4 ++--
 spec/datagrid_spec.rb        | 2 +-
 spec/spec_helper.rb          | 4 ++++
 6 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b928556..75abc2c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog
 
+## 2.0.0
+
+[Changes and migration guide](./version-2)
+
 ## 1.8.3
 
 * Fix rails hooking for version 7.1. [#327](https://github.com/bogdan/datagrid/issues/327)
diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index 56a85a7..6ff31f4 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -23,12 +23,18 @@ def translate_from_namespace(namespace, grid_class, key)
         I18n.t(lookups.shift, default: lookups).presence
       end
 
+      def deprecator
+        defined?(Rails) && Rails.version >= "7.1.0" ?
+          Rails.deprecator : ActiveSupport::Deprecator
+      end
+
       def warn_once(message, delay = 5)
+
         @warnings ||= {}
         timestamp = @warnings[message]
         return false if timestamp && timestamp >= Time.now - delay
 
-        warn message
+        deprecator.warn(message)
         @warnings[message] = Time.now
         true
       end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index a5c40b3..4a8ec28 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -429,7 +429,7 @@ class OrderedGrid < Datagrid::Base
         column(:category)
       end
       object = OrderedGrid.new(descending: true, order: :category)
-      silence_warnings do
+      silence_deprecator do
         expect(subject.datagrid_order_for(object, object.column_by_name(:category))).to equal_to_dom(<<~HTML)
         <div class="order">
         <a class="asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
@@ -442,7 +442,7 @@ class OrderedGrid < Datagrid::Base
 
   describe ".datagrid_form_for" do
     around(:each) do |e|
-      silence_warnings do
+      silence_deprecator do
         e.run
       end
     end
@@ -736,7 +736,7 @@ def param_name
         column(:name)
         column(:group_id, class: 'short-column')
       end
-      silence_warnings do
+      silence_deprecator do
         expect(subject.send(:datagrid_column_classes, grid, :name)).to eq(
           "name ordered desc"
         )
diff --git a/spec/datagrid/utils_spec.rb b/spec/datagrid/utils_spec.rb
index 1128edb..e3ef21a 100644
--- a/spec/datagrid/utils_spec.rb
+++ b/spec/datagrid/utils_spec.rb
@@ -5,13 +5,13 @@
 describe Datagrid::Utils do
   describe ".warn_once" do
     it "should work" do
-      silence_warnings do
+      silence_deprecator do
         expect(Datagrid::Utils.warn_once("hello", 0.2)).to eq(true)
       end
       sleep(0.1)
       expect(Datagrid::Utils.warn_once("hello", 0.2)).to eq(false)
       sleep(0.2)
-      silence_warnings do
+      silence_deprecator do
         expect(Datagrid::Utils.warn_once("hello", 0.2)).to eq(true)
       end
     end
diff --git a/spec/datagrid_spec.rb b/spec/datagrid_spec.rb
index 881cb57..db0b6bc 100644
--- a/spec/datagrid_spec.rb
+++ b/spec/datagrid_spec.rb
@@ -92,7 +92,7 @@
   end
 
   it "deprecates inclucsion of Datagrid module" do
-    silence_warnings do
+    silence_deprecator do
       class DeprecatedInclusion
         include Datagrid
         scope { Entry }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 617d4ab..4735698 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -102,6 +102,10 @@ def action_view_template
   template
 end
 
+def silence_deprecator(&block)
+  Datagrid::Utils.deprecator.silence(&block)
+end
+
 # Requires supporting files with custom matchers and macros, etc,
 # in ./support/ and its subdirectories.
 Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }

From 26dabcb51cb05c3d829e74acd9f79c43b8e43af5 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 12 Nov 2024 20:29:40 +0100
Subject: [PATCH 095/157] Fix rails 7.0 build

---
 lib/datagrid/utils.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index 6ff31f4..fea258f 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -25,7 +25,7 @@ def translate_from_namespace(namespace, grid_class, key)
 
       def deprecator
         defined?(Rails) && Rails.version >= "7.1.0" ?
-          Rails.deprecator : ActiveSupport::Deprecator
+          Rails.deprecator : ActiveSupport::Deprecation
       end
 
       def warn_once(message, delay = 5)

From ff5193fd60bc120c13233699e5c738a35cb2eeba Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 12 Nov 2024 20:38:58 +0100
Subject: [PATCH 096/157] Cleanup old monkey patch

---
 lib/datagrid/drivers/abstract_driver.rb     | 7 -------
 lib/datagrid/drivers/active_record.rb       | 8 --------
 lib/datagrid/utils.rb                       | 6 +++---
 spec/datagrid/drivers/active_record_spec.rb | 4 ++--
 4 files changed, 5 insertions(+), 20 deletions(-)

diff --git a/lib/datagrid/drivers/abstract_driver.rb b/lib/datagrid/drivers/abstract_driver.rb
index 6125e53..b663558 100644
--- a/lib/datagrid/drivers/abstract_driver.rb
+++ b/lib/datagrid/drivers/abstract_driver.rb
@@ -93,13 +93,6 @@ def default_cache_key(asset)
         raise NotImplementedError
       end
 
-      def where_by_timestamp_gotcha(scope, name, value)
-        value = Datagrid::Utils.format_date_as_timestamp(value)
-        scope = greater_equal(scope, name, value.first) if value.first
-        scope = less_equal(scope, name, value.last) if value.last
-        scope
-      end
-
       def default_preload(scope, value)
         raise NotImplementedError
       end
diff --git a/lib/datagrid/drivers/active_record.rb b/lib/datagrid/drivers/active_record.rb
index 282dccd..bf10cac 100644
--- a/lib/datagrid/drivers/active_record.rb
+++ b/lib/datagrid/drivers/active_record.rb
@@ -142,11 +142,3 @@ def contains_predicate
     end
   end
 end
-
-if defined?(ActiveRecord::Base)
-  ActiveRecord::Base.class_eval do
-    def self.datagrid_where_by_timestamp(column, value)
-      Datagrid::Drivers::ActiveRecord.new.where_by_timestamp_gotcha(self, column, value)
-    end
-  end
-end
diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index fea258f..3d24995 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -120,10 +120,10 @@ def parse_datetime(value)
       def format_date_as_timestamp(value)
         if !value
           value
-        elsif value.is_a?(Array)
-          [value.first&.beginning_of_day, value.last&.end_of_day]
+        # elsif value.is_a?(Array)
+          # value.first&.beginning_of_day..value.last&.end_of_day
         elsif value.is_a?(Range)
-          (value.begin&.beginning_of_day..value.end&.end_of_day)
+          value.begin&.beginning_of_day..value.end&.end_of_day
         else
           value.beginning_of_day..value.end_of_day
         end
diff --git a/spec/datagrid/drivers/active_record_spec.rb b/spec/datagrid/drivers/active_record_spec.rb
index 1dd671a..d99b77a 100644
--- a/spec/datagrid/drivers/active_record_spec.rb
+++ b/spec/datagrid/drivers/active_record_spec.rb
@@ -37,13 +37,13 @@
     end
   end
 
-  describe "gotcha #datagrid_where_by_timestamp" do
+  describe "where by timestamp" do
     subject do
       test_report(created_at: 10.days.ago..5.days.ago) do
         scope { Entry }
 
         filter(:created_at, :date, range: true) do |value, scope, _grid|
-          scope.joins(:group).datagrid_where_by_timestamp("groups.created_at", value)
+          scope.joins(:group).where(groups: {created_at: value})
         end
       end.assets
     end

From 3ab520c1c7bf10d3c0645fef87aa5841001dd4e5 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 12 Nov 2024 21:04:03 +0100
Subject: [PATCH 097/157] Forbid Range argument for non-range filter

---
 lib/datagrid/filters/base_filter.rb          | 10 ++++--
 lib/datagrid/filters/integer_filter.rb       |  4 +++
 spec/datagrid/filters/date_filter_spec.rb    | 24 ++++++++++---
 spec/datagrid/filters/integer_filter_spec.rb | 36 +++++++++++++++++++-
 4 files changed, 66 insertions(+), 8 deletions(-)

diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index f7f3074..0080d5a 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -54,12 +54,18 @@ def parse_values(value)
         if multiple?
           return nil if value.nil?
 
-          normalize_multiple_value(value).map do |v|
+          return normalize_multiple_value(value).map do |v|
             parse(v)
           end
-        elsif value.is_a?(Array)
+        end
+
+        case value
+        when Array
           raise Datagrid::ArgumentError,
             "#{grid_class}##{name} filter can not accept Array argument. Use :multiple option."
+        when Range
+          raise Datagrid::ArgumentError,
+            "#{grid_class}##{name} filter can not accept Range argument. Use :range option."
         else
           parse(value)
         end
diff --git a/lib/datagrid/filters/integer_filter.rb b/lib/datagrid/filters/integer_filter.rb
index 79ff5f8..4ebde9b 100644
--- a/lib/datagrid/filters/integer_filter.rb
+++ b/lib/datagrid/filters/integer_filter.rb
@@ -19,6 +19,10 @@ def parse(value)
         end
         return value if value.is_a?(Range)
 
+        if value.to_i == 0 && value.is_a?(String) && value !~ %r{\A\s*-?0}
+          return nil
+        end
+
         value.to_i
       end
     end
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 72fa450..fa26d34 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -7,14 +7,28 @@
   it "supports date range argument" do
     e1 = Entry.create!(created_at: 7.days.ago)
     e2 = Entry.create!(created_at: 4.days.ago)
-    e3 = Entry.create!(created_at: 1.day.ago)
+    e3 = Entry.create!(created_at: 3.days.ago)
+    e4 = Entry.create!(created_at: 1.day.ago)
+
     report = test_report(created_at: 5.day.ago..3.days.ago) do
       scope { Entry }
-      filter(:created_at, :date)
+      filter(:created_at, :date, range: true)
     end
+
+    expect(report.created_at).to eq(5.days.ago.to_date..3.days.ago.to_date)
     expect(report.assets).not_to include(e1)
     expect(report.assets).to include(e2)
-    expect(report.assets).not_to include(e3)
+    expect(report.assets).to include(e3)
+    expect(report.assets).not_to include(e4)
+  end
+
+  it "raises when range assigned to non-range filter" do
+    expect {
+      test_report(created_at: 5.day.ago..3.days.ago) do
+        scope { Entry }
+        filter(:created_at, :date)
+      end
+    }.to raise_error(ArgumentError)
   end
 
   it "endless date range argument" do
@@ -22,7 +36,7 @@
     e2 = Entry.create!(created_at: 4.days.ago)
     report = test_report(created_at: 5.days.ago..) do
       scope { Entry }
-      filter(:created_at, :date)
+      filter(:created_at, :date, range: true)
     end
     expect(report.assets).not_to include(e1)
     expect(report.assets).to include(e2)
@@ -31,7 +45,7 @@
     expect(report.assets).not_to include(e2)
   end
 
-  it "supports hash argument" do
+  it "supports hash argument for range filter" do
     report = test_report do
       scope { Entry }
       filter(:created_at, :date, range: true)
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index 913249d..776d58f 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -13,7 +13,7 @@
   it "should support integer range argument" do
     report = test_report(group_id: 3..5) do
       scope { Entry }
-      filter(:group_id, :integer)
+      filter(:group_id, :integer, range: true)
     end
     expect(report.assets).not_to include(entry1)
     expect(report.assets).to include(entry4)
@@ -170,4 +170,38 @@
     report.group_id = (nil...nil).as_json
     expect(report.group_id).to eq(nil)
   end
+
+  it "type casts value" do
+    report = test_report do
+      scope { Entry }
+      filter(:group_id, :integer)
+    end
+
+    report.group_id = "1"
+    expect(report.group_id).to eq(1)
+
+    report.group_id = " 1 "
+    expect(report.group_id).to eq(1)
+
+    report.group_id = 1.1
+    expect(report.group_id).to eq(1)
+
+    report.group_id = "1.1"
+    expect(report.group_id).to eq(1)
+
+    report.group_id = "-1"
+    expect(report.group_id).to eq(-1)
+
+    report.group_id = "-1.1"
+    expect(report.group_id).to eq(-1)
+
+    report.group_id = "1a"
+    expect(report.group_id).to eq(1)
+
+    report.group_id = "aa"
+    expect(report.group_id).to eq(nil)
+
+    report.group_id = "a1"
+    expect(report.group_id).to eq(nil)
+  end
 end

From 7aa28321257ee2f656fadb1446d125e83ae3ac78 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 12 Nov 2024 21:28:19 +0100
Subject: [PATCH 098/157] Add date range deserialization test

---
 lib/datagrid/filters/date_filter.rb          |  8 ++++++--
 spec/datagrid/filters/date_filter_spec.rb    | 11 +++++++++++
 spec/datagrid/filters/integer_filter_spec.rb |  2 +-
 3 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index 9793698..f3eccd8 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -12,7 +12,9 @@ def default_input_options
       end
 
       def apply(grid_object, scope, value)
-        value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
+        if value.is_a?(Range)
+          value = value.begin&.beginning_of_day..value.end&.end_of_day
+        end
         super
       end
 
@@ -29,7 +31,9 @@ def format(value)
       end
 
       def default_filter_where(scope, value)
-        value = Datagrid::Utils.format_date_as_timestamp(value) if driver.is_timestamp?(scope, name)
+        if driver.is_timestamp?(scope, name)
+          value = Datagrid::Utils.format_date_as_timestamp(value)
+        end
         super
       end
 
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index fa26d34..288b1cd 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -239,4 +239,15 @@ def entry_dated(date)
       expect(report.filter_value_as_string(:created_at)).to eq("01/02/2012")
     end
   end
+
+  it "deserializes range" do
+    report = test_report do
+      scope  { Entry }
+      filter(:created_at, :date, range: true)
+    end
+
+    value = Date.new(2012, 1, 1)..Date.new(2012, 1, 2)
+    report.created_at = value.as_json
+    expect(report.created_at).to eq(value)
+  end
 end
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index 776d58f..19ed985 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -53,7 +53,7 @@
   end
 
   it "should find something in one integer interval" do
-    report = test_report(group_id: (4..4)) do
+    report = test_report(group_id: 4) do
       scope { Entry }
       filter(:group_id, :integer, range: true)
     end

From 12de0d4bbf1f68992fb2ada961843f8ec3a2d53f Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 13 Nov 2024 11:12:25 +0100
Subject: [PATCH 099/157] Fix regression for boolean filter default value

---
 lib/datagrid/filters.rb                         | 7 +++----
 lib/datagrid/filters/base_filter.rb             | 2 +-
 lib/datagrid/filters/boolean_filter.rb          | 7 +++++++
 lib/datagrid/filters/dynamic_filter.rb          | 8 +++-----
 lib/datagrid/filters/enum_filter.rb             | 7 ++++---
 lib/datagrid/filters/extended_boolean_filter.rb | 2 +-
 6 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index c5fee88..7ec0ae4 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -100,7 +100,7 @@ def filter(name, type = :default, **options, &block)
         raise ConfigurationError, "filter class #{type.inspect} not found" unless klass
 
         position = Datagrid::Utils.extract_position_from_options(filters_array, options)
-        filter = klass.new(self, name, options, &block)
+        filter = klass.new(self, name, **options, &block)
         filters_array.insert(position, filter)
 
         datagrid_attribute(name) do |value|
@@ -144,9 +144,8 @@ def filters_inspection
     def initialize(...)
       self.filters_array = self.class.filters_array.clone
       filters_array.each do |filter|
-        if (value = filter.default(self))
-          self[filter.name] = value
-        end
+        value = filter.default(self)
+        self[filter.name] = value unless value.nil?
       end
       super
     end
diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index 0080d5a..cb2ec9a 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -13,7 +13,7 @@ module Filters
     class BaseFilter
       attr_accessor :grid_class, :options, :block, :name
 
-      def initialize(grid_class, name, options = {}, &block)
+      def initialize(grid_class, name, **options, &block)
         self.grid_class = grid_class
         self.name = name.to_sym
         self.options = options
diff --git a/lib/datagrid/filters/boolean_filter.rb b/lib/datagrid/filters/boolean_filter.rb
index 6c16209..f6ff874 100644
--- a/lib/datagrid/filters/boolean_filter.rb
+++ b/lib/datagrid/filters/boolean_filter.rb
@@ -5,6 +5,13 @@
 module Datagrid
   module Filters
     class BooleanFilter < Datagrid::Filters::BaseFilter
+
+      # @!visibility private
+      def initialize(grid, name, **opts)
+        super(grid, name, **opts)
+        options[:default] ||= false
+      end
+
       def default_input_options
         { **super, type: "checkbox" }
       end
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index 8ac9d2e..b05721a 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -19,13 +19,11 @@ class DynamicFilter < Datagrid::Filters::BaseFilter
       ].freeze
       AVAILABLE_OPERATIONS = %w[= =~ >= <=].freeze
 
-      def initialize(*)
-        super
+      def initialize(grid, name, **options, &block)
         options[:select] ||= default_select
         options[:operations] ||= DEFAULT_OPERATIONS
-        return if options.key?(:include_blank)
-
-        options[:include_blank] = false
+        options[:include_blank] = false unless  options.key?(:include_blank)
+        super(grid, name, **options, &block)
       end
 
       def default_input_options
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 0c9e15e..ac21aac 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -7,9 +7,10 @@ module Filters
     class EnumFilter < Datagrid::Filters::BaseFilter
       include Datagrid::Filters::SelectOptions
 
-      def initialize(*args)
-        super
-        options[:multiple] = true if enum_checkboxes?
+      # @!visibility private
+      def initialize(grid, name, **options, &block)
+        options[:multiple] = true if options[:checkboxes]
+        super(grid, name, **options, &block)
         raise Datagrid::ConfigurationError, ":select option not specified" unless options[:select]
       end
 
diff --git a/lib/datagrid/filters/extended_boolean_filter.rb b/lib/datagrid/filters/extended_boolean_filter.rb
index ad8efa8..c771fd9 100644
--- a/lib/datagrid/filters/extended_boolean_filter.rb
+++ b/lib/datagrid/filters/extended_boolean_filter.rb
@@ -9,7 +9,7 @@ class ExtendedBooleanFilter < Datagrid::Filters::EnumFilter
       TRUTH_VALUES = [true, "true", "y", "yes"].freeze
       FALSE_VALUES = [false, "false", "n", "no"].freeze
 
-      def initialize(report, attribute, options = {}, &block)
+      def initialize(*args, **options)
         options[:select] = -> { boolean_select }
         super
       end

From 5bf9208d184c8acc8f18b27033790cb954f8e2b1 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 13 Nov 2024 11:20:28 +0100
Subject: [PATCH 100/157] Add separator localization to upgrade guide

---
 version-2/Readme.markdown | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index d4d5d13..2538e17 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -20,6 +20,7 @@ List of things introduces:
 1. HTML5 data attributes
 1. Inherit `Datagrid::Base` instead of `include Datagrid`
 1. `ApplicationGrid` is recommended base class instead of `BaseGrid`
+1. Introduced `datagrid.filters.range.separator` localization
 1. Remove SASS dependency
 1. Replace `rake datagrid:copy_partials` with `rails g datagrid:views`
 
@@ -398,6 +399,14 @@ class UsersGrid < ApplicationGrid
 end
 ```
 
+## Introduced range filter separator localization
+
+A separator symbol between range filter inputs is now a part of localizations to avoid hardcore.
+Add `datagrid.filters.range.separator` to your localization file.
+
+[See commit for details](https://github.com/bogdan/datagrid/commit/2bd914a39a5f8367758ad697d7ccf8d98379fff7#diff-0e78e11f3e693a6523052bc71095ec539fa390cf04b50d74c35c9af5260f50f3L2)
+
+
 ## Remove SASS dependency
 
 SASS is no longer a default choice when starting a Ruby on Rails project.

From ee983ffb49cb30000c7c5c95b58af805f8810d43 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 13 Nov 2024 11:35:36 +0100
Subject: [PATCH 101/157] linting

---
 Gemfile                                      |   3 +-
 lib/datagrid/core.rb                         |   3 -
 lib/datagrid/filters/boolean_filter.rb       |   3 +-
 lib/datagrid/filters/date_filter.rb          |   8 +-
 lib/datagrid/filters/dynamic_filter.rb       |   6 +-
 lib/datagrid/filters/enum_filter.rb          |   2 +-
 lib/datagrid/filters/integer_filter.rb       |   4 +-
 lib/datagrid/filters/ranged_filter.rb        |   2 +-
 lib/datagrid/form_builder.rb                 |   8 +-
 lib/datagrid/generators/scaffold.rb          | 260 +++++++++----------
 lib/datagrid/generators/views.rb             |   4 +-
 lib/datagrid/helper.rb                       |  13 +-
 lib/datagrid/renderer.rb                     |   7 +-
 lib/datagrid/utils.rb                        |  14 +-
 spec/datagrid/drivers/active_record_spec.rb  |   2 +-
 spec/datagrid/filters/base_filter_spec.rb    |   3 +-
 spec/datagrid/filters/boolean_filter_spec.rb |   4 +-
 spec/datagrid/filters/date_filter_spec.rb    |   4 +-
 spec/datagrid/form_builder_spec.rb           |   4 +-
 spec/datagrid/helper_spec.rb                 |  56 ++--
 spec/spec_helper.rb                          |   2 +-
 version-2/find_broken_range_filters.rb       |   2 +
 22 files changed, 201 insertions(+), 213 deletions(-)

diff --git a/Gemfile b/Gemfile
index 55d01f1..cf4744e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -14,13 +14,12 @@ group :development do
   gem "pry-byebug"
   gem "rails-dom-testing", "~> 2.2"
   gem "rspec"
+  gem "rubocop", "~> 1.68"
   gem "sequel"
   gem "sqlite3", "~> 1.7.0"
-  gem "rubocop", "~> 1.68"
 
   group :mongo do
     gem "bson"
     gem "mongoid", "~> 9.0"
   end
 end
-
diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index e11830a..8bc04d8 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -161,9 +161,6 @@ def attributes
     #   grid.attributes = {first_name: 'John', last_name: 'Smith'}
     #   grid.first_name # => 'John'
     #   grid.last_name # => 'Smith'
-    def attributes=(value)
-      super
-    end
 
     # @return [Object] Any datagrid attribute value
     def [](attribute)
diff --git a/lib/datagrid/filters/boolean_filter.rb b/lib/datagrid/filters/boolean_filter.rb
index f6ff874..753f3da 100644
--- a/lib/datagrid/filters/boolean_filter.rb
+++ b/lib/datagrid/filters/boolean_filter.rb
@@ -5,10 +5,9 @@
 module Datagrid
   module Filters
     class BooleanFilter < Datagrid::Filters::BaseFilter
-
       # @!visibility private
       def initialize(grid, name, **opts)
-        super(grid, name, **opts)
+        super
         options[:default] ||= false
       end
 
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index f3eccd8..9793698 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -12,9 +12,7 @@ def default_input_options
       end
 
       def apply(grid_object, scope, value)
-        if value.is_a?(Range)
-          value = value.begin&.beginning_of_day..value.end&.end_of_day
-        end
+        value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
         super
       end
 
@@ -31,9 +29,7 @@ def format(value)
       end
 
       def default_filter_where(scope, value)
-        if driver.is_timestamp?(scope, name)
-          value = Datagrid::Utils.format_date_as_timestamp(value)
-        end
+        value = Datagrid::Utils.format_date_as_timestamp(value) if driver.is_timestamp?(scope, name)
         super
       end
 
diff --git a/lib/datagrid/filters/dynamic_filter.rb b/lib/datagrid/filters/dynamic_filter.rb
index b05721a..9926ff0 100644
--- a/lib/datagrid/filters/dynamic_filter.rb
+++ b/lib/datagrid/filters/dynamic_filter.rb
@@ -22,12 +22,12 @@ class DynamicFilter < Datagrid::Filters::BaseFilter
       def initialize(grid, name, **options, &block)
         options[:select] ||= default_select
         options[:operations] ||= DEFAULT_OPERATIONS
-        options[:include_blank] = false unless  options.key?(:include_blank)
-        super(grid, name, **options, &block)
+        options[:include_blank] = false unless options.key?(:include_blank)
+        super
       end
 
       def default_input_options
-        {**super, type: nil}
+        { **super, type: nil }
       end
 
       def parse_values(filter)
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index ac21aac..ec154a8 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -10,7 +10,7 @@ class EnumFilter < Datagrid::Filters::BaseFilter
       # @!visibility private
       def initialize(grid, name, **options, &block)
         options[:multiple] = true if options[:checkboxes]
-        super(grid, name, **options, &block)
+        super
         raise Datagrid::ConfigurationError, ":select option not specified" unless options[:select]
       end
 
diff --git a/lib/datagrid/filters/integer_filter.rb b/lib/datagrid/filters/integer_filter.rb
index 4ebde9b..26146ff 100644
--- a/lib/datagrid/filters/integer_filter.rb
+++ b/lib/datagrid/filters/integer_filter.rb
@@ -19,9 +19,7 @@ def parse(value)
         end
         return value if value.is_a?(Range)
 
-        if value.to_i == 0 && value.is_a?(String) && value !~ %r{\A\s*-?0}
-          return nil
-        end
+        return nil if value.to_i.zero? && value.is_a?(String) && value !~ %r{\A\s*-?0}
 
         value.to_i
       end
diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index a18cb18..421a13a 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -3,7 +3,7 @@
 module Datagrid
   module Filters
     module RangedFilter
-      SERIALIZED_RANGE = %r{\A(.*)\.{2,3}(.*)\z}.freeze
+      SERIALIZED_RANGE = %r{\A(.*)\.{2,3}(.*)\z}
 
       def parse_values(value)
         return super unless range?
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index aec1cc0..b901822 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -7,9 +7,9 @@ module FormBuilder
     # @param filter_or_attribute [Datagrid::Filters::BaseFilter, String, Symbol] filter object or filter name
     # @param options [Hash] options of rails form input helper
     # @return [String] a form input html for the corresponding filter
-    def datagrid_filter(filter_or_attribute, **options, &block)
+    def datagrid_filter(filter_or_attribute, ...)
       filter = datagrid_get_filter(filter_or_attribute)
-      send(filter.form_builder_helper_name, filter, **options, &block)
+      send(filter.form_builder_helper_name, filter, ...)
     end
 
     # @param filter_or_attribute [Datagrid::Filters::BaseFilter, String, Symbol] filter object or filter name
@@ -44,9 +44,7 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
         text_area filter.name, value: object.filter_value_as_string(filter), **options, &block
       when :checkbox
         value = options.fetch(:value, 1).to_s
-        if filter.enum_checkboxes? && enum_checkbox_checked?(filter, value)
-          options = {checked: true, **options}
-        end
+        options = { checked: true, **options } if filter.enum_checkboxes? && enum_checkbox_checked?(filter, value)
         check_box filter.name, options, value
       when :hidden
         hidden_field filter.name, **options
diff --git a/lib/datagrid/generators/scaffold.rb b/lib/datagrid/generators/scaffold.rb
index 0956e46..172c8b5 100644
--- a/lib/datagrid/generators/scaffold.rb
+++ b/lib/datagrid/generators/scaffold.rb
@@ -6,168 +6,168 @@
 module Datagrid
   # @!visibility private
   module Generators
-  # @!visibility private
-  class Scaffold < Rails::Generators::NamedBase
-    include Rails::Generators::ResourceHelpers
-
-    check_class_collision suffix: "Grid"
-    source_root File.expand_path("#{__FILE__}/../../../templates")
-
-    def create_scaffold
-      template "base.rb.erb", base_grid_file unless file_exists?(base_grid_file)
-      template "grid.rb.erb", "app/grids/#{grid_class_name.underscore}.rb"
-      if file_exists?(grid_controller_file)
-        inject_into_file grid_controller_file, index_action, after: %r{class .*#{grid_controller_class_name}.*\n}
-      else
-        create_file grid_controller_file, controller_code
-      end
-      create_file view_file, view_code
-      route(generate_routing_namespace("resources :#{grid_controller_short_name}"))
-      gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate) || defined?(::Pagy)
-      in_root do
-        {
-          "css" => " *= require datagrid",
-          "css.sass" => " *= require datagrid",
-          "css.scss" => " *= require datagrid",
-        }.each do |extension, string|
-          file = "app/assets/stylesheets/application.#{extension}"
-          if file_exists?(file)
-            inject_into_file file, "#{string}\n", { before: %r{.*require_self} } # before all
+    # @!visibility private
+    class Scaffold < Rails::Generators::NamedBase
+      include Rails::Generators::ResourceHelpers
+
+      check_class_collision suffix: "Grid"
+      source_root File.expand_path("#{__FILE__}/../../../templates")
+
+      def create_scaffold
+        template "base.rb.erb", base_grid_file unless file_exists?(base_grid_file)
+        template "grid.rb.erb", "app/grids/#{grid_class_name.underscore}.rb"
+        if file_exists?(grid_controller_file)
+          inject_into_file grid_controller_file, index_action, after: %r{class .*#{grid_controller_class_name}.*\n}
+        else
+          create_file grid_controller_file, controller_code
+        end
+        create_file view_file, view_code
+        route(generate_routing_namespace("resources :#{grid_controller_short_name}"))
+        gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate) || defined?(::Pagy)
+        in_root do
+          {
+            "css" => " *= require datagrid",
+            "css.sass" => " *= require datagrid",
+            "css.scss" => " *= require datagrid",
+          }.each do |extension, string|
+            file = "app/assets/stylesheets/application.#{extension}"
+            if file_exists?(file)
+              inject_into_file file, "#{string}\n", { before: %r{.*require_self} } # before all
+            end
           end
         end
       end
-    end
 
-    def view_file
-      Rails.root.join("app/views").join(controller_file_path).join("index.html.erb")
-    end
+      def view_file
+        Rails.root.join("app/views").join(controller_file_path).join("index.html.erb")
+      end
 
-    def grid_class_name
-      "#{file_name.camelize.pluralize}Grid"
-    end
+      def grid_class_name
+        "#{file_name.camelize.pluralize}Grid"
+      end
 
-    def grid_base_class
-      file_exists?("app/grids/base_grid.rb") ? "BaseGrid" : "ApplicationGrid"
-    end
+      def grid_base_class
+        file_exists?("app/grids/base_grid.rb") ? "BaseGrid" : "ApplicationGrid"
+      end
 
-    def grid_controller_class_name
-      "#{controller_class_name.camelize}Controller"
-    end
+      def grid_controller_class_name
+        "#{controller_class_name.camelize}Controller"
+      end
 
-    def grid_controller_file
-      Rails.root.join("app/controllers").join("#{grid_controller_class_name.underscore}.rb")
-    end
+      def grid_controller_file
+        Rails.root.join("app/controllers").join("#{grid_controller_class_name.underscore}.rb")
+      end
 
-    def grid_controller_short_name
-      controller_file_name
-    end
+      def grid_controller_short_name
+        controller_file_name
+      end
 
-    def grid_model_name
-      file_name.camelize.singularize
-    end
+      def grid_model_name
+        file_name.camelize.singularize
+      end
 
-    def grid_param_name
-      grid_class_name.underscore
-    end
+      def grid_param_name
+        grid_class_name.underscore
+      end
 
-    def pagination_helper_code
-      if defined?(::WillPaginate)
-        "will_paginate(@grid.assets)"
-      elsif defined?(::Pagy)
-        "pagy_nav(@pagy)"
-      else
-        # Kaminari is default
-        "paginate(@grid.assets)"
+      def pagination_helper_code
+        if defined?(::WillPaginate)
+          "will_paginate(@grid.assets)"
+        elsif defined?(::Pagy)
+          "pagy_nav(@pagy)"
+        else
+          # Kaminari is default
+          "paginate(@grid.assets)"
+        end
       end
-    end
 
-    def table_helper_code
-      if defined?(::Pagy)
-        "datagrid_table @grid, @records"
-      else
-        "datagrid_table @grid"
+      def table_helper_code
+        if defined?(::Pagy)
+          "datagrid_table @grid, @records"
+        else
+          "datagrid_table @grid"
+        end
       end
-    end
 
-    def base_grid_file
-      "app/grids/application_grid.rb"
-    end
+      def base_grid_file
+        "app/grids/application_grid.rb"
+      end
 
-    def grid_route_name
-      "#{controller_class_name.underscore.gsub('/', '_')}_path"
-    end
+      def grid_route_name
+        "#{controller_class_name.underscore.gsub('/', '_')}_path"
+      end
 
-    def index_code
-      if defined?(::Pagy)
-        <<-RUBY
+      def index_code
+        if defined?(::Pagy)
+          <<-RUBY
     @grid = #{grid_class_name}.new(grid_params)
     @pagy, @assets = pagy(@grid.assets)
-        RUBY
-      else
-        <<-RUBY
+          RUBY
+        else
+          <<-RUBY
     @grid = #{grid_class_name}.new(grid_params) do |scope|
       scope.page(params[:page])
     end
-        RUBY
+          RUBY
+        end
       end
-    end
 
-    def controller_code
-      <<~RUBY
-        class #{grid_controller_class_name} < ApplicationController
-          def index
-        #{index_code.rstrip}
-          end
+      def controller_code
+        <<~RUBY
+          class #{grid_controller_class_name} < ApplicationController
+            def index
+          #{index_code.rstrip}
+            end
 
-          protected
+            protected
 
-          def grid_params
-            params.fetch(:#{grid_param_name}, {}).permit!
+            def grid_params
+              params.fetch(:#{grid_param_name}, {}).permit!
+            end
           end
-        end
-      RUBY
-    end
+        RUBY
+      end
 
-    def view_code
-      indent(<<~ERB)
-        <%= datagrid_form_with model: @grid, url: #{grid_route_name} %>
+      def view_code
+        indent(<<~ERB)
+          <%= datagrid_form_with model: @grid, url: #{grid_route_name} %>
 
-        <%= #{pagination_helper_code} %>
-        <%= #{table_helper_code} %>
-        <%= #{pagination_helper_code} %>
-      ERB
-    end
+          <%= #{pagination_helper_code} %>
+          <%= #{table_helper_code} %>
+          <%= #{pagination_helper_code} %>
+        ERB
+      end
 
-    protected
-
-    def generate_routing_namespace(code)
-      depth = regular_class_path.length
-      # Create 'namespace' ladder
-      # namespace :foo do
-      #   namespace :bar do
-      namespace_ladder = regular_class_path.each_with_index.map do |ns, i|
-        indent("namespace :#{ns} do\n", i * 2)
-      end.join
-
-      # Create route
-      #     get 'baz/index'
-      route = indent(code, depth * 2)
-
-      # Create `end` ladder
-      #   end
-      # end
-      end_ladder = (1..depth).reverse_each.map do |i|
-        indent("end\n", i * 2)
-      end.join
-
-      # Combine the 3 parts to generate complete route entry
-      "#{namespace_ladder}#{route}\n#{end_ladder}"
-    end
+      protected
+
+      def generate_routing_namespace(code)
+        depth = regular_class_path.length
+        # Create 'namespace' ladder
+        # namespace :foo do
+        #   namespace :bar do
+        namespace_ladder = regular_class_path.each_with_index.map do |ns, i|
+          indent("namespace :#{ns} do\n", i * 2)
+        end.join
+
+        # Create route
+        #     get 'baz/index'
+        route = indent(code, depth * 2)
+
+        # Create `end` ladder
+        #   end
+        # end
+        end_ladder = (1..depth).reverse_each.map do |i|
+          indent("end\n", i * 2)
+        end.join
+
+        # Combine the 3 parts to generate complete route entry
+        "#{namespace_ladder}#{route}\n#{end_ladder}"
+      end
 
-    def file_exists?(name)
-      name = Rails.root.join(name) unless name.to_s.first == "/"
-      File.exist?(name)
+      def file_exists?(name)
+        name = Rails.root.join(name) unless name.to_s.first == "/"
+        File.exist?(name)
+      end
     end
   end
-  end
 end
diff --git a/lib/datagrid/generators/views.rb b/lib/datagrid/generators/views.rb
index 0a85252..3f00a35 100644
--- a/lib/datagrid/generators/views.rb
+++ b/lib/datagrid/generators/views.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   module Generators
     class Views < Rails::Generators::Base
@@ -6,7 +8,7 @@ class Views < Rails::Generators::Base
       desc "Copies Datagrid partials to your application."
       def copy_views
         Dir.glob(File.join(self.class.source_root, "**", "*")).each do |file_path|
-          relative_path = file_path.sub(self.class.source_root + "/", "")
+          relative_path = file_path.sub("#{self.class.source_root}/", "")
 
           next if relative_path == "_order_for.html.erb"
 
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index dc5e157..f18b124 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -114,9 +114,8 @@ def datagrid_order_for(grid, column, options = {})
     # @param grid [Datagrid] grid object
     # @return [String] form HTML tag markup
     def datagrid_form_with(**options)
-      if block_given?
-        raise ArgumentError, 'datagrid_form_with block argument is invalid. Use form_with instead.'
-      end
+      raise ArgumentError, "datagrid_form_with block argument is invalid. Use form_with instead." if block_given?
+
       datagrid_renderer.form_with(**options)
     end
 
@@ -155,8 +154,8 @@ def datagrid_form_for(grid, options = {})
     #   Last Name: <%= row.last_name %>
     # @example
     #   <%= datagrid_row(grid, user, columns: [:first_name, :last_name, :actions]) %>
-    def datagrid_row(grid, asset, **options, &block)
-      datagrid_renderer.row(grid, asset, **options, &block)
+    def datagrid_row(grid, asset, ...)
+      datagrid_renderer.row(grid, asset, ...)
     end
 
     # Generates an ascending or descending order url for the given column
@@ -176,8 +175,8 @@ def datagrid_renderer
 
     def datagrid_column_classes(grid, column)
       Datagrid::Utils.warn_once(<<~MSG)
-      datagrid_column_classes is deprecated. Assign necessary classes manually.
-      Correspond to default datagrid/rows partial for example.)
+        datagrid_column_classes is deprecated. Assign necessary classes manually.
+        Correspond to default datagrid/rows partial for example.)
       MSG
       column = grid.column_by_name(column)
       order_class = if grid.ordered_by?(column)
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
index 8423cec..e2fbbc6 100644
--- a/lib/datagrid/renderer.rb
+++ b/lib/datagrid/renderer.rb
@@ -27,16 +27,15 @@ def form_for(grid, options = {})
           method: :get,
           as: grid.param_name,
           local: true,
-          **options
+          **options,
         },
       )
     end
 
     def form_with(**options)
       grid = options[:model]
-      if grid&.filters&.empty?
-        raise ArgumentError, "Grid has no available filters"
-      end
+      raise ArgumentError, "Grid has no available filters" if grid&.filters&.empty?
+
       _render_partial("form", options[:partials], { grid: options[:model], options: options })
     end
 
diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index 3d24995..f97707d 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -24,12 +24,14 @@ def translate_from_namespace(namespace, grid_class, key)
       end
 
       def deprecator
-        defined?(Rails) && Rails.version >= "7.1.0" ?
-          Rails.deprecator : ActiveSupport::Deprecation
+        if defined?(Rails) && Rails.version >= "7.1.0"
+          Rails.deprecator
+        else
+          ActiveSupport::Deprecation
+        end
       end
 
       def warn_once(message, delay = 5)
-
         @warnings ||= {}
         timestamp = @warnings[message]
         return false if timestamp && timestamp >= Time.now - delay
@@ -99,9 +101,7 @@ def parse_date(value)
       def parse_datetime(value)
         return nil if value.blank?
         return value if value.is_a?(Range)
-        if defined?(ActiveSupport::TimeWithZone) && value.is_a?(ActiveSupport::TimeWithZone)
-          return value
-        end
+        return value if defined?(ActiveSupport::TimeWithZone) && value.is_a?(ActiveSupport::TimeWithZone)
 
         if value.is_a?(String)
           Array(Datagrid.configuration.datetime_formats).each do |format|
@@ -120,7 +120,7 @@ def parse_datetime(value)
       def format_date_as_timestamp(value)
         if !value
           value
-        # elsif value.is_a?(Array)
+          # elsif value.is_a?(Array)
           # value.first&.beginning_of_day..value.last&.end_of_day
         elsif value.is_a?(Range)
           value.begin&.beginning_of_day..value.end&.end_of_day
diff --git a/spec/datagrid/drivers/active_record_spec.rb b/spec/datagrid/drivers/active_record_spec.rb
index d99b77a..872cb49 100644
--- a/spec/datagrid/drivers/active_record_spec.rb
+++ b/spec/datagrid/drivers/active_record_spec.rb
@@ -43,7 +43,7 @@
         scope { Entry }
 
         filter(:created_at, :date, range: true) do |value, scope, _grid|
-          scope.joins(:group).where(groups: {created_at: value})
+          scope.joins(:group).where(groups: { created_at: value })
         end
       end.assets
     end
diff --git a/spec/datagrid/filters/base_filter_spec.rb b/spec/datagrid/filters/base_filter_spec.rb
index 9a6340f..9f86ec2 100644
--- a/spec/datagrid/filters/base_filter_spec.rb
+++ b/spec/datagrid/filters/base_filter_spec.rb
@@ -17,12 +17,11 @@ def name_default
   end
 
   describe "#default_scope?" do
-
     it "identifies filters without custom block" do
       grid = test_report do
         scope { Entry }
         filter(:id, :integer)
-        filter(:group_id, :integer) do |value, scope|
+        filter(:group_id, :integer) do |value, _scope|
           scope("group_id >= ?", value)
         end
       end
diff --git a/spec/datagrid/filters/boolean_filter_spec.rb b/spec/datagrid/filters/boolean_filter_spec.rb
index 96ed6cd..4b10491 100644
--- a/spec/datagrid/filters/boolean_filter_spec.rb
+++ b/spec/datagrid/filters/boolean_filter_spec.rb
@@ -1,4 +1,6 @@
-require 'spec_helper'
+# frozen_string_literal: true
+
+require "spec_helper"
 
 describe Datagrid::Filters::BooleanFilter do
   it "applies default filtering" do
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 288b1cd..d42646b 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -23,12 +23,12 @@
   end
 
   it "raises when range assigned to non-range filter" do
-    expect {
+    expect do
       test_report(created_at: 5.day.ago..3.days.ago) do
         scope { Entry }
         filter(:created_at, :date)
       end
-    }.to raise_error(ArgumentError)
+    end.to raise_error(ArgumentError)
   end
 
   it "endless date range argument" do
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 6279457..98f16fa 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -132,8 +132,8 @@ class MyTemplate
       context "datetime filter type is text" do
         let(:_filter) { :created_at }
         let(:_grid) do
-          created_at = ActiveSupport::TimeZone['UTC'].local(
-            2024, 1, 1, 9, 25, 15
+          created_at = ActiveSupport::TimeZone["UTC"].local(
+            2024, 1, 1, 9, 25, 15,
           )
           test_report(created_at: created_at) do
             scope { Entry }
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 4a8ec28..822c60e 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -431,10 +431,10 @@ class OrderedGrid < Datagrid::Base
       object = OrderedGrid.new(descending: true, order: :category)
       silence_deprecator do
         expect(subject.datagrid_order_for(object, object.column_by_name(:category))).to equal_to_dom(<<~HTML)
-        <div class="order">
-        <a class="asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
-        <a class="desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">&darr;</a>
-        </div>
+          <div class="order">
+          <a class="asc" href="/location?ordered_grid%5Bdescending%5D=false&amp;ordered_grid%5Border%5D=category">&uarr;</a>
+          <a class="desc" href="/location?ordered_grid%5Bdescending%5D=true&amp;ordered_grid%5Border%5D=category">&darr;</a>
+          </div>
         HTML
       end
     end
@@ -461,16 +461,16 @@ class FormForGrid < Datagrid::Base
       end
       object = FormForGrid.new(category: "hello")
       expect(subject.datagrid_form_for(object, url: "/grid")).to equal_to_dom(<<~HTML)
- <form class="datagrid-form" action="/grid" accept-charset="UTF-8" method="get">
-  <div class="datagrid-filter" data-filter="category" data-type="string">
-    <label for="form_for_grid_category">Category</label>
-    <input value="hello" type="text" name="form_for_grid[category]" id="form_for_grid_category" />
-  </div>
-  <div class="datagrid-actions">
-    <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
-    <a class="datagrid-reset" href="/location">Reset</a>
-  </div>
-</form>
+         <form class="datagrid-form" action="/grid" accept-charset="UTF-8" method="get">
+          <div class="datagrid-filter" data-filter="category" data-type="string">
+            <label for="form_for_grid_category">Category</label>
+            <input value="hello" type="text" name="form_for_grid[category]" id="form_for_grid_category" />
+          </div>
+          <div class="datagrid-actions">
+            <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
+            <a class="datagrid-reset" href="/location">Reset</a>
+          </div>
+        </form>
       HTML
     end
     it "should support html classes for grid class with namespace" do
@@ -517,7 +517,6 @@ def param_name
     end
   end
 
-
   describe ".datagrid_form_with" do
     it "returns namespaced partial if partials options is passed" do
       rendered_form = subject.datagrid_form_with(
@@ -534,16 +533,16 @@ class FormWithGrid < Datagrid::Base
       end
       object = FormWithGrid.new(category: "hello")
       expect(subject.datagrid_form_with(model: object, url: "/grid")).to equal_to_dom(<<~HTML)
- <form class="datagrid-form" action="/grid" accept-charset="UTF-8" method="get">
-  <div class="datagrid-filter" data-filter="category" data-type="string">
-    <label for="form_with_grid_category">Category</label>
-    <input value="hello" type="text" name="form_with_grid[category]" id="form_with_grid_category" />
-  </div>
-  <div class="datagrid-actions">
-    <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
-    <a class="datagrid-reset" href="/location">Reset</a>
-  </div>
-</form>
+         <form class="datagrid-form" action="/grid" accept-charset="UTF-8" method="get">
+          <div class="datagrid-filter" data-filter="category" data-type="string">
+            <label for="form_with_grid_category">Category</label>
+            <input value="hello" type="text" name="form_with_grid[category]" id="form_with_grid_category" />
+          </div>
+          <div class="datagrid-actions">
+            <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
+            <a class="datagrid-reset" href="/location">Reset</a>
+          </div>
+        </form>
       HTML
     end
     it "should support html classes for grid class with namespace" do
@@ -591,7 +590,6 @@ def param_name
     end
   end
 
-
   describe ".datagrid_row" do
     let(:grid) do
       test_report do
@@ -734,14 +732,14 @@ def param_name
       grid = test_report(order: :name, descending: true) do
         scope { Entry }
         column(:name)
-        column(:group_id, class: 'short-column')
+        column(:group_id, class: "short-column")
       end
       silence_deprecator do
         expect(subject.send(:datagrid_column_classes, grid, :name)).to eq(
-          "name ordered desc"
+          "name ordered desc",
         )
         expect(subject.send(:datagrid_column_classes, grid, :group_id)).to eq(
-          "group_id short-column"
+          "group_id short-column",
         )
       end
     end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4735698..2322b78 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -108,4 +108,4 @@ def silence_deprecator(&block)
 
 # Requires supporting files with custom matchers and macros, etc,
 # in ./support/ and its subdirectories.
-Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].sort.each { |f| require f }
+Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
diff --git a/version-2/find_broken_range_filters.rb b/version-2/find_broken_range_filters.rb
index 989643d..c53fff6 100644
--- a/version-2/find_broken_range_filters.rb
+++ b/version-2/find_broken_range_filters.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # Important in development to have all classes in memory
 Rails.application.eager_load!
 

From 61b0b5082f5f82967558d56962ef99984a494ffb Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 13 Nov 2024 21:09:49 +0100
Subject: [PATCH 102/157] Fix w3 validator warning for label[for] not
 corresponding to any input[id]

---
 app/views/datagrid/_range_filter.html.erb |  4 +++-
 lib/datagrid/form_builder.rb              | 11 ---------
 spec/datagrid/form_builder_spec.rb        | 26 ++++++++++-----------
 version-2/Readme.markdown                 | 28 ++++++++++++++++++++++-
 4 files changed, 43 insertions(+), 26 deletions(-)

diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
index 3b8ca85..b308d43 100644
--- a/app/views/datagrid/_range_filter.html.erb
+++ b/app/views/datagrid/_range_filter.html.erb
@@ -1,3 +1,5 @@
 <%= form.datagrid_filter_input(filter, class: 'datagrid-range-from', **from_options) %>
 <span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
-<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options) %>
+<%# Generating id only for "from" input to make sure 
+  # there is no duplicate id in DOM and click on label focuses the first input -%>
+<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index b901822..49e1d90 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -190,17 +190,6 @@ def datagrid_range_filter_options(object, filter, type, **options)
       type_method_map = { from: :begin, to: :end }
       options[:value] = object[filter.name]&.public_send(type_method_map[type])
       options[:name] = @template.field_name(object_name, filter.name, type)
-      # In case of datagrid ranged filter
-      # from and to input will have same id
-      if !options.key?(:id)
-        # Rails provides it's own default id for all inputs
-        # In order to prevent that we assign no id by default
-        options[:id] = nil
-      elsif options[:id].present?
-        # If the id was given we prefix it
-        # with from_ and to_ accordingly
-        options[:id] = [type, options[:id]].join("_")
-      end
       options
     end
 
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 98f16fa..89960be 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -189,10 +189,10 @@ class MyTemplate
         let(:_range) { [1, 2] }
         it {
           should equal_to_dom(
-            '<input value="1" id="from_hello" class="datagrid-range-from"
+            '<input value="1" id="hello" class="datagrid-range-from"
                 type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="2" id="to_hello" class="datagrid-range-to"
+            '<input value="2" class="datagrid-range-to"
                 type="number" step="1" name="report[group_id][to]"/>',
           )
         }
@@ -201,7 +201,7 @@ class MyTemplate
         let(:_range) { [10, nil] }
         it {
           should equal_to_dom(
-            '<input value="10" class="datagrid-range-from"
+            '<input value="10" class="datagrid-range-from" id="report_group_id"
                 type="number" step="1" name="report[group_id][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to"
@@ -214,7 +214,7 @@ class MyTemplate
         let(:_range) { [nil, 10] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]"/>' \
+            '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="10" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>',
           )
@@ -226,7 +226,7 @@ class MyTemplate
         let(:_range) { 2..1 }
         it {
           should equal_to_dom(
-            '<input value="1" class="datagrid-range-from" type="number" step="1" name="report[group_id][from]"/>' \
+            '<input value="1" class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>',
           )
@@ -248,7 +248,7 @@ class MyTemplate
         let(:_range) { nil }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]">
+            '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id">
             <span class="datagrid-range-separator"> - </span>
             <input class="datagrid-range-to" type="number" step="1" name="report[group_id][to]">',
           )
@@ -267,7 +267,7 @@ class MyTemplate
       let(:_range) { [1.5, 2.5] }
       it {
         should equal_to_dom(
-          '<input value="1.5" class="datagrid-range-from"
+          '<input value="1.5" class="datagrid-range-from" id="report_rating"
               type="number" step="any" name="report[rating][from]"/>' \
           '<span class="datagrid-range-separator"> - </span>' \
           '<input value="2.5" class="datagrid-range-to"
@@ -288,7 +288,7 @@ class MyTemplate
         let(:_range) { ["2012-01-03", nil] }
         it {
           should equal_to_dom(
-            '<input value="2012-01-03" class="datagrid-range-from" type="date" name="report[created_at][from]"/>' \
+            '<input value="2012-01-03" class="datagrid-range-from" type="date" name="report[created_at][from]" id="report_created_at"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to" type="date" name="report[created_at][to]" value=""/>',
           )
@@ -304,7 +304,7 @@ class MyTemplate
         let(:_range) { ["2013/01/01", "2013/02/02"] }
         it {
           should equal_to_dom(
-            '<input value="2013-01-01" class="datagrid-range-from"
+            '<input value="2013-01-01" class="datagrid-range-from" id="report_created_at"
                 type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2013-02-02" class="datagrid-range-to"
@@ -316,11 +316,11 @@ class MyTemplate
         let(:_range) { [nil, "2012-01-03"] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from"
+            '<input class="datagrid-range-from" id="report_created_at"
                 type="date" value="" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-03" class="datagrid-range-to"
-                type="date"  name="report[created_at][to]"/>',
+                type="date" name="report[created_at][to]"/>',
           )
         }
         it { should be_html_safe }
@@ -330,7 +330,7 @@ class MyTemplate
         let(:_range) { Date.parse("2012-01-02")..Date.parse("2012-01-01") }
         it {
           should equal_to_dom(
-            '<input value="2012-01-01" class="datagrid-range-from"
+            '<input value="2012-01-01" class="datagrid-range-from" id="report_created_at"
                 type="date" name="report[created_at][from]"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input value="2012-01-02" class="datagrid-range-to"
@@ -347,7 +347,7 @@ class MyTemplate
         let(:_range) { [nil, nil] }
         it {
           should equal_to_dom(
-            '<input class="datagrid-range-from" type="date" value="" name="report[created_at][from]"/>' \
+            '<input class="datagrid-range-from" type="date" value="" name="report[created_at][from]" id="report_created_at"/>' \
             '<span class="datagrid-range-separator"> - </span>' \
             '<input class="datagrid-range-to" type="date" value="" name="report[created_at][to]"/>',
           )
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 2538e17..68772e1 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -18,6 +18,7 @@ List of things introduces:
 1. Native Rails Engines.
    * while supported, the library was not initially designed for it.
 1. HTML5 data attributes
+1. Consistent id attribute for range filter inputs
 1. Inherit `Datagrid::Base` instead of `include Datagrid`
 1. `ApplicationGrid` is recommended base class instead of `BaseGrid`
 1. Introduced `datagrid.filters.range.separator` localization
@@ -373,6 +374,31 @@ Renders:
 [Modify built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
 if you want to change this behavior completely.
 
+## id attribute for range filter inputs
+
+[W3 validator](https://validator.w3.org/) complains when 
+a `label[for]` attribute doesn't correspond to any `input[id]` in the same form.
+Version 1 generated no id attribute for range filter inputs by default which resulted in a warning:
+
+``` html
+<label for="musics_grid_year">Year</label>
+<input class="year integer_fiilter from" multiple name="musics_grid[year][]" type="text">
+<span class="separator integer"> - </span>
+<input class="year integer_filter to" multiple name="musics_grid[year][]" type="text">
+```
+
+Version 2 generates id attribute only for the first input, so that a click on label focuses the first input:
+
+``` html
+<label for="musics_grid_year">Year</label>
+<input id="musics_grid_year" step="1" class="datagrid-range-from" name="musics_grid[year][from]" type="number">
+<span class="datagrid-range-separator"> - </span>
+<input step="1" class="datagrid-range-to" name="musics_grid[year][to]" type="number">
+```
+
+The behavior can be changed by modifying 
+[built-in view](https://github.com/bogdan/datagrid/blob/version-2/app/views/datagrid/_range_filter.html.erb#L3).
+
 ## ApplicationGrid base class
 
 Previously recommended base class `BaseGrid` is incosistent
@@ -404,7 +430,7 @@ end
 A separator symbol between range filter inputs is now a part of localizations to avoid hardcore.
 Add `datagrid.filters.range.separator` to your localization file.
 
-[See commit for details](https://github.com/bogdan/datagrid/commit/2bd914a39a5f8367758ad697d7ccf8d98379fff7#diff-0e78e11f3e693a6523052bc71095ec539fa390cf04b50d74c35c9af5260f50f3L2)
+[See related view](https://github.com/bogdan/datagrid/blob/version-2/app/views/datagrid/_range_filter.html.erb#L2)
 
 
 ## Remove SASS dependency

From 05cf3dec447ab23de1228ca8a9ab4a8a55573334 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 13 Nov 2024 21:27:27 +0100
Subject: [PATCH 103/157] Change error type

---
 lib/datagrid/form_builder.rb | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 49e1d90..09c8b6a 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -205,7 +205,7 @@ def datagrid_get_filter(attribute_or_filter)
       return attribute_or_filter unless Utils.string_like?(attribute_or_filter)
 
       object.class.filter_by_name(attribute_or_filter) ||
-        raise(Error, "Datagrid filter #{attribute_or_filter} not found")
+        raise(ArgumentError, "Datagrid filter #{attribute_or_filter} not found")
     end
 
     def add_html_classes(options, *classes)
@@ -228,8 +228,5 @@ def render_partial(name, locals)
     def add_filter_options(filter, **options)
       { **filter.default_input_options, **filter.input_options, **options }
     end
-
-    class Error < StandardError
-    end
   end
 end

From ff1924df9efc532a2311d3c3e3f469b37363c80b Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Wed, 13 Nov 2024 21:28:05 +0100
Subject: [PATCH 104/157] cleanup

---
 lib/datagrid/form_builder.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 09c8b6a..f603023 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -28,9 +28,9 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       options = add_filter_options(filter, **options)
       type = options.delete(:type)&.to_sym
       if %i[datetime-local date].include?(type)
-        if options.key?(:value) && options[:value].nil? &&
+        if options.key?(:value) && options[:value].nil?
            # https://github.com/rails/rails/pull/53387
-           (options[:value] = "")
+           options[:value] = ""
         end
       elsif options[:value]
         options[:value] = filter.format(options[:value])

From b4878f17c1c34edf4f9fcefaad818921ae46730b Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 09:57:36 +0100
Subject: [PATCH 105/157] Cleanup

---
 lib/datagrid/form_builder.rb | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 12be3fa..5788744 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -75,11 +75,11 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
     protected
 
     def datagrid_extended_boolean_filter(filter, options = {}, &block)
-      datagrid_filter_input(filter, **options, type: :select, &block)
+      datagrid_filter_input(filter, **options, &block)
     end
 
     def datagrid_boolean_filter(filter, options = {})
-      datagrid_filter_input(filter.name, **options, type: :checkbox)
+      datagrid_filter_input(filter.name, **options)
     end
 
     def datagrid_date_filter(filter, options = {})
@@ -111,7 +111,7 @@ def datagrid_enum_filter(filter, options = {}, &block)
           },
         )
       else
-        datagrid_filter_input(filter, **options, type: :select, &block)
+        datagrid_filter_input(filter, **options, &block)
       end
     end
 
@@ -190,10 +190,10 @@ def datagrid_range_filter(_type, filter, options = {})
       end
     end
 
-    def datagrid_range_filter_options(object, filter, type, **options)
+    def datagrid_range_filter_options(object, filter, section, **options)
       type_method_map = { from: :begin, to: :end }
-      options[:value] = object[filter.name]&.public_send(type_method_map[type])
-      options[:name] = @template.field_name(object_name, filter.name, type)
+      options[:value] = object[filter.name]&.public_send(type_method_map[section])
+      options[:name] = @template.field_name(object_name, filter.name, section)
       options
     end
 

From c133d7e83ad3ae842eaf504ce4f9782a9a6bd024 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 10:05:49 +0100
Subject: [PATCH 106/157] Cleanup

---
 lib/datagrid/filters/base_filter.rb |  4 ++
 lib/datagrid/form_builder.rb        | 93 ++++++++++-------------------
 2 files changed, 34 insertions(+), 63 deletions(-)

diff --git a/lib/datagrid/filters/base_filter.rb b/lib/datagrid/filters/base_filter.rb
index cb2ec9a..01c7663 100644
--- a/lib/datagrid/filters/base_filter.rb
+++ b/lib/datagrid/filters/base_filter.rb
@@ -98,6 +98,10 @@ def multiple?
         options[:multiple]
       end
 
+      def range?
+        false
+      end
+
       def allow_nil?
         options.key?(:allow_nil) ? options[:allow_nil] : options[:allow_blank]
       end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 5788744..9ab6279 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -7,9 +7,17 @@ module FormBuilder
     # @param filter_or_attribute [Datagrid::Filters::BaseFilter, String, Symbol] filter object or filter name
     # @param options [Hash] options of rails form input helper
     # @return [String] a form input html for the corresponding filter
-    def datagrid_filter(filter_or_attribute, ...)
+    def datagrid_filter(filter_or_attribute, **options, &block)
       filter = datagrid_get_filter(filter_or_attribute)
-      send(filter.form_builder_helper_name, filter, ...)
+      if filter.range?
+        datagrid_range_filter(filter, options, &block)
+      elsif filter.enum_checkboxes?
+        datagrid_enum_checkboxes_filter(filter, options, &block)
+      elsif filter.type == :dynamic
+        datagrid_dynamic_filter(filter, options, &block)
+      else
+        datagrid_filter_input(filter, **options, &block)
+      end
     end
 
     # @param filter_or_attribute [Datagrid::Filters::BaseFilter, String, Symbol] filter object or filter name
@@ -74,45 +82,21 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
 
     protected
 
-    def datagrid_extended_boolean_filter(filter, options = {}, &block)
-      datagrid_filter_input(filter, **options, &block)
-    end
-
-    def datagrid_boolean_filter(filter, options = {})
-      datagrid_filter_input(filter.name, **options)
-    end
-
-    def datagrid_date_filter(filter, options = {})
-      datagrid_range_filter(:date, filter, options)
-    end
-
-    def datagrid_date_time_filter(filter, options = {})
-      datagrid_range_filter(:datetime, filter, options)
-    end
-
-    def datagrid_default_filter(filter, options = {})
-      datagrid_filter_input(filter, **options)
-    end
-
-    def datagrid_enum_filter(filter, options = {}, &block)
-      if filter.enum_checkboxes?
-        elements = object.select_options(filter).map do |element|
-          text, value = @template.send(:option_text_and_value, element)
-          checked = enum_checkbox_checked?(filter, value)
-          [value, text, checked]
-        end
-        render_partial(
-          "enum_checkboxes",
-          {
-            elements: elements,
-            form: self,
-            filter: filter,
-            options: options,
-          },
-        )
-      else
-        datagrid_filter_input(filter, **options, &block)
+    def datagrid_enum_checkboxes_filter(filter, options = {}, &block)
+      elements = object.select_options(filter).map do |element|
+        text, value = @template.send(:option_text_and_value, element)
+        checked = enum_checkbox_checked?(filter, value)
+        [value, text, checked]
       end
+      render_partial(
+        "enum_checkboxes",
+        {
+          elements: elements,
+          form: self,
+          filter: filter,
+          options: options,
+        },
+      )
     end
 
     def enum_checkbox_checked?(filter, option_value)
@@ -126,11 +110,6 @@ def enum_checkbox_checked?(filter, option_value)
       end
     end
 
-    def datagrid_integer_filter(filter, options = {})
-      options[:value] = "" if filter.multiple? && object[filter.name].blank?
-      datagrid_range_filter(:integer, filter, options)
-    end
-
     def datagrid_dynamic_filter(filter, options = {})
       field, operation, value = object.filter_value(filter)
       options = add_filter_options(filter, **options)
@@ -178,16 +157,12 @@ def dynamic_filter_select(name, variants, select_options, html_options)
       end
     end
 
-    def datagrid_range_filter(_type, filter, options = {})
-      if filter.range?
-        from_options = datagrid_range_filter_options(object, filter, :from, **options)
-        to_options = datagrid_range_filter_options(object, filter, :to, **options)
-        render_partial "range_filter", {
-          from_options: from_options, to_options: to_options, filter: filter, form: self,
-        }
-      else
-        datagrid_filter_input(filter, **options)
-      end
+    def datagrid_range_filter(filter, options = {})
+      from_options = datagrid_range_filter_options(object, filter, :from, **options)
+      to_options = datagrid_range_filter_options(object, filter, :to, **options)
+      render_partial "range_filter", {
+        from_options: from_options, to_options: to_options, filter: filter, form: self,
+      }
     end
 
     def datagrid_range_filter_options(object, filter, section, **options)
@@ -197,14 +172,6 @@ def datagrid_range_filter_options(object, filter, section, **options)
       options
     end
 
-    def datagrid_string_filter(filter, options = {})
-      datagrid_range_filter(:string, filter, options)
-    end
-
-    def datagrid_float_filter(filter, options = {})
-      datagrid_range_filter(:float, filter, options)
-    end
-
     def datagrid_get_filter(attribute_or_filter)
       return attribute_or_filter unless Utils.string_like?(attribute_or_filter)
 

From f5e082ab75a10ef69b0db44b09c04dadd3b2655b Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 10:11:41 +0100
Subject: [PATCH 107/157] Linting

---
 lib/datagrid/drivers/array.rb | 2 +-
 lib/datagrid/form_builder.rb  | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/datagrid/drivers/array.rb b/lib/datagrid/drivers/array.rb
index 32007cd..5a47d8c 100644
--- a/lib/datagrid/drivers/array.rb
+++ b/lib/datagrid/drivers/array.rb
@@ -58,7 +58,7 @@ def scope_has_column?(scope, column_name)
         scope.any? && scope.first.respond_to?(column_name)
       end
 
-      def is_timestamp?(scope, column_name)
+      def timestamp_column?(scope, column_name)
         scope_has_column?(scope, column_name) &&
           timestamp_class?(get(scope.first, column_name).class)
       end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 9ab6279..6f497b5 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -41,8 +41,8 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
       type = options.delete(:type)&.to_sym
       if %i[datetime-local date].include?(type)
         if options.key?(:value) && options[:value].nil?
-           # https://github.com/rails/rails/pull/53387
-           options[:value] = ""
+          # https://github.com/rails/rails/pull/53387
+          options[:value] = ""
         end
       elsif options[:value]
         options[:value] = filter.format(options[:value])
@@ -82,7 +82,7 @@ def datagrid_filter_input(attribute_or_filter, **options, &block)
 
     protected
 
-    def datagrid_enum_checkboxes_filter(filter, options = {}, &block)
+    def datagrid_enum_checkboxes_filter(filter, options = {})
       elements = object.select_options(filter).map do |element|
         text, value = @template.send(:option_text_and_value, element)
         checked = enum_checkbox_checked?(filter, value)

From e12826bb59e5fa388adc370d6a0c96f5437e02de Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 10:26:28 +0100
Subject: [PATCH 108/157] Mention the removal of grid class from table[class]

---
 version-2/Readme.markdown | 68 +++++++++++++++++++++++----------------
 1 file changed, 41 insertions(+), 27 deletions(-)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 68772e1..506dd92 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -294,8 +294,18 @@ will always generate from/to inputs instead:
 It is more semantic and collision proof to use `data-*` attributes
 instead of classes for meta information from backend.
 Therefor built-in partials now generate data attributes by default
-instead of classes for column names:
+instead of classes for column names.
 
+* Filter name `input[class]` implemented as `.datagrid-filter[data-filter]`.
+* Filter type `input[class]` implemented as `.datagrid-filter[data-type]`.
+* Grid class `table[class]` removed due to:
+  * security concerns from some users
+  * breaking CSS classes naming convention
+* Column name `th[class], td[class]` implemented as `td[data-column], th[data-column]`.
+
+Note that the behavior change can be reverted by 
+[updating built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials).
+Version 2 makes it as easy as possible to override the defaults of the UI.
 
 ### Filters
 
@@ -319,42 +329,46 @@ Version 2:
 </div>
 ```
 
-Diff for [built-in partials between V1 and V2](./views.diff)
+Diff for [built-in views between V1 and V2](./views.diff)
 
 ### Columns
 
 Version 1:
 
 ``` html
-<tr>
-    <th class="name">Name</th>
-    <th class="category">Category</th>
-</tr>
-<tr>
-    <td class="name">John</th>
-    <td class="category">Worker</th>
-</tr>
-<tr>
-    <td class="name">Mike</th>
-    <td class="category">Manager</th>
-</tr>
+<table class="datagrid users_grid">
+    <tr>
+        <th class="name">Name</th>
+        <th class="category">Category</th>
+    </tr>
+    <tr>
+        <td class="name">John</th>
+        <td class="category">Worker</th>
+    </tr>
+    <tr>
+        <td class="name">Mike</th>
+        <td class="category">Manager</th>
+    </tr>
+</table>
 ```
 
 Version 2:
 
 ``` html
-<tr>
-    <th data-column="name">Name</th>
-    <th data-column="category">Category</th>
-</tr>
-<tr>
-    <td data-column="name">John</th>
-    <td data-column="category">Worker</th>
-</tr>
-<tr>
-    <td data-column="name">Mike</th>
-    <td data-column="category">Manager</th>
-</tr>
+<table class="datagrid-table">
+    <tr>
+        <th data-column="name">Name</th>
+        <th data-column="category">Category</th>
+    </tr>
+    <tr>
+        <td data-column="name">John</th>
+        <td data-column="category">Worker</th>
+    </tr>
+    <tr>
+        <td data-column="name">Mike</th>
+        <td data-column="category">Manager</th>
+    </tr>
+</table>
 ```
 
 If you still want to have an HTML class attached to a column use `class` column option:
@@ -371,7 +385,7 @@ Renders:
 <td class="short-column" data-column="name">John</td>
 ```
 
-[Modify built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
+[Modify built-in views](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
 if you want to change this behavior completely.
 
 ## id attribute for range filter inputs

From b7756755ed0d1eb8f96cfbe99aa18a5955424258 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 11:05:01 +0100
Subject: [PATCH 109/157] column[url] option removal guide

---
 version-2/Readme.markdown               | 42 +++++++++++++++++++++----
 version-2/find_deprecated_url_option.rb | 19 +++++++++++
 2 files changed, 55 insertions(+), 6 deletions(-)
 create mode 100644 version-2/find_deprecated_url_option.rb

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 506dd92..6f22c0e 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -9,22 +9,52 @@ Version 2 addresses all that evolution.
 
 List of things introduces:
 
+## API changes
+
+1. Ruby endless ranges for range filters.
+1. Use Hash instead of Array for multiparameter attributes.
+1. Remove `column[url]` option.
+1. Inherit `Datagrid::Base` instead of `include Datagrid`.
+1. `ApplicationGrid` is recommended base class instead of `BaseGrid`.
+
+## Frontend Changes
+
 1. Use `form_with` instead of `form_for`.
 1. Deprecated `datagrid_order_for`
-1. Ruby endless ranges for range filters.
 1. Modern modular CSS classes.
 1. HTML5 input types: number, date, datetime-local.
-1. Use Hash instead of Array for multiparameter attirubtes.
-1. Native Rails Engines.
+1. Native Rails Engines:
    * while supported, the library was not initially designed for it.
 1. HTML5 data attributes
-1. Consistent id attribute for range filter inputs
-1. Inherit `Datagrid::Base` instead of `include Datagrid`
-1. `ApplicationGrid` is recommended base class instead of `BaseGrid`
+1. Consistent `label[for]` and `input[id]` for range filters.
 1. Introduced `datagrid.filters.range.separator` localization
 1. Remove SASS dependency
 1. Replace `rake datagrid:copy_partials` with `rails g datagrid:views`
 
+## Remove column[url] option
+
+`column[url]` option was introduced before flexible data/html output layer for columns was established. Here is how the deprecated option can be migrated to modern setup:
+
+Version 1:
+
+``` ruby
+column(:user, url: -> (user) => { user_profile_path(user) }) do
+  user.name
+end
+```
+
+Version 2:
+
+``` ruby
+column(:user) do |user|
+  format(user.name) do |value|
+    link_to value, user_profile_path(user)
+  end
+end
+```
+
+All deprecated columns can be found [with a script](./find_deprecated_url_option.rb)
+
 ## Use form\_with
 
 Rails [deprecates form\_for in favor of form\_with](https://guides.rubyonrails.org/form_helpers.html#using-form-tag-and-form-for).
diff --git a/version-2/find_deprecated_url_option.rb b/version-2/find_deprecated_url_option.rb
new file mode 100644
index 0000000..fbb5f19
--- /dev/null
+++ b/version-2/find_deprecated_url_option.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# Important in development to have all classes in memory
+Rails.application.eager_load!
+
+included_classes = ObjectSpace.each_object(Class).select do |klass|
+  klass.included_modules.include?(Datagrid)
+end
+
+base_subclasses = ObjectSpace.each_object(Class).select do |klass|
+  klass < Datagrid::Base
+end
+classes = [*included_classes, *base_subclasses].uniq
+
+classes.flat_map(&:columns).select do |f|
+  f.options[:url]
+end.map do |f|
+  [f.grid_class, f.name].join("#")
+end

From c153d67b41fbf0f4a38b54360f9f493c0a55a92e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 11:14:40 +0100
Subject: [PATCH 110/157] column[url] removal guide

---
 version-2/Readme.markdown | 16 ++++++++++++++--
 version-2/deprecations.sh |  1 +
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 6f22c0e..5154ba4 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -60,7 +60,6 @@ All deprecated columns can be found [with a script](./find_deprecated_url_option
 Rails [deprecates form\_for in favor of form\_with](https://guides.rubyonrails.org/form_helpers.html#using-form-tag-and-form-for).
 
 `datagrid_form_for` is now deprecated if favor of `datagrid_form_with`.
-However, `datagrid_form_for` would also use Rails `form_with` because they share the same view partial.
 
 TODO: update the wiki
 
@@ -71,7 +70,8 @@ datagrid_form_for(@users_grid, url: users_path)
 datagrid_form_with(model: @users_grid, url: users_path)
 ```
 
-Built-in partial uses `form_with` no matter
+Version 2 built-in view `datagrid/form` uses `form_with` no matter of the with helper is used.
+Beware of that.
 
 [Grep all deprecations](./deprecations.sh).
 
@@ -79,6 +79,18 @@ Built-in partial uses `form_with` no matter
 
 `datagrid_order_for` helper serves no purpose and should not be used directly.
 The recommended way is to include your ordering code directly into `datagrid/head` partial.
+You implement `datagrid_order_for` in `ApplicationHelper` 
+and copy [datagrid/order\_for](../app/views/datagrid/_order_for.html.erb) into your project
+if you consider it useful:
+
+``` ruby
+module ApplicationHelper
+  def datagrid_order_for(grid, column)
+    render(partial: "datagrid/order_for", locals: { grid: grid, column: column })
+  end
+end
+```
+
 See default [head partial](../app/views/datagrid/_head.html.erb) for example.
 [Grep all deprecations](./deprecations.sh).
 
diff --git a/version-2/deprecations.sh b/version-2/deprecations.sh
index 01a4341..3460343 100644
--- a/version-2/deprecations.sh
+++ b/version-2/deprecations.sh
@@ -5,6 +5,7 @@ git grep 'datagrid_form_for'
 git grep 'datagrid_order_for'
 
 # Put necessary classes manually
+# or copy the helper from datagrid source code to ApplicationHelper
 git grep 'datagrid_column_classes' 
 
 # Inherit Datagrid::Base

From 5a0d9665bbf7bf7b810cc691ee9146c3982744b2 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 11:15:29 +0100
Subject: [PATCH 111/157] Cleanup

---
 app/views/datagrid/_range_filter.html.erb | 5 ++---
 lib/datagrid/filters/date_filter.rb       | 9 +++------
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
index b308d43..ffbb0f8 100644
--- a/app/views/datagrid/_range_filter.html.erb
+++ b/app/views/datagrid/_range_filter.html.erb
@@ -1,5 +1,4 @@
 <%= form.datagrid_filter_input(filter, class: 'datagrid-range-from', **from_options) %>
 <span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
-<%# Generating id only for "from" input to make sure 
-  # there is no duplicate id in DOM and click on label focuses the first input -%>
-<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
+<%# Generating id only for "from" input to make sure -%>
+<%# there is no duplicate id in DOM and click on label focuses the first input -%> <%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index c219276..e8398f4 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -12,7 +12,9 @@ def default_input_options
       end
 
       def apply(grid_object, scope, value)
-        value = value.begin&.beginning_of_day..value.end&.end_of_day if value.is_a?(Range)
+        if value.is_a?(Range) && driver.timestamp_column?(scope, name)
+          value = value.begin&.beginning_of_day..value.end&.end_of_day
+        end
         super
       end
 
@@ -28,11 +30,6 @@ def format(value)
         end
       end
 
-      def default_filter_where(scope, value)
-        value = Datagrid::Utils.format_date_as_timestamp(value) if driver.timestamp_column?(scope, name)
-        super
-      end
-
       protected
 
       def formats

From d7f75ba40db3034bf16736fab8dc40a0638bab9b Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 11:19:44 +0100
Subject: [PATCH 112/157] Cleanup

---
 app/views/datagrid/_range_filter.html.erb | 3 ++-
 lib/datagrid/utils.rb                     | 2 --
 version-2/views.diff                      | 7 ++++---
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
index ffbb0f8..21d1f0e 100644
--- a/app/views/datagrid/_range_filter.html.erb
+++ b/app/views/datagrid/_range_filter.html.erb
@@ -1,4 +1,5 @@
 <%= form.datagrid_filter_input(filter, class: 'datagrid-range-from', **from_options) %>
 <span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
 <%# Generating id only for "from" input to make sure -%>
-<%# there is no duplicate id in DOM and click on label focuses the first input -%> <%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
+<%# there is no duplicate id in DOM and click on label focuses the first input -%> 
+<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
diff --git a/lib/datagrid/utils.rb b/lib/datagrid/utils.rb
index f0c5d26..c7ea68d 100644
--- a/lib/datagrid/utils.rb
+++ b/lib/datagrid/utils.rb
@@ -121,8 +121,6 @@ def parse_datetime(value)
       def format_date_as_timestamp(value)
         if !value
           value
-          # elsif value.is_a?(Array)
-          # value.first&.beginning_of_day..value.last&.end_of_day
         elsif value.is_a?(Range)
           value.begin&.beginning_of_day..value.end&.end_of_day
         else
diff --git a/version-2/views.diff b/version-2/views.diff
index f971d0e..61c42fa 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -78,16 +78,17 @@ index e939128..b355528 100644
    <% end %>
  </tr>
 diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
-index 7a8a123..3b8ca85 100644
+index 7a8a123..ffbb0f8 100644
 --- a/app/views/datagrid/_range_filter.html.erb
 +++ b/app/views/datagrid/_range_filter.html.erb
-@@ -1,3 +1,3 @@
+@@ -1,3 +1,4 @@
 -<%= form.datagrid_filter_input(filter, **from_options) %>
 -<span class="separator <%= filter.type %>"><%= I18n.t('datagrid.filters.range.separator') %></span>
 -<%= form.datagrid_filter_input(filter, **to_options) %>
 +<%= form.datagrid_filter_input(filter, class: 'datagrid-range-from', **from_options) %>
 +<span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
-+<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options) %>
++<%# Generating id only for "from" input to make sure -%>
++<%# there is no duplicate id in DOM and click on label focuses the first input -%> <%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
 diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
 index f54d21c..340aea6 100644
 --- a/app/views/datagrid/_row.html.erb

From a80b0bcd463224d54d52a46970698226951e5762 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 17:23:02 +0100
Subject: [PATCH 113/157] Fix regression bug

---
 lib/datagrid/filters/date_filter.rb       |  2 +-
 spec/datagrid/filters/date_filter_spec.rb | 15 +++++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index 3557214..cf3b7a5 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -12,7 +12,7 @@ def default_input_options
       end
 
       def apply(grid_object, scope, value)
-        if driver.timestamp_column?(scope, name)
+        if grid_object.driver.timestamp_column?(scope, name)
           value = Datagrid::Utils.format_date_as_timestamp(value)
         end
         super
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 9941b5f..01f8cdf 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -267,4 +267,19 @@ def entry_dated(date)
     expect(report.assets).to include(e4)
     expect(report.assets).to_not include(e5)
   end
+
+
+  it "allows filter to be defined before scope" do
+    class ParentGrid < Datagrid::Base
+      filter(:created_at, :date, range: true)
+    end
+
+    class ChildGrid < ParentGrid
+      scope do
+        Entry
+      end
+    end
+
+    expect(ChildGrid.new.assets).to eq([])
+  end
 end

From 481e3f566d364a433676f528b24bc0020a554435 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Thu, 14 Nov 2024 21:44:26 +0100
Subject: [PATCH 114/157] Disable default label[for] for enum checkboxes

---
 lib/datagrid/filters/enum_filter.rb | 11 +++++++++++
 spec/datagrid/form_builder_spec.rb  |  4 ++++
 2 files changed, 15 insertions(+)

diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index ec154a8..75ea00a 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -29,6 +29,17 @@ def default_input_options
         }
       end
 
+      def label_options
+        if enum_checkboxes?
+          # Each checkbox has its own label
+          # The main label has no specific input to focus
+          # See app/views/datagrid/_enum_checkboxes.html.erb
+          {for: nil, **super}
+        else
+          super
+        end
+      end
+
       def strict
         options[:strict]
       end
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 89960be..04bbe6b 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -466,6 +466,10 @@ class MyTemplate
           )
         }
 
+        it "disables label[for] attribute" do
+          expect(view.datagrid_label(_filter)).to eq("<label>Category</label>")
+        end
+
         context "when partials option passed and partial exists" do
           let(:view_options) { { partials: "custom_checkboxes" } }
           it { should equal_to_dom("custom_enum_checkboxes") }

From bad591423268a0fcb0f7416dd5cec18cfa4eaa42 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 09:49:41 +0100
Subject: [PATCH 115/157] Fix assignment hash value with string keys to range
 filter

---
 lib/datagrid/filters/ranged_filter.rb     | 2 +-
 spec/datagrid/filters/date_filter_spec.rb | 8 ++++++++
 version-2/Readme.markdown                 | 2 +-
 3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/lib/datagrid/filters/ranged_filter.rb b/lib/datagrid/filters/ranged_filter.rb
index 421a13a..5b3fac0 100644
--- a/lib/datagrid/filters/ranged_filter.rb
+++ b/lib/datagrid/filters/ranged_filter.rb
@@ -46,7 +46,7 @@ def default_filter_where(scope, value)
       protected
 
       def parse_hash(result)
-        to_range(result[:from], result[:to])
+        to_range(result[:from] || result["from"], result[:to] || result["to"])
       end
 
       def to_range(from, to, exclusive = false)
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 01f8cdf..60f2d9c 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -54,14 +54,22 @@
     to = 3.days.ago
     report.created_at = { from: from, to: to }
     expect(report.created_at).to eq(from.to_date..to.to_date)
+
+    report.created_at = { "from" => from, "to" => to }
+    expect(report.created_at).to eq(from.to_date..to.to_date)
+
     report.created_at = {}
     expect(report.created_at).to eq(nil)
+
     report.created_at = { from: nil, to: nil }
     expect(report.created_at).to eq(nil)
+
     report.created_at = { from: Date.today, to: nil }
     expect(report.created_at).to eq(Date.today..nil)
+
     report.created_at = { from: nil, to: Date.today }
     expect(report.created_at).to eq(nil..Date.today)
+
     report.created_at = { from: Time.now, to: Time.now }
     expect(report.created_at).to eq(Date.today..Date.today)
   end
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 5154ba4..dbca39f 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -483,7 +483,7 @@ end
 
 ## Introduced range filter separator localization
 
-A separator symbol between range filter inputs is now a part of localizations to avoid hardcore.
+A separator symbol between range filter inputs is now a part of localizations to avoid hard coding.
 Add `datagrid.filters.range.separator` to your localization file.
 
 [See related view](https://github.com/bogdan/datagrid/blob/version-2/app/views/datagrid/_range_filter.html.erb#L2)

From e872fec6abc63daea887c9184c9315422e6ba315 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 11:31:52 +0100
Subject: [PATCH 116/157] Update upgrade guide

---
 version-2/Readme.markdown | 37 ++++++++++++++++++++++++++++++-------
 1 file changed, 30 insertions(+), 7 deletions(-)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index dbca39f..35397d0 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -79,9 +79,10 @@ Beware of that.
 
 `datagrid_order_for` helper serves no purpose and should not be used directly.
 The recommended way is to include your ordering code directly into `datagrid/head` partial.
-You implement `datagrid_order_for` in `ApplicationHelper` 
-and copy [datagrid/order\_for](../app/views/datagrid/_order_for.html.erb) into your project
-if you consider it useful:
+See default [head partial](../app/views/datagrid/_head.html.erb) for example.
+
+You can implement `datagrid_order_for` in `ApplicationHelper` 
+and copy [datagrid/order\_for](../app/views/datagrid/_order_for.html.erb) into your project if you consider it useful:
 
 ``` ruby
 module ApplicationHelper
@@ -91,7 +92,6 @@ module ApplicationHelper
 end
 ```
 
-See default [head partial](../app/views/datagrid/_head.html.erb) for example.
 [Grep all deprecations](./deprecations.sh).
 
 ## Inherit Datagrid::Base
@@ -188,7 +188,7 @@ if you want to change them.
 Diff for [built-in partials between V1 and V2](./views.diff)
 See [a new built-in CSS file](../app/assets/datagrid.css).
 
-### Example
+### Form layout difference
 
 The difference in layout generation from v1 to v2.
 
@@ -230,9 +230,32 @@ Version 1 layout:
 
 Version 2 layout:
 
-TODO
-
 ``` html
+<form class="datagrid-form" action="/users" accept-charset="UTF-8" method="get">
+  <div class="datagrid-filter" data-filter="id" data-type="integer">
+    <label for="g_id">Id</label>
+    <input step="1" class="datagrid-range-from" name="g[id][from]" type="number" id="g_id" />
+    <span class="datagrid-range-separator"> - </span>
+    <input step="1" class="datagrid-range-to" name="g[id][to]" type="number" />
+  </div>
+
+  <div class="datagrid-filter" data-filter="group_id" data-type="enum">
+    <label>Group</label>
+    <div class="datagrid-enum-checkboxes">
+      <label for="g_group_id_1" class="datagrid-enum-checkbox-label">
+        <input id="g_group_id_1" value="1" type="checkbox" name="g[group_id][]" />1
+      </label>
+      <label for="g_group_id_2" class="datagrid-enum-checkbox-label">
+        <input id="g_group_id_2" value="2" type="checkbox" name="g[group_id][]" />2
+      </label>
+    </div>
+  </div>
+
+  <div class="datagrid-actions">
+    <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
+    <a class="datagrid-reset" href="/location">Reset</a>
+  </div>
+</form>
 ```
 
 ## HTML5 input types

From 95c289a973e9d2040c967b55c290219c5bbd8ec4 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 13:49:41 +0100
Subject: [PATCH 117/157] Cleanup

---
 version-2/Readme.markdown | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 35397d0..fbae3f6 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -201,8 +201,6 @@ Version 1 layout:
 ``` html
 <form class="datagrid-form partial_default_grid" id="new_g"
     action="/users" accept-charset="UTF-8" method="get">
-  <input name="utf8" type="hidden" value="✓" autocomplete="off" />
-
   <div class="datagrid-filter filter">
     <label for="g_id">Id</label>
     <input class="id integer_filter from" multiple type="text" name="g[id][]" />
@@ -252,7 +250,8 @@ Version 2 layout:
   </div>
 
   <div class="datagrid-actions">
-    <input type="submit" name="commit" value="Search" class="datagrid-submit" data-disable-with="Search" />
+    <input type="submit" name="commit" value="Search"
+      class="datagrid-submit" data-disable-with="Search" />
     <a class="datagrid-reset" href="/location">Reset</a>
   </div>
 </form>

From 5ee5555aee5e245df720ba8cf9edcbb51d7d18ee Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:12:11 +0100
Subject: [PATCH 118/157] Update version-2/views.diff

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 version-2/views.diff | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index 61c42fa..d31ca27 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -49,7 +49,7 @@ index e939128..b355528 100644
 +    <%= content_tag(
 +      :th,
 +      class: {
-+        # Adding html clases based on condition
++        # Adding HTML classes based on condition
 +        # Consider maintaining consistency with datagrid/rows partial
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
 +        "datagrid-order-active-desc": grid.ordered_by?(column ,true),

From 41832ef5c5e686de668e3f1ee792a9dd0e457643 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:12:17 +0100
Subject: [PATCH 119/157] Update version-2/views.diff

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 version-2/views.diff | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index d31ca27..d4ffe8f 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -52,7 +52,7 @@ index e939128..b355528 100644
 +        # Adding HTML classes based on condition
 +        # Consider maintaining consistency with datagrid/rows partial
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
-+        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
++        "datagrid-order-active-desc": grid.ordered_by?(column, true),
 +        column.html_class => column.html_class.present?,
 +      },
 +      "data-column": column.name

From a9190adf418a6e278126f3412612e5e2993ce222 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:12:23 +0100
Subject: [PATCH 120/157] Update version-2/views.diff

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 version-2/views.diff | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index d4ffe8f..e157c6b 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -101,7 +101,7 @@ index f54d21c..340aea6 100644
 +      :td,
 +      datagrid_value(grid, column, asset),
 +      class: {
-+        # Adding html clases based on condition
++        # Adding HTML classes based on condition
 +        # Consider maintaining consistency with datagrid/head partial
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
 +        "datagrid-order-active-desc": grid.ordered_by?(column ,true),

From 6981dc51084d8adb827ce798ddd66936f83b2b77 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:12:32 +0100
Subject: [PATCH 121/157] Update version-2/views.diff

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 version-2/views.diff | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index e157c6b..93f3047 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -104,7 +104,7 @@ index f54d21c..340aea6 100644
 +        # Adding HTML classes based on condition
 +        # Consider maintaining consistency with datagrid/head partial
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
-+        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
++        "datagrid-order-active-desc": grid.ordered_by?(column, true),
 +        column.html_class => column.html_class.present?,
 +      },
 +      "data-column": column.name

From c2b5543d8367ac4cec61c0e57d45a48a99af412e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:12:38 +0100
Subject: [PATCH 122/157] Update app/views/datagrid/_head.html.erb

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 app/views/datagrid/_head.html.erb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index b355528..248bc0c 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -3,7 +3,7 @@
     <%= content_tag(
       :th,
       class: {
-        # Adding html clases based on condition
+        # Adding HTML classes based on condition
         # Consider maintaining consistency with datagrid/rows partial
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
         "datagrid-order-active-desc": grid.ordered_by?(column ,true),

From 47a2d09bfba23763e3a0f23d4b388e7bcaf3d2b6 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:23:01 +0100
Subject: [PATCH 123/157] Update app/views/datagrid/_head.html.erb

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 app/views/datagrid/_head.html.erb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index 248bc0c..2aa1779 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -6,7 +6,7 @@
         # Adding HTML classes based on condition
         # Consider maintaining consistency with datagrid/rows partial
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
-        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
+        "datagrid-order-active-desc": grid.ordered_by?(column, true),
         column.html_class => column.html_class.present?,
       },
       "data-column": column.name

From cbece13e4af2a8007b0efe5d1c23e533ad8e7992 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:23:43 +0100
Subject: [PATCH 124/157] Update app/views/datagrid/_table.html.erb

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 app/views/datagrid/_table.html.erb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
index 0b5ff24..afcb9ab 100644
--- a/app/views/datagrid/_table.html.erb
+++ b/app/views/datagrid/_table.html.erb
@@ -5,7 +5,7 @@ Local variables:
 * options - passed options Hash
 %>
 <% if grid.html_columns(*options[:columns]).any? %>
-  <%= content_tag :table, class: 'datagrid-table', **options.fetch(:html, {}) do %>
+  <%= tag.table, class: 'datagrid-table', **options.fetch(:html, {}) do %>
     <thead>
       <%= datagrid_header(grid, options) %>
     </thead>

From dbd3d4624eaa5b2233b005c75143f930a94908c2 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:28:18 +0100
Subject: [PATCH 125/157] Migrate from content_tag to tag

---
 app/views/datagrid/_head.html.erb         |  3 +--
 app/views/datagrid/_range_filter.html.erb |  2 +-
 app/views/datagrid/_row.html.erb          |  3 +--
 app/views/datagrid/_table.html.erb        |  2 +-
 lib/datagrid/columns.rb                   |  4 ++--
 spec/datagrid/columns_spec.rb             |  2 +-
 spec/datagrid/form_builder_spec.rb        |  2 +-
 spec/datagrid/helper_spec.rb              | 12 ++++++------
 8 files changed, 14 insertions(+), 16 deletions(-)

diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index 2aa1779..71e6b03 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -1,7 +1,6 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <%= content_tag(
-      :th,
+    <%= tag.th(
       class: {
         # Adding HTML classes based on condition
         # Consider maintaining consistency with datagrid/rows partial
diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
index 21d1f0e..faa2575 100644
--- a/app/views/datagrid/_range_filter.html.erb
+++ b/app/views/datagrid/_range_filter.html.erb
@@ -1,5 +1,5 @@
 <%= form.datagrid_filter_input(filter, class: 'datagrid-range-from', **from_options) %>
 <span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
 <%# Generating id only for "from" input to make sure -%>
-<%# there is no duplicate id in DOM and click on label focuses the first input -%> 
+<%# there is no duplicate id in DOM and click on label focuses the first input -%>
 <%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
index 340aea6..f57c729 100644
--- a/app/views/datagrid/_row.html.erb
+++ b/app/views/datagrid/_row.html.erb
@@ -1,7 +1,6 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
-    <%= content_tag(
-      :td,
+    <%= tag.td(
       datagrid_value(grid, column, asset),
       class: {
         # Adding html clases based on condition
diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
index afcb9ab..2e9273e 100644
--- a/app/views/datagrid/_table.html.erb
+++ b/app/views/datagrid/_table.html.erb
@@ -5,7 +5,7 @@ Local variables:
 * options - passed options Hash
 %>
 <% if grid.html_columns(*options[:columns]).any? %>
-  <%= tag.table, class: 'datagrid-table', **options.fetch(:html, {}) do %>
+  <%= tag.table class: 'datagrid-table', **options.fetch(:html, {}) do %>
     <thead>
       <%= datagrid_header(grid, options) %>
     </thead>
diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index dca4638..5b495ac 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -115,7 +115,7 @@ def respond_to(&block)
       # @example
       #   column(:name) do |model|
       #     format(model.name) do |value|
-      #       content_tag(:strong, value)
+      #       tag.strong(value)
       #     end
       #   end
       def format(value, &block)
@@ -325,7 +325,7 @@ def column_by_name(name)
     # @example
     #   column(:name) do |model|
     #     format(model.name) do |value|
-    #       content_tag(:strong, value)
+    #       tag.strong(value)
     #     end
     #   end
     #
diff --git a/spec/datagrid/columns_spec.rb b/spec/datagrid/columns_spec.rb
index 3e313f0..dbd47b3 100644
--- a/spec/datagrid/columns_spec.rb
+++ b/spec/datagrid/columns_spec.rb
@@ -215,7 +215,7 @@ class Report27 < Datagrid::Base
       filter(:name)
       column(:entries_count, "count(entries.id)") do |model|
         format("(#{model.entries_count})") do |value|
-          content_tag(:span, value)
+          tag.span(value)
         end
       end
     end
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 04bbe6b..a1a42d4 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -376,7 +376,7 @@ class MyTemplate
       context "when block is given" do
         let(:_filter_block) do
           proc do
-            template.content_tag(:option, "block option", value: "block_value")
+            template.tag.option("block option", value: "block_value")
           end
         end
         it {
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 822c60e..b5fcdd7 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -201,7 +201,7 @@
       end
       expect(
         subject.datagrid_rows(rp) do |row|
-          subject.content_tag(:strong, row.name)
+          subject.tag.strong(row.name)
         end,
       ).to match_css_pattern(
         "strong" => "Star",
@@ -233,7 +233,7 @@
       rp = test_report do
         scope { Entry }
         column(:name, html: true) do |model|
-          content_tag(:span, model.name)
+          tag.span(model.name)
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
@@ -279,7 +279,7 @@
     it "should render argument-based html columns" do
       rp = test_report do
         scope { Entry }
-        column(:name, html: ->(data) { content_tag :h1, data })
+        column(:name, html: ->(data) { tag.h1 data })
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
         "tr td[data-column=name] h1" => "Star",
@@ -289,7 +289,7 @@
     it "should render argument-based html columns with custom data" do
       rp = test_report do
         scope { Entry }
-        column(:name, html: ->(data) { content_tag :em, data }) do
+        column(:name, html: ->(data) { tag.em data }) do
           name.upcase
         end
       end
@@ -302,7 +302,7 @@
       rp = test_report do
         scope { Entry }
         column(:name, html: true) do |model, grid|
-          content_tag(:span, "#{model.name}-#{grid.assets.klass}")
+          tag.span("#{model.name}-#{grid.assets.klass}")
         end
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
@@ -314,7 +314,7 @@
       rp = test_report do
         scope { Entry }
         column(:name, html: lambda { |data, model|
-          content_tag :h1, "#{data}-#{model.name.downcase}"
+          tag.h1 "#{data}-#{model.name.downcase}"
         },)
       end
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(

From 0f761ca6fbde0b15abb5119520d98e81f92b9625 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 19:28:47 +0100
Subject: [PATCH 126/157] Update view diff

---
 version-2/views.diff | 29 ++++++++++++++---------------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index 93f3047..97aeb0e 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -39,15 +39,14 @@ index 7e175c1..fc4f4ae 100644
    </div>
  <% end -%>
 diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
-index e939128..b355528 100644
+index e939128..71e6b03 100644
 --- a/app/views/datagrid/_head.html.erb
 +++ b/app/views/datagrid/_head.html.erb
-@@ -1,8 +1,31 @@
+@@ -1,8 +1,30 @@
  <tr>
    <% grid.html_columns(*options[:columns]).each do |column| %>
 -    <th class="<%= datagrid_column_classes(grid, column) %>">
-+    <%= content_tag(
-+      :th,
++    <%= tag.th(
 +      class: {
 +        # Adding HTML classes based on condition
 +        # Consider maintaining consistency with datagrid/rows partial
@@ -78,33 +77,33 @@ index e939128..b355528 100644
    <% end %>
  </tr>
 diff --git a/app/views/datagrid/_range_filter.html.erb b/app/views/datagrid/_range_filter.html.erb
-index 7a8a123..ffbb0f8 100644
+index 7a8a123..faa2575 100644
 --- a/app/views/datagrid/_range_filter.html.erb
 +++ b/app/views/datagrid/_range_filter.html.erb
-@@ -1,3 +1,4 @@
+@@ -1,3 +1,5 @@
 -<%= form.datagrid_filter_input(filter, **from_options) %>
 -<span class="separator <%= filter.type %>"><%= I18n.t('datagrid.filters.range.separator') %></span>
 -<%= form.datagrid_filter_input(filter, **to_options) %>
 +<%= form.datagrid_filter_input(filter, class: 'datagrid-range-from', **from_options) %>
 +<span class="datagrid-range-separator"><%= I18n.t('datagrid.filters.range.separator') %></span>
 +<%# Generating id only for "from" input to make sure -%>
-+<%# there is no duplicate id in DOM and click on label focuses the first input -%> <%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
++<%# there is no duplicate id in DOM and click on label focuses the first input -%>
++<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
 diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
-index f54d21c..340aea6 100644
+index f54d21c..f57c729 100644
 --- a/app/views/datagrid/_row.html.erb
 +++ b/app/views/datagrid/_row.html.erb
-@@ -1,5 +1,16 @@
+@@ -1,5 +1,15 @@
  <tr>
    <% grid.html_columns(*options[:columns]).each do |column| %>
 -    <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_value(grid, column, asset) %></td>
-+    <%= content_tag(
-+      :td,
++    <%= tag.td(
 +      datagrid_value(grid, column, asset),
 +      class: {
-+        # Adding HTML classes based on condition
++        # Adding html clases based on condition
 +        # Consider maintaining consistency with datagrid/head partial
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
-+        "datagrid-order-active-desc": grid.ordered_by?(column, true),
++        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
 +        column.html_class => column.html_class.present?,
 +      },
 +      "data-column": column.name
@@ -112,7 +111,7 @@ index f54d21c..340aea6 100644
    <% end %>
  </tr>
 diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
-index 8708c05..0b5ff24 100644
+index 8708c05..2e9273e 100644
 --- a/app/views/datagrid/_table.html.erb
 +++ b/app/views/datagrid/_table.html.erb
 @@ -5,7 +5,7 @@ Local variables:
@@ -120,7 +119,7 @@ index 8708c05..0b5ff24 100644
  %>
  <% if grid.html_columns(*options[:columns]).any? %>
 -  <%= content_tag :table, options[:html] do %>
-+  <%= content_tag :table, class: 'datagrid-table', **options.fetch(:html, {}) do %>
++  <%= tag.table class: 'datagrid-table', **options.fetch(:html, {}) do %>
      <thead>
        <%= datagrid_header(grid, options) %>
      </thead>

From ad56fe61af11e2716ce440e5344368a4620b4228 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 20:46:15 +0100
Subject: [PATCH 127/157] Update lib/datagrid/core.rb

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 lib/datagrid/core.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/datagrid/core.rb b/lib/datagrid/core.rb
index 8bc04d8..ddc450c 100644
--- a/lib/datagrid/core.rb
+++ b/lib/datagrid/core.rb
@@ -273,7 +273,7 @@ def sanitize_for_mass_assignment(attributes)
       if forbidden_attributes_protection
         super
       elsif defined?(ActionController::Parameters) && attributes.is_a?(ActionController::Parameters)
-        attributes.permit!.to_h
+        attributes.to_unsafe_h
       else
         attributes
       end

From 6fab0e8c07705c1a694aad3ebc9aa3e528b20ecf Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 20:46:33 +0100
Subject: [PATCH 128/157] Update app/views/datagrid/_row.html.erb

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 app/views/datagrid/_row.html.erb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
index f57c729..c64b0bf 100644
--- a/app/views/datagrid/_row.html.erb
+++ b/app/views/datagrid/_row.html.erb
@@ -6,7 +6,7 @@
         # Adding html clases based on condition
         # Consider maintaining consistency with datagrid/head partial
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
-        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
+        "datagrid-order-active-desc": grid.ordered_by?(column, true),
         column.html_class => column.html_class.present?,
       },
       "data-column": column.name

From f30a9bba953a9cb24314bf3a5b6811e32016834f Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Fri, 15 Nov 2024 20:46:39 +0100
Subject: [PATCH 129/157] Update version-2/views.diff

Co-authored-by: Bohdan Zhuravel <bohdan@zhuravel.bz>
---
 version-2/views.diff | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index 97aeb0e..69d50c6 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -103,7 +103,7 @@ index f54d21c..f57c729 100644
 +        # Adding html clases based on condition
 +        # Consider maintaining consistency with datagrid/head partial
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
-+        "datagrid-order-active-desc": grid.ordered_by?(column ,true),
++        "datagrid-order-active-desc": grid.ordered_by?(column, true),
 +        column.html_class => column.html_class.present?,
 +      },
 +      "data-column": column.name

From 14452df3865e9e1e8162f9dda10e2fd247c32e0e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 16 Nov 2024 11:03:17 +0100
Subject: [PATCH 130/157] Fix specifying inline class in #datagrid_filter for
 enum checkboxes

---
 app/views/datagrid/_enum_checkboxes.html.erb |  2 +-
 spec/datagrid/form_builder_spec.rb           | 15 +++++++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
index f225cc6..a5d4dcb 100644
--- a/app/views/datagrid/_enum_checkboxes.html.erb
+++ b/app/views/datagrid/_enum_checkboxes.html.erb
@@ -5,7 +5,7 @@ You can add indent if whitespace doesn't matter for you
 <div class="datagrid-enum-checkboxes">
 <%- elements.each do |value, text, checked| -%>
 <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
-<%= form.datagrid_label(filter.name, **options, for: id, class: 'datagrid-enum-checkbox-label') do -%>
+<%= form.datagrid_label(filter.name, for: id, class: 'datagrid-enum-checkbox-label', **options) do -%>
 <%= form.datagrid_filter_input(filter.name, id: id, value: value) -%>
 <%= text -%>
 <%- end -%>
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index a1a42d4..947749c 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -474,6 +474,21 @@ class MyTemplate
           let(:view_options) { { partials: "custom_checkboxes" } }
           it { should equal_to_dom("custom_enum_checkboxes") }
         end
+
+        context "when inline class attribute specified" do
+          let(:_filter_options) { {for: nil, class: 'custom-class'} }
+
+          it { should equal_to_dom(<<~HTML) }
+          <div class="datagrid-enum-checkboxes">
+            <label class="custom-class">
+              <input id="report_category_first" value="first" type="checkbox" name="report[category][]">first
+            </label>
+            <label class="custom-class">
+              <input id="report_category_second" value="second" type="checkbox" name="report[category][]">second
+            </label>
+          </div>
+          HTML
+        end
       end
     end
 

From 757fd562e6faeae1123e6504d9854dc3344df7d6 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 16 Nov 2024 11:06:40 +0100
Subject: [PATCH 131/157] Remove datagrid-enum-checkbox-label css class as
 styling by tag name is good enough

---
 app/views/datagrid/_enum_checkboxes.html.erb |  2 +-
 spec/datagrid/form_builder_spec.rb           | 10 +++++-----
 version-2/Readme.markdown                    |  4 ++--
 version-2/form-v2.html                       |  4 ++--
 version-2/views.diff                         |  2 +-
 5 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
index a5d4dcb..5668c49 100644
--- a/app/views/datagrid/_enum_checkboxes.html.erb
+++ b/app/views/datagrid/_enum_checkboxes.html.erb
@@ -5,7 +5,7 @@ You can add indent if whitespace doesn't matter for you
 <div class="datagrid-enum-checkboxes">
 <%- elements.each do |value, text, checked| -%>
 <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
-<%= form.datagrid_label(filter.name, for: id, class: 'datagrid-enum-checkbox-label', **options) do -%>
+<%= form.datagrid_label(filter.name, for: id, **options) do -%>
 <%= form.datagrid_filter_input(filter.name, id: id, value: value) -%>
 <%= text -%>
 <%- end -%>
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 947749c..59434c0 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -451,12 +451,12 @@ class MyTemplate
           should equal_to_dom(
             <<~HTML,
               <div class="datagrid-enum-checkboxes">
-              <label for="report_category_first" class="datagrid-enum-checkbox-label">
+              <label for="report_category_first">
               <input type="checkbox" id="report_category_first"
                   value="first" name="report[category][]" />
               first
               </label>
-              <label for="report_category_second" class="datagrid-enum-checkbox-label">
+              <label for="report_category_second">
               <input type="checkbox" id="report_category_second"
                   value="second" name="report[category][]" />
               second
@@ -643,15 +643,15 @@ class MyTemplate
       let(:expected_html) do
         <<~DOM
           <div class="datagrid-enum-checkboxes">
-            <label for="report_column_names_id" class="datagrid-enum-checkbox-label">
+            <label for="report_column_names_id">
               <input id="report_column_names_id" type="checkbox" value="id" checked name="report[column_names][]">
               Id
             </label>
-            <label for="report_column_names_name" class="datagrid-enum-checkbox-label">
+            <label for="report_column_names_name">
               <input id="report_column_names_name" type="checkbox" value="name" checked name="report[column_names][]"/>
               Name
             </label>
-            <label for="report_column_names_category" class="datagrid-enum-checkbox-label">
+            <label for="report_column_names_category">
               <input id="report_column_names_category" type="checkbox" value="category" name="report[column_names][]">
               Category
             </label>
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index fbae3f6..116fcc8 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -240,10 +240,10 @@ Version 2 layout:
   <div class="datagrid-filter" data-filter="group_id" data-type="enum">
     <label>Group</label>
     <div class="datagrid-enum-checkboxes">
-      <label for="g_group_id_1" class="datagrid-enum-checkbox-label">
+      <label for="g_group_id_1">
         <input id="g_group_id_1" value="1" type="checkbox" name="g[group_id][]" />1
       </label>
-      <label for="g_group_id_2" class="datagrid-enum-checkbox-label">
+      <label for="g_group_id_2">
         <input id="g_group_id_2" value="2" type="checkbox" name="g[group_id][]" />2
       </label>
     </div>
diff --git a/version-2/form-v2.html b/version-2/form-v2.html
index a67fd98..49d696b 100644
--- a/version-2/form-v2.html
+++ b/version-2/form-v2.html
@@ -9,10 +9,10 @@
   <div class="datagrid-filter" data-filter="group_id" data-type="enum">
     <label for="g_group_id">Group</label>
     <div class="datagrid-enum-checkboxes">
-      <label class="datagrid-enum-checkbox-label" for="g_group_id_1">
+      <label for="g_group_id_1">
         <input id="g_group_id_1" value="1" type="checkbox" name="g[group_id][]" />1
       </label>
-      <label class="datagrid-enum-checkbox-label" for="g_group_id_2">
+      <label for="g_group_id_2">
         <input id="g_group_id_2" type="checkbox" value="2" name="g[group_id][]" />2
       </label>
     </div>
diff --git a/version-2/views.diff b/version-2/views.diff
index 97aeb0e..54e411f 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -11,7 +11,7 @@ index 9f48319..f225cc6 100644
  <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
 -<%= form.label filter.name, options.merge(for: id) do -%>
 -<%= form.check_box(filter.name, {multiple: true, id: id, checked: checked, include_hidden: false}, value.to_s, nil) -%>
-+<%= form.datagrid_label(filter.name, **options, for: id, class: 'datagrid-enum-checkbox-label') do -%>
++<%= form.datagrid_label(filter.name, **options, for: id) do -%>
 +<%= form.datagrid_filter_input(filter.name, id: id, value: value) -%>
  <%= text -%>
  <%- end -%>

From c7b66ce07cf0a1b5d75b773123781095a95d1509 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 16 Nov 2024 11:08:07 +0100
Subject: [PATCH 132/157] Update view diff

---
 version-2/views.diff | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index 977385c..e49aaa8 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -1,5 +1,5 @@
 diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
-index 9f48319..f225cc6 100644
+index 9f48319..5668c49 100644
 --- a/app/views/datagrid/_enum_checkboxes.html.erb
 +++ b/app/views/datagrid/_enum_checkboxes.html.erb
 @@ -2,10 +2,12 @@
@@ -11,7 +11,7 @@ index 9f48319..f225cc6 100644
  <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
 -<%= form.label filter.name, options.merge(for: id) do -%>
 -<%= form.check_box(filter.name, {multiple: true, id: id, checked: checked, include_hidden: false}, value.to_s, nil) -%>
-+<%= form.datagrid_label(filter.name, **options, for: id) do -%>
++<%= form.datagrid_label(filter.name, for: id, **options) do -%>
 +<%= form.datagrid_filter_input(filter.name, id: id, value: value) -%>
  <%= text -%>
  <%- end -%>
@@ -90,7 +90,7 @@ index 7a8a123..faa2575 100644
 +<%# there is no duplicate id in DOM and click on label focuses the first input -%>
 +<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
 diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
-index f54d21c..f57c729 100644
+index f54d21c..c64b0bf 100644
 --- a/app/views/datagrid/_row.html.erb
 +++ b/app/views/datagrid/_row.html.erb
 @@ -1,5 +1,15 @@

From 2a3fb80c37deb62ae17026062415db65b7b7216b Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 16 Nov 2024 13:23:17 +0100
Subject: [PATCH 133/157] Improve stylesheet to support enum filter

---
 app/assets/stylesheets/datagrid.css | 33 +++++++----------------------
 1 file changed, 8 insertions(+), 25 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.css b/app/assets/stylesheets/datagrid.css
index 7083c37..1d13d62 100644
--- a/app/assets/stylesheets/datagrid.css
+++ b/app/assets/stylesheets/datagrid.css
@@ -51,36 +51,21 @@ table.datagrid-table th {
   margin: 10px;
 }
 
-.datagrid-filter::before,
-.datagrid-filter::after {
-  content: '';
-  display: table;
-}
-
-.datagrid-filter::after {
-  clear: both;
-}
-
 .datagrid-filter label {
   width: 150px;
-  float: left;
+  display: inline-block;
 }
 
-.datagrid-filter input {
+.datagrid-filter input, .datagrid-filter select {
   border: 2px solid #ccc;
   border-radius: 4px;
-  float: left;
+  display: inline-block;
   padding: 5px 12px;
   width: 300px;
 }
 
 input.datagrid-range-from, input.datagrid-range-to {
-  width: 144px;
-}
-
-.datagrid-filter select {
-  float: left;
-  width: 300px
+  width: 138px;
 }
 
 .datagrid-filter select[multiple] {
@@ -95,32 +80,30 @@ select.datagrid-dynamic-operation {
 }
 
 .datagrid-dynamic-value {
-  margin: 10px 0 0 150px;
+  margin: 10px 0 0 154px;
   clear: both;
 }
 
 .datagrid-range-separator {
-  float: left;
+  display: inline-block;
   margin: 6px 4px 0;
 }
 
 .datagrid-enum-checkboxes {
-  float: left;
+  display: inline-block;
 }
 
 select.datagrid-dynamic-field {
-  width: 242px
+  width: 238px
 }
 
 .datagrid-enum-checkboxes input {
   margin: 7px;
-  float: none;
   width: auto;
 }
 
 .datagrid-enum-checkboxes label {
   display: block;
-  float: none;
   width: 100%;
 }
 

From f072cde083d8e959fd4c3e0420595b27d7806551 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 16 Nov 2024 13:25:05 +0100
Subject: [PATCH 134/157] Fix dynamic filter layout

---
 app/assets/stylesheets/datagrid.css | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/app/assets/stylesheets/datagrid.css b/app/assets/stylesheets/datagrid.css
index 1d13d62..89d42a9 100644
--- a/app/assets/stylesheets/datagrid.css
+++ b/app/assets/stylesheets/datagrid.css
@@ -74,9 +74,13 @@ input.datagrid-range-from, input.datagrid-range-to {
   height: 100px;
 }
 
+select.datagrid-dynamic-field {
+  width: 228px
+}
+
 select.datagrid-dynamic-operation {
   margin-left: 7px;
-  width: 50px;
+  width: 60px;
 }
 
 .datagrid-dynamic-value {
@@ -93,10 +97,6 @@ select.datagrid-dynamic-operation {
   display: inline-block;
 }
 
-select.datagrid-dynamic-field {
-  width: 238px
-}
-
 .datagrid-enum-checkboxes input {
   margin: 7px;
   width: auto;

From a33986c864939783ad8602b61ca2a55be34fb292 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sat, 16 Nov 2024 13:31:21 +0100
Subject: [PATCH 135/157] Cleanup stylesheet

---
 app/assets/stylesheets/datagrid.css | 1 -
 1 file changed, 1 deletion(-)

diff --git a/app/assets/stylesheets/datagrid.css b/app/assets/stylesheets/datagrid.css
index 89d42a9..d06f1a4 100644
--- a/app/assets/stylesheets/datagrid.css
+++ b/app/assets/stylesheets/datagrid.css
@@ -85,7 +85,6 @@ select.datagrid-dynamic-operation {
 
 .datagrid-dynamic-value {
   margin: 10px 0 0 154px;
-  clear: both;
 }
 
 .datagrid-range-separator {

From 5b6cd380ca7bb0896372e038dcdada10b0dffb12 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 17 Nov 2024 11:23:14 +0100
Subject: [PATCH 136/157] Improve Scaffold.view_code

---
 lib/datagrid/generators/scaffold.rb       | 24 +++++++++++++++++------
 spec/datagrid/generators/scaffold_spec.rb | 14 ++++++++++++-
 templates/controller.rb.erb               |  6 ------
 templates/index.html.erb                  |  5 -----
 4 files changed, 31 insertions(+), 18 deletions(-)
 delete mode 100644 templates/controller.rb.erb
 delete mode 100644 templates/index.html.erb

diff --git a/lib/datagrid/generators/scaffold.rb b/lib/datagrid/generators/scaffold.rb
index 172c8b5..d361bc1 100644
--- a/lib/datagrid/generators/scaffold.rb
+++ b/lib/datagrid/generators/scaffold.rb
@@ -23,7 +23,7 @@ def create_scaffold
         end
         create_file view_file, view_code
         route(generate_routing_namespace("resources :#{grid_controller_short_name}"))
-        gem "kaminari" unless defined?(::Kaminari) || defined?(::WillPaginate) || defined?(::Pagy)
+        gem "kaminari" unless kaminari? || will_paginate? || pagy?
         in_root do
           {
             "css" => " *= require datagrid",
@@ -71,9 +71,9 @@ def grid_param_name
       end
 
       def pagination_helper_code
-        if defined?(::WillPaginate)
+        if will_paginate?
           "will_paginate(@grid.assets)"
-        elsif defined?(::Pagy)
+        elsif pagy?
           "pagy_nav(@pagy)"
         else
           # Kaminari is default
@@ -82,7 +82,7 @@ def pagination_helper_code
       end
 
       def table_helper_code
-        if defined?(::Pagy)
+        if pagy?
           "datagrid_table @grid, @records"
         else
           "datagrid_table @grid"
@@ -98,7 +98,7 @@ def grid_route_name
       end
 
       def index_code
-        if defined?(::Pagy)
+        if pagy?
           <<-RUBY
     @grid = #{grid_class_name}.new(grid_params)
     @pagy, @assets = pagy(@grid.assets)
@@ -129,7 +129,7 @@ def grid_params
       end
 
       def view_code
-        indent(<<~ERB)
+        <<~ERB
           <%= datagrid_form_with model: @grid, url: #{grid_route_name} %>
 
           <%= #{pagination_helper_code} %>
@@ -168,6 +168,18 @@ def file_exists?(name)
         name = Rails.root.join(name) unless name.to_s.first == "/"
         File.exist?(name)
       end
+
+      def pagy?
+        defined?(::Pagy)
+      end
+
+      def will_paginate?
+        defined?(::WillPaginate)
+      end
+
+      def kaminari?
+        defined?(::Kaminari)
+      end
     end
   end
 end
diff --git a/spec/datagrid/generators/scaffold_spec.rb b/spec/datagrid/generators/scaffold_spec.rb
index d83f88d..0282055 100644
--- a/spec/datagrid/generators/scaffold_spec.rb
+++ b/spec/datagrid/generators/scaffold_spec.rb
@@ -24,7 +24,7 @@
     end
   end
 
-  describe ".controller_code" do
+  describe "#controller_code" do
     it "works" do
       expect(subject.controller_code).to eq(<<~RUBY)
         class UsersController < ApplicationController
@@ -43,4 +43,16 @@ def grid_params
       RUBY
     end
   end
+
+  describe "#view_code" do
+    it "works" do
+      expect(subject.view_code).to eq(<<~ERB)
+      <%= datagrid_form_with model: @grid, url: users_path %>
+
+      <%= paginate(@grid.assets) %>
+      <%= datagrid_table @grid %>
+      <%= paginate(@grid.assets) %>
+      ERB
+    end
+  end
 end
diff --git a/templates/controller.rb.erb b/templates/controller.rb.erb
deleted file mode 100644
index 71a6253..0000000
--- a/templates/controller.rb.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-class <%= grid_controller_class_name %> < ApplicationController
-
-<%= index_action -%>
-
-end
-
diff --git a/templates/index.html.erb b/templates/index.html.erb
deleted file mode 100644
index 133754b..0000000
--- a/templates/index.html.erb
+++ /dev/null
@@ -1,5 +0,0 @@
-<%%= datagrid_form_with model: @grid, url: <%= grid_route_name %> %>
-
-<%%= <%=pagination_helper_code%> %>
-<%%= <%=table_helper_code%> %>
-<%%= <%=pagination_helper_code%> %>

From 0769160987913320c6c5c7c122cdbf958aeb0033 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 17 Nov 2024 11:31:16 +0100
Subject: [PATCH 137/157] Pagy tests for scaffold

---
 spec/datagrid/generators/scaffold_spec.rb | 39 +++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/spec/datagrid/generators/scaffold_spec.rb b/spec/datagrid/generators/scaffold_spec.rb
index 0282055..19a00df 100644
--- a/spec/datagrid/generators/scaffold_spec.rb
+++ b/spec/datagrid/generators/scaffold_spec.rb
@@ -42,6 +42,29 @@ def grid_params
         end
       RUBY
     end
+
+    context "with pagy" do
+      before do
+        allow(subject).to receive(:pagy?).and_return(true)
+      end
+
+      it "works" do
+        expect(subject.controller_code).to eq(<<~RUBY)
+        class UsersController < ApplicationController
+          def index
+            @grid = UsersGrid.new(grid_params)
+            @pagy, @assets = pagy(@grid.assets)
+          end
+
+          protected
+
+          def grid_params
+            params.fetch(:users_grid, {}).permit!
+          end
+        end
+        RUBY
+      end
+    end
   end
 
   describe "#view_code" do
@@ -54,5 +77,21 @@ def grid_params
       <%= paginate(@grid.assets) %>
       ERB
     end
+
+    context "with pagy" do
+      before do
+        allow(subject).to receive(:pagy?).and_return(true)
+      end
+
+      it "works" do
+        expect(subject.view_code).to eq(<<~ERB)
+        <%= datagrid_form_with model: @grid, url: users_path %>
+
+        <%= pagy_nav(@pagy) %>
+        <%= datagrid_table @grid, @records %>
+        <%= pagy_nav(@pagy) %>
+        ERB
+      end
+    end
   end
 end

From 76b9792d75eef6a179c2decbe32334b2d6acf6d4 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 17 Nov 2024 11:40:47 +0100
Subject: [PATCH 138/157] Reprecate elements in favor of choices inside
 enum_checkboxes

---
 app/views/datagrid/_enum_checkboxes.html.erb   |  2 +-
 lib/datagrid/deprecated_object.rb              | 18 ++++++++++++++++++
 lib/datagrid/form_builder.rb                   | 14 +++++++++++++-
 spec/datagrid/form_builder_spec.rb             | 12 ++++++++++++
 .../_enum_checkboxes.html.erb                  |  1 +
 version-2/Readme.markdown                      | 12 ++++++++++++
 version-2/deprecations.sh                      |  5 +++++
 7 files changed, 62 insertions(+), 2 deletions(-)
 create mode 100644 lib/datagrid/deprecated_object.rb
 create mode 100644 spec/support/test_partials/deprecated_enum_checkboxes/_enum_checkboxes.html.erb

diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
index 5668c49..281bb6f 100644
--- a/app/views/datagrid/_enum_checkboxes.html.erb
+++ b/app/views/datagrid/_enum_checkboxes.html.erb
@@ -3,7 +3,7 @@ Indent in this file may cause extra space to appear.
 You can add indent if whitespace doesn't matter for you
 %>
 <div class="datagrid-enum-checkboxes">
-<%- elements.each do |value, text, checked| -%>
+<%- choices.each do |value, text| -%>
 <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
 <%= form.datagrid_label(filter.name, for: id, **options) do -%>
 <%= form.datagrid_filter_input(filter.name, id: id, value: value) -%>
diff --git a/lib/datagrid/deprecated_object.rb b/lib/datagrid/deprecated_object.rb
new file mode 100644
index 0000000..6d964eb
--- /dev/null
+++ b/lib/datagrid/deprecated_object.rb
@@ -0,0 +1,18 @@
+module Datagrid
+  # @!visibility private
+  class DeprecatedObject < BasicObject
+    def initialize(real_object, &block)
+      @real_object = real_object
+      @block = block
+    end
+
+    def method_missing(method_name, *args, &block)
+      @block.call
+      @real_object.public_send(method_name, *args, &block)
+    end
+
+    def respond_to_missing?(method_name, include_private = false)
+      @real_object.respond_to?(method_name, include_private)
+    end
+  end
+end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 6f497b5..378cf75 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -1,6 +1,7 @@
 # frozen_string_literal: true
 
 require "action_view"
+require 'datagrid/deprecated_object'
 
 module Datagrid
   module FormBuilder
@@ -88,11 +89,22 @@ def datagrid_enum_checkboxes_filter(filter, options = {})
         checked = enum_checkbox_checked?(filter, value)
         [value, text, checked]
       end
+      choices = elements.map do |value, text, *_|
+        [value, text]
+      end
       render_partial(
         "enum_checkboxes",
         {
-          elements: elements,
           form: self,
+          elements: Datagrid::DeprecatedObject.new(
+            elements,
+
+          ) do
+            Datagrid::Utils.warn_once(
+              "Using `elements` variable in app/views/datagrid/enum_checkboxes is deprecated, use `choices` instead."
+            )
+          end,
+          choices: choices,
           filter: filter,
           options: options,
         },
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 59434c0..4ac369a 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -475,6 +475,18 @@ class MyTemplate
           it { should equal_to_dom("custom_enum_checkboxes") }
         end
 
+        context "when using deprecated elements variable in partial" do
+          around do |ex|
+            Datagrid::Utils.deprecator.silence do
+              ex.run
+            end
+          end
+          let(:view_options) { { partials: "deprecated_enum_checkboxes" } }
+          it { should equal_to_dom(
+            [["first", "first",false],["second","second",false]].to_json
+          ) }
+        end
+
         context "when inline class attribute specified" do
           let(:_filter_options) { {for: nil, class: 'custom-class'} }
 
diff --git a/spec/support/test_partials/deprecated_enum_checkboxes/_enum_checkboxes.html.erb b/spec/support/test_partials/deprecated_enum_checkboxes/_enum_checkboxes.html.erb
new file mode 100644
index 0000000..cae2685
--- /dev/null
+++ b/spec/support/test_partials/deprecated_enum_checkboxes/_enum_checkboxes.html.erb
@@ -0,0 +1 @@
+<%= elements.to_json %>
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 116fcc8..147d87f 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -27,6 +27,7 @@ List of things introduces:
    * while supported, the library was not initially designed for it.
 1. HTML5 data attributes
 1. Consistent `label[for]` and `input[id]` for range filters.
+1. Updated app/views/datagrid/enum\_checkboxes
 1. Introduced `datagrid.filters.range.separator` localization
 1. Remove SASS dependency
 1. Replace `rake datagrid:copy_partials` with `rails g datagrid:views`
@@ -477,6 +478,17 @@ Version 2 generates id attribute only for the first input, so that a click on la
 The behavior can be changed by modifying 
 [built-in view](https://github.com/bogdan/datagrid/blob/version-2/app/views/datagrid/_range_filter.html.erb#L3).
 
+## Updated enum\_checkboxes view
+
+`app/views/datagrid/enum_checkboxes` is now configured differently:
+
+1. Use `datagrid_filter_input` instead of `check_box` to ensure `filter` options behave consistently.
+2. Use `choices` local variable instead of `elements`
+  * `elements` variables contains values: `value`, `text` and `checked`.
+  * `choices` has only first two values to ensure `checked` is determined automatically and consistently.
+
+Diff for [built-in partials between V1 and V2](./views.diff)
+
 ## ApplicationGrid base class
 
 Previously recommended base class `BaseGrid` is incosistent
diff --git a/version-2/deprecations.sh b/version-2/deprecations.sh
index 3460343..46340f5 100644
--- a/version-2/deprecations.sh
+++ b/version-2/deprecations.sh
@@ -18,3 +18,8 @@ git grep 'datagrid:copy_partials'
 git grep 'BaseDatagrid'
 git grep 'BaseGrid'
 
+# Use choices instead
+git grep 'elements' app/views/datagrid/_enum_checkboxes.*
+
+# Use datagrid_filter_input instead
+git grep 'check_box' app/views/datagrid/_enum_checkboxes.*

From e82f48bc4f2c6b8672d4c9f8affa7e05bdeb41f2 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 17 Nov 2024 11:51:01 +0100
Subject: [PATCH 139/157] Linting

---
 .rubocop.yml                              |  4 ++
 lib/datagrid/deprecated_object.rb         |  6 +-
 lib/datagrid/filters/date_filter.rb       |  4 +-
 lib/datagrid/filters/enum_filter.rb       |  2 +-
 lib/datagrid/form_builder.rb              |  7 ++-
 spec/datagrid/filters/date_filter_spec.rb |  3 +-
 spec/datagrid/form_builder_spec.rb        | 74 ++++++++++++-----------
 spec/datagrid/generators/scaffold_spec.rb | 34 +++++------
 8 files changed, 70 insertions(+), 64 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index 8ff7392..0c4cec2 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -35,3 +35,7 @@ Metrics/ModuleLength:
   Enabled: false
 Style/Documentation:
   Enabled: false
+Style/OptionalBooleanParameter:
+  Enabled: false
+Style/MultilineBlockChain:
+  Enabled: false
diff --git a/lib/datagrid/deprecated_object.rb b/lib/datagrid/deprecated_object.rb
index 6d964eb..aa31646 100644
--- a/lib/datagrid/deprecated_object.rb
+++ b/lib/datagrid/deprecated_object.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 module Datagrid
   # @!visibility private
   class DeprecatedObject < BasicObject
@@ -6,9 +8,9 @@ def initialize(real_object, &block)
       @block = block
     end
 
-    def method_missing(method_name, *args, &block)
+    def method_missing(method_name, ...)
       @block.call
-      @real_object.public_send(method_name, *args, &block)
+      @real_object.public_send(method_name, ...)
     end
 
     def respond_to_missing?(method_name, include_private = false)
diff --git a/lib/datagrid/filters/date_filter.rb b/lib/datagrid/filters/date_filter.rb
index cf3b7a5..e846226 100644
--- a/lib/datagrid/filters/date_filter.rb
+++ b/lib/datagrid/filters/date_filter.rb
@@ -12,9 +12,7 @@ def default_input_options
       end
 
       def apply(grid_object, scope, value)
-        if grid_object.driver.timestamp_column?(scope, name)
-          value = Datagrid::Utils.format_date_as_timestamp(value)
-        end
+        value = Datagrid::Utils.format_date_as_timestamp(value) if grid_object.driver.timestamp_column?(scope, name)
         super
       end
 
diff --git a/lib/datagrid/filters/enum_filter.rb b/lib/datagrid/filters/enum_filter.rb
index 75ea00a..d21d6f4 100644
--- a/lib/datagrid/filters/enum_filter.rb
+++ b/lib/datagrid/filters/enum_filter.rb
@@ -34,7 +34,7 @@ def label_options
           # Each checkbox has its own label
           # The main label has no specific input to focus
           # See app/views/datagrid/_enum_checkboxes.html.erb
-          {for: nil, **super}
+          { for: nil, **super }
         else
           super
         end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 378cf75..e6fe3bd 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 require "action_view"
-require 'datagrid/deprecated_object'
+require "datagrid/deprecated_object"
 
 module Datagrid
   module FormBuilder
@@ -98,10 +98,11 @@ def datagrid_enum_checkboxes_filter(filter, options = {})
           form: self,
           elements: Datagrid::DeprecatedObject.new(
             elements,
-
           ) do
             Datagrid::Utils.warn_once(
-              "Using `elements` variable in app/views/datagrid/enum_checkboxes is deprecated, use `choices` instead."
+              <<~MSG,
+                Using `elements` variable in enum_checkboxes view is deprecated, use `choices` instead.
+              MSG
             )
           end,
           choices: choices,
diff --git a/spec/datagrid/filters/date_filter_spec.rb b/spec/datagrid/filters/date_filter_spec.rb
index 60f2d9c..15e3fb3 100644
--- a/spec/datagrid/filters/date_filter_spec.rb
+++ b/spec/datagrid/filters/date_filter_spec.rb
@@ -261,7 +261,7 @@ def entry_dated(date)
 
   it "supports search by timestamp column" do
     report = test_report(created_at: Date.today) do
-      scope {Entry}
+      scope { Entry }
       filter(:created_at, :date)
     end
     e1 = Entry.create!(created_at: Date.yesterday + 23.hours)
@@ -276,7 +276,6 @@ def entry_dated(date)
     expect(report.assets).to_not include(e5)
   end
 
-
   it "allows filter to be defined before scope" do
     class ParentGrid < Datagrid::Base
       filter(:created_at, :date, range: true)
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 4ac369a..2519c3b 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -213,11 +213,11 @@ class MyTemplate
       context "with only right bound" do
         let(:_range) { [nil, 10] }
         it {
-          should equal_to_dom(
-            '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id"/>' \
-            '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="10" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>',
-          )
+          should equal_to_dom(<<~HTML)
+            <input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id"/>
+            <span class="datagrid-range-separator"> - </span>
+            <input value="10" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>
+          HTML
         }
         it { should be_html_safe }
       end
@@ -225,11 +225,11 @@ class MyTemplate
       context "with invalid range value" do
         let(:_range) { 2..1 }
         it {
-          should equal_to_dom(
-            '<input value="1" class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id"/>' \
-            '<span class="datagrid-range-separator"> - </span>' \
-            '<input value="2" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>',
-          )
+          should equal_to_dom(<<~HTML)
+            <input value="1" class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id"/>
+            <span class="datagrid-range-separator"> - </span>
+            <input value="2" class="datagrid-range-to" type="number" step="1" name="report[group_id][to]"/>
+          HTML
         }
       end
 
@@ -247,11 +247,11 @@ class MyTemplate
         let(:view_options) { { partials: "not_existed" } }
         let(:_range) { nil }
         it {
-          should equal_to_dom(
-            '<input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id">
+          should equal_to_dom(<<~HTML)
+            <input class="datagrid-range-from" type="number" step="1" name="report[group_id][from]" id="report_group_id">
             <span class="datagrid-range-separator"> - </span>
-            <input class="datagrid-range-to" type="number" step="1" name="report[group_id][to]">',
-          )
+            <input class="datagrid-range-to" type="number" step="1" name="report[group_id][to]">
+          HTML
         }
       end
     end
@@ -287,11 +287,11 @@ class MyTemplate
       context "with only left bound" do
         let(:_range) { ["2012-01-03", nil] }
         it {
-          should equal_to_dom(
-            '<input value="2012-01-03" class="datagrid-range-from" type="date" name="report[created_at][from]" id="report_created_at"/>' \
-            '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" type="date" name="report[created_at][to]" value=""/>',
-          )
+          should equal_to_dom(<<~HTML)
+            <input value="2012-01-03" class="datagrid-range-from" type="date" name="report[created_at][from]" id="report_created_at"/>
+            <span class="datagrid-range-separator"> - </span>
+            <input class="datagrid-range-to" type="date" name="report[created_at][to]" value=""/>
+          HTML
         }
         it { should be_html_safe }
       end
@@ -346,11 +346,11 @@ class MyTemplate
         end
         let(:_range) { [nil, nil] }
         it {
-          should equal_to_dom(
-            '<input class="datagrid-range-from" type="date" value="" name="report[created_at][from]" id="report_created_at"/>' \
-            '<span class="datagrid-range-separator"> - </span>' \
-            '<input class="datagrid-range-to" type="date" value="" name="report[created_at][to]"/>',
-          )
+          should equal_to_dom(<<~HTML)
+            <input class="datagrid-range-from" type="date" value="" name="report[created_at][from]" id="report_created_at"/>
+            <span class="datagrid-range-separator"> - </span>
+            <input class="datagrid-range-to" type="date" value="" name="report[created_at][to]"/>
+          HTML
         }
       end
     end
@@ -482,23 +482,25 @@ class MyTemplate
             end
           end
           let(:view_options) { { partials: "deprecated_enum_checkboxes" } }
-          it { should equal_to_dom(
-            [["first", "first",false],["second","second",false]].to_json
-          ) }
+          it {
+            should equal_to_dom(
+              [["first", "first", false], ["second", "second", false]].to_json,
+            )
+          }
         end
 
         context "when inline class attribute specified" do
-          let(:_filter_options) { {for: nil, class: 'custom-class'} }
+          let(:_filter_options) { { for: nil, class: "custom-class" } }
 
           it { should equal_to_dom(<<~HTML) }
-          <div class="datagrid-enum-checkboxes">
-            <label class="custom-class">
-              <input id="report_category_first" value="first" type="checkbox" name="report[category][]">first
-            </label>
-            <label class="custom-class">
-              <input id="report_category_second" value="second" type="checkbox" name="report[category][]">second
-            </label>
-          </div>
+            <div class="datagrid-enum-checkboxes">
+              <label class="custom-class">
+                <input id="report_category_first" value="first" type="checkbox" name="report[category][]">first
+              </label>
+              <label class="custom-class">
+                <input id="report_category_second" value="second" type="checkbox" name="report[category][]">second
+              </label>
+            </div>
           HTML
         end
       end
diff --git a/spec/datagrid/generators/scaffold_spec.rb b/spec/datagrid/generators/scaffold_spec.rb
index 19a00df..843b187 100644
--- a/spec/datagrid/generators/scaffold_spec.rb
+++ b/spec/datagrid/generators/scaffold_spec.rb
@@ -50,18 +50,18 @@ def grid_params
 
       it "works" do
         expect(subject.controller_code).to eq(<<~RUBY)
-        class UsersController < ApplicationController
-          def index
-            @grid = UsersGrid.new(grid_params)
-            @pagy, @assets = pagy(@grid.assets)
-          end
+          class UsersController < ApplicationController
+            def index
+              @grid = UsersGrid.new(grid_params)
+              @pagy, @assets = pagy(@grid.assets)
+            end
 
-          protected
+            protected
 
-          def grid_params
-            params.fetch(:users_grid, {}).permit!
+            def grid_params
+              params.fetch(:users_grid, {}).permit!
+            end
           end
-        end
         RUBY
       end
     end
@@ -70,11 +70,11 @@ def grid_params
   describe "#view_code" do
     it "works" do
       expect(subject.view_code).to eq(<<~ERB)
-      <%= datagrid_form_with model: @grid, url: users_path %>
+        <%= datagrid_form_with model: @grid, url: users_path %>
 
-      <%= paginate(@grid.assets) %>
-      <%= datagrid_table @grid %>
-      <%= paginate(@grid.assets) %>
+        <%= paginate(@grid.assets) %>
+        <%= datagrid_table @grid %>
+        <%= paginate(@grid.assets) %>
       ERB
     end
 
@@ -85,11 +85,11 @@ def grid_params
 
       it "works" do
         expect(subject.view_code).to eq(<<~ERB)
-        <%= datagrid_form_with model: @grid, url: users_path %>
+          <%= datagrid_form_with model: @grid, url: users_path %>
 
-        <%= pagy_nav(@pagy) %>
-        <%= datagrid_table @grid, @records %>
-        <%= pagy_nav(@pagy) %>
+          <%= pagy_nav(@pagy) %>
+          <%= datagrid_table @grid, @records %>
+          <%= pagy_nav(@pagy) %>
         ERB
       end
     end

From 7d6eca1b58f06225c22f3bb29da17bd1570bd1a8 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 17 Nov 2024 11:58:37 +0100
Subject: [PATCH 140/157] Update diff

---
 version-2/views.diff | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index e49aaa8..da806d5 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -1,13 +1,14 @@
 diff --git a/app/views/datagrid/_enum_checkboxes.html.erb b/app/views/datagrid/_enum_checkboxes.html.erb
-index 9f48319..5668c49 100644
+index 9f48319..281bb6f 100644
 --- a/app/views/datagrid/_enum_checkboxes.html.erb
 +++ b/app/views/datagrid/_enum_checkboxes.html.erb
 @@ -2,10 +2,12 @@
  Indent in this file may cause extra space to appear.
  You can add indent if whitespace doesn't matter for you
  %>
+-<%- elements.each do |value, text, checked| -%>
 +<div class="datagrid-enum-checkboxes">
- <%- elements.each do |value, text, checked| -%>
++<%- choices.each do |value, text| -%>
  <%- id = [form.object_name, filter.name, value].join('_').underscore -%>
 -<%= form.label filter.name, options.merge(for: id) do -%>
 -<%= form.check_box(filter.name, {multiple: true, id: id, checked: checked, include_hidden: false}, value.to_s, nil) -%>

From 036575035ba28d1e7e41b8b856bacdae56bc418a Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 17 Nov 2024 15:08:54 +0100
Subject: [PATCH 141/157] Add column[tag_options] option instead of
 column[class]

---
 CHANGELOG.md                      |  3 +++
 app/views/datagrid/_head.html.erb |  4 ++--
 app/views/datagrid/_row.html.erb  |  4 ++--
 lib/datagrid/columns.rb           |  4 +++-
 lib/datagrid/columns/column.rb    | 16 ++++++++++++++++
 spec/datagrid/helper_spec.rb      | 26 ++++++++++++++++++++++----
 spec/spec_helper.rb               |  5 +++++
 version-2/Readme.markdown         | 15 +++++++++++++++
 8 files changed, 68 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75abc2c..d9e0c68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
 
 ## 2.0.0
 
+Version 2 is a major update implementing a lot of major improvements
+at the cost of backward compatibility.
+
 [Changes and migration guide](./version-2)
 
 ## 1.8.3
diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index 71e6b03..a7880e4 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -6,9 +6,9 @@
         # Consider maintaining consistency with datagrid/rows partial
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
         "datagrid-order-active-desc": grid.ordered_by?(column, true),
-        column.html_class => column.html_class.present?,
       },
-      "data-column": column.name
+      "data-column": column.name,
+      **column.tag_options,
     ) do %>
       <%= column.header %>
       <% if column.supports_order? && options[:order] -%>
diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
index c64b0bf..6d293cf 100644
--- a/app/views/datagrid/_row.html.erb
+++ b/app/views/datagrid/_row.html.erb
@@ -7,9 +7,9 @@
         # Consider maintaining consistency with datagrid/head partial
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
         "datagrid-order-active-desc": grid.ordered_by?(column, true),
-        column.html_class => column.html_class.present?,
       },
-      "data-column": column.name
+      "data-column": column.name,
+      **column.tag_options,
     ) %>
   <% end %>
 </tr>
diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index 5b495ac..9f146f6 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -71,7 +71,7 @@ def columns(*column_names, data: false, html: false)
       # * <tt>html</tt> - determines if current column should be present in html table and how is it formatted
       # * <tt>order</tt> - determines if this column could be sortable and how.
       #   The value of order is explicitly passed to ORM ordering method.
-      #   Ex: <tt>"created_at, id"</tt> for ActiveRecord, <tt>[:created_at, :id]</tt> for Mongoid
+      #   Example: <tt>"created_at, id"</tt> for ActiveRecord, <tt>[:created_at, :id]</tt> for Mongoid
       # * <tt>order_desc</tt> - determines a descending order for given column
       #   (only in case when <tt>:order</tt> can not be easily reversed by ORM)
       # * <tt>order_by_value</tt> - used in case it is easier to perform ordering at ruby level not on database level.
@@ -85,6 +85,8 @@ def columns(*column_names, data: false, html: false)
       # * <tt>if</tt> - the column is shown if the reult of calling this argument is true
       # * <tt>unless</tt> - the column is shown unless the reult of calling this argument is true
       # * <tt>preload</tt> - spefies which associations of the scope should be preloaded for this column
+      # * `tag_options` - specify HTML attributes to be set for `<td>` or `<th>` of a column
+      #   Example: `{ class: "content-align-right", "data-group": "statistics" }`
       #
       # @see https://github.com/bogdan/datagrid/wiki/Columns
       def column(name, query = nil, **options, &block)
diff --git a/lib/datagrid/columns/column.rb b/lib/datagrid/columns/column.rb
index 502295d..65f4aad 100644
--- a/lib/datagrid/columns/column.rb
+++ b/lib/datagrid/columns/column.rb
@@ -39,6 +39,15 @@ def initialize(grid_class, name, query, options = {}, &block)
         self.grid_class = grid_class
         self.name = name.to_sym
         self.options = options
+        if options[:class]
+          Datagrid::Utils.warn_once(
+            "column[class] option is deprecated. Use {tag_options: {class: ...}} instead."
+          )
+          self.options[:tag_options] = {
+            **self.options.fetch(:tag_options, {}),
+            class: options[:class],
+          }
+        end
         if options[:html] == true
           self.html_block = block
         else
@@ -108,7 +117,14 @@ def mandatory?
         !!options[:mandatory]
       end
 
+      def tag_options
+        options[:tag_options] || {}
+      end
+
       def html_class
+        Datagrid::Utils.warn_once(
+          "Column#html_class is deprecated. Use Column#tag_options instead."
+        )
         options[:class]
       end
 
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index b5fcdd7..45bd6ef 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -377,16 +377,34 @@
     end
 
     it "should allow CSS classes to be specified for a column" do
-      rp = test_report do
-        scope { Entry }
-        column(:name, class: "my_class")
+      rp = expect_deprecated do
+        test_report do
+          scope { Entry }
+          column(:name, class: "my-class")
+        end
       end
 
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
-        "tr td[data-column=name].my_class" => "Star",
+        "tr td[data-column=name].my-class" => "Star",
       )
     end
 
+    it "supports tag_options option" do
+      report = test_report do
+        scope { Entry }
+        column(:name, tag_options: {
+          class: 'my-class',
+          "data-sort-method": "qsort"
+        })
+      end
+
+      expect(subject.datagrid_rows(report, [entry])).to equal_to_dom(<<~HTML)
+        <tr>
+          <td class="my-class" data-column="name" data-sort-method="qsort">Star</td>
+        </tr>
+       HTML
+    end
+
     context "when grid has complicated columns" do
       let(:grid) do
         test_report(name: "Hello") do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 2322b78..cbcefb8 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -106,6 +106,11 @@ def silence_deprecator(&block)
   Datagrid::Utils.deprecator.silence(&block)
 end
 
+def expect_deprecated(message = /deprecated/, &block)
+  expect(Datagrid::Utils.deprecator).to receive(:warn).with(message)
+  block.call
+end
+
 # Requires supporting files with custom matchers and macros, etc,
 # in ./support/ and its subdirectories.
 Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 147d87f..3d8c9e5 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -26,6 +26,7 @@ List of things introduces:
 1. Native Rails Engines:
    * while supported, the library was not initially designed for it.
 1. HTML5 data attributes
+1. Use `column[tag_options]` option instead of `column[class]`.
 1. Consistent `label[for]` and `input[id]` for range filters.
 1. Updated app/views/datagrid/enum\_checkboxes
 1. Introduced `datagrid.filters.range.separator` localization
@@ -453,6 +454,20 @@ Renders:
 [Modify built-in views](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
 if you want to change this behavior completely.
 
+## Use column[tag\_options]
+
+`column[class]` option is deprecated in favor of more flexible `column[tag_options]` 
+that allows to specify any `td/td` html attribute.
+
+Example migration:
+
+``` ruby
+# V1
+column(:status, class: 'issue-status')
+# V2
+column(:status, tag_options: {class: 'issue-status'})
+```
+
 ## id attribute for range filter inputs
 
 [W3 validator](https://validator.w3.org/) complains when 

From 5acee620b38dcd2fd568a1e13c2837436adeb94e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Sun, 17 Nov 2024 19:14:14 +0100
Subject: [PATCH 142/157] Improve tag_options: merge custom class with order
 classes

---
 app/views/datagrid/_head.html.erb             | 11 +++++----
 app/views/datagrid/_row.html.erb              | 13 +++++-----
 spec/datagrid/helper_spec.rb                  | 24 ++++++++++++++++---
 version-2/Readme.markdown                     |  4 +++-
 ...n.rb => find_deprecated_column_options.rb} |  2 +-
 5 files changed, 38 insertions(+), 16 deletions(-)
 rename version-2/{find_deprecated_url_option.rb => find_deprecated_column_options.rb} (92%)

diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index a7880e4..ee5d89e 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -1,14 +1,15 @@
 <tr>
   <% grid.html_columns(*options[:columns]).each do |column| %>
     <%= tag.th(
-      class: {
+      # Consider maintaining consistency with datagrid/rows partial
+      "data-column": column.name,
+      **column.tag_options,
+      class: class_names(
+        column.tag_options[:class],
         # Adding HTML classes based on condition
-        # Consider maintaining consistency with datagrid/rows partial
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
         "datagrid-order-active-desc": grid.ordered_by?(column, true),
-      },
-      "data-column": column.name,
-      **column.tag_options,
+      )
     ) do %>
       <%= column.header %>
       <% if column.supports_order? && options[:order] -%>
diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
index 6d293cf..b493cfe 100644
--- a/app/views/datagrid/_row.html.erb
+++ b/app/views/datagrid/_row.html.erb
@@ -2,14 +2,15 @@
   <% grid.html_columns(*options[:columns]).each do |column| %>
     <%= tag.td(
       datagrid_value(grid, column, asset),
-      class: {
-        # Adding html clases based on condition
-        # Consider maintaining consistency with datagrid/head partial
-        "datagrid-order-active-asc": grid.ordered_by?(column, false),
-        "datagrid-order-active-desc": grid.ordered_by?(column, true),
-      },
+      # Consider maintaining consistency with datagrid/rows partial
       "data-column": column.name,
       **column.tag_options,
+      class: class_names(
+        column.tag_options[:class],
+        # Adding HTML classes based on condition
+        "datagrid-order-active-asc": grid.ordered_by?(column, false),
+        "datagrid-order-active-desc": grid.ordered_by?(column, true),
+      )
     ) %>
   <% end %>
 </tr>
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 45bd6ef..ce89042 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -390,17 +390,18 @@
     end
 
     it "supports tag_options option" do
-      report = test_report do
+      report = test_report(order: :name, descending: true) do
         scope { Entry }
         column(:name, tag_options: {
           class: 'my-class',
-          "data-sort-method": "qsort"
+          "data-column-group": "core",
+          "data-column": nil,
         })
       end
 
       expect(subject.datagrid_rows(report, [entry])).to equal_to_dom(<<~HTML)
         <tr>
-          <td class="my-class" data-column="name" data-sort-method="qsort">Star</td>
+          <td class="my-class datagrid-order-active-desc" data-column-group="core">Star</td>
         </tr>
        HTML
     end
@@ -743,6 +744,23 @@ def param_name
         </th></tr>
       HTML
     end
+
+    it "supports tag_options option" do
+      grid = test_report(order: :name, descending: true) do
+        scope { Entry }
+        column(:name, order: false, tag_options: {
+          class: 'my-class',
+          "data-column-group": "core",
+          "data-column": nil,
+        })
+      end
+
+      expect(subject.datagrid_header(grid)).to equal_to_dom(<<~HTML)
+        <tr>
+          <th class="my-class datagrid-order-active-desc" data-column-group="core">Name</th>
+        </tr>
+       HTML
+    end
   end
 
   describe ".datagrid_column_classes" do
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 3d8c9e5..b9f0b12 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -55,7 +55,7 @@ column(:user) do |user|
 end
 ```
 
-All deprecated columns can be found [with a script](./find_deprecated_url_option.rb)
+All deprecated columns can be found [with a script](./find_deprecated_column_options.rb).
 
 ## Use form\_with
 
@@ -468,6 +468,8 @@ column(:status, class: 'issue-status')
 column(:status, tag_options: {class: 'issue-status'})
 ```
 
+All deprecated columns can be found [with a script](./find_deprecated_column_options.rb).
+
 ## id attribute for range filter inputs
 
 [W3 validator](https://validator.w3.org/) complains when 
diff --git a/version-2/find_deprecated_url_option.rb b/version-2/find_deprecated_column_options.rb
similarity index 92%
rename from version-2/find_deprecated_url_option.rb
rename to version-2/find_deprecated_column_options.rb
index fbb5f19..1b03e50 100644
--- a/version-2/find_deprecated_url_option.rb
+++ b/version-2/find_deprecated_column_options.rb
@@ -13,7 +13,7 @@
 classes = [*included_classes, *base_subclasses].uniq
 
 classes.flat_map(&:columns).select do |f|
-  f.options[:url]
+  f.options[:url] || f.options[:class]
 end.map do |f|
   [f.grid_class, f.name].join("#")
 end

From af0985f6e7c67ccbd4c6819d43fd58eb0f2f5128 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 11:30:18 +0100
Subject: [PATCH 143/157] Cleanup

---
 spec/datagrid/core_spec.rb                   |  9 ++----
 spec/datagrid/filters/integer_filter_spec.rb |  7 ++---
 version-2/Readme.markdown                    |  6 ++--
 version-2/views.diff                         | 32 +++++++++++---------
 4 files changed, 26 insertions(+), 28 deletions(-)

diff --git a/spec/datagrid/core_spec.rb b/spec/datagrid/core_spec.rb
index 35a03a4..58cae7e 100644
--- a/spec/datagrid/core_spec.rb
+++ b/spec/datagrid/core_spec.rb
@@ -231,12 +231,9 @@ class EqualTest < Datagrid::Base
     end
 
     it "supports hash attribute assignment" do
-      grid = test_grid(
-        ActionController::Parameters.new(group_id: { from: 1, to: 2 }),
-      ) do
-        scope { Entry }
-        filter(:group_id, :integer, range: true)
-      end
+      grid = test_grid_filter(:group_id, :integer, range: true)
+      grid.attributes = ActionController::Parameters.new(group_id: { from: 1, to: 2 })
+
       expect(grid.group_id).to eq(1..2)
     end
   end
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index 0ffd5d9..4c310bb 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -64,10 +64,9 @@
   end
 
   it "converts infinite range to nil" do
-    report = test_grid(group_id: (nil..nil)) do
-      scope { Entry }
-      filter(:group_id, :integer, range: true)
-    end
+    report = test_grid_filter(:group_id, :integer, range: true)
+    report.group_id = nil..nil
+
     expect(report.group_id).to eq(nil)
   end
 
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index b9f0b12..50487b3 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -72,7 +72,7 @@ datagrid_form_for(@users_grid, url: users_path)
 datagrid_form_with(model: @users_grid, url: users_path)
 ```
 
-Version 2 built-in view `datagrid/form` uses `form_with` no matter of the with helper is used.
+Version 2 built-in view `datagrid/form` uses `form_with` no matter of the which helper is used.
 Beware of that.
 
 [Grep all deprecations](./deprecations.sh).
@@ -133,7 +133,7 @@ grid.id # V1: [1, nil]
 Version 2 makes an effort to make the transition as smooth as possible to you:
 
 * Old Array format will be converted to new Range format
-* Serialization/Deserialization of Range is help correctly
+* Serialization/Deserialization of Range is held correctly
 
 ``` ruby
 grid.id = 1..5
@@ -457,7 +457,7 @@ if you want to change this behavior completely.
 ## Use column[tag\_options]
 
 `column[class]` option is deprecated in favor of more flexible `column[tag_options]` 
-that allows to specify any `td/td` html attribute.
+that allows to specify any `th/td` html attribute.
 
 Example migration:
 
diff --git a/version-2/views.diff b/version-2/views.diff
index da806d5..5d13642 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -40,22 +40,23 @@ index 7e175c1..fc4f4ae 100644
    </div>
  <% end -%>
 diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
-index e939128..71e6b03 100644
+index e939128..ee5d89e 100644
 --- a/app/views/datagrid/_head.html.erb
 +++ b/app/views/datagrid/_head.html.erb
-@@ -1,8 +1,30 @@
+@@ -1,8 +1,31 @@
  <tr>
    <% grid.html_columns(*options[:columns]).each do |column| %>
 -    <th class="<%= datagrid_column_classes(grid, column) %>">
 +    <%= tag.th(
-+      class: {
++      # Consider maintaining consistency with datagrid/rows partial
++      "data-column": column.name,
++      **column.tag_options,
++      class: class_names(
++        column.tag_options[:class],
 +        # Adding HTML classes based on condition
-+        # Consider maintaining consistency with datagrid/rows partial
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
 +        "datagrid-order-active-desc": grid.ordered_by?(column, true),
-+        column.html_class => column.html_class.present?,
-+      },
-+      "data-column": column.name
++      )
 +    ) do %>
        <%= column.header %>
 -      <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
@@ -91,23 +92,24 @@ index 7a8a123..faa2575 100644
 +<%# there is no duplicate id in DOM and click on label focuses the first input -%>
 +<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
 diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
-index f54d21c..c64b0bf 100644
+index f54d21c..b493cfe 100644
 --- a/app/views/datagrid/_row.html.erb
 +++ b/app/views/datagrid/_row.html.erb
-@@ -1,5 +1,15 @@
+@@ -1,5 +1,16 @@
  <tr>
    <% grid.html_columns(*options[:columns]).each do |column| %>
 -    <td class="<%= datagrid_column_classes(grid, column) %>"><%= datagrid_value(grid, column, asset) %></td>
 +    <%= tag.td(
 +      datagrid_value(grid, column, asset),
-+      class: {
-+        # Adding html clases based on condition
-+        # Consider maintaining consistency with datagrid/head partial
++      # Consider maintaining consistency with datagrid/rows partial
++      "data-column": column.name,
++      **column.tag_options,
++      class: class_names(
++        column.tag_options[:class],
++        # Adding HTML classes based on condition
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
 +        "datagrid-order-active-desc": grid.ordered_by?(column, true),
-+        column.html_class => column.html_class.present?,
-+      },
-+      "data-column": column.name
++      )
 +    ) %>
    <% end %>
  </tr>

From f5fb22d628f342003eb3714c5722ec69644cddb4 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 11:41:01 +0100
Subject: [PATCH 144/157] Support tag_options in deprecated
 datagrid_column_classes

---
 lib/datagrid/helper.rb       | 15 ++++++++-------
 spec/datagrid/helper_spec.rb |  8 ++++++--
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index f18b124..bc42a56 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -167,12 +167,7 @@ def datagrid_order_path(grid, column, descending)
       datagrid_renderer.order_path(grid, column, descending, request)
     end
 
-    protected
-
-    def datagrid_renderer
-      Renderer.for(self)
-    end
-
+    # @!visibility private
     def datagrid_column_classes(grid, column)
       Datagrid::Utils.warn_once(<<~MSG)
         datagrid_column_classes is deprecated. Assign necessary classes manually.
@@ -182,7 +177,13 @@ def datagrid_column_classes(grid, column)
       order_class = if grid.ordered_by?(column)
                       ["ordered", grid.descending ? "desc" : "asc"]
                     end
-      [column.name, order_class, column.options[:class]].compact.join(" ")
+      class_names(column.name, order_class, column.options[:class], column.tag_options[:class])
+    end
+
+    protected
+
+    def datagrid_renderer
+      Renderer.for(self)
     end
   end
 end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 0c7bce9..37aaa34 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -731,13 +731,17 @@ def param_name
       grid = test_grid(order: :name, descending: true) do
         scope { Entry }
         column(:name)
+        column(:category, tag_options: { class: "long-column" })
         column(:group_id, class: "short-column")
       end
       silence_deprecator do
-        expect(subject.send(:datagrid_column_classes, grid, :name)).to eq(
+        expect(subject.datagrid_column_classes(grid, :name)).to eq(
           "name ordered desc",
         )
-        expect(subject.send(:datagrid_column_classes, grid, :group_id)).to eq(
+        expect(subject.datagrid_column_classes(grid, :category)).to eq(
+          "category long-column",
+        )
+        expect(subject.datagrid_column_classes(grid, :group_id)).to eq(
           "group_id short-column",
         )
       end

From fd2af34ff12d1bad78b2aa4dd243d1666d693ced Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 12:25:32 +0100
Subject: [PATCH 145/157] Merge Renderer into Helper

---
 app/views/datagrid/_head.html.erb             |   2 +-
 app/views/datagrid/_table.html.erb            |   2 +-
 lib/datagrid/base.rb                          |   2 -
 lib/datagrid/helper.rb                        | 129 ++++++++++++--
 lib/datagrid/renderer.rb                      | 158 ------------------
 spec/datagrid/helper_spec.rb                  |  15 ++
 .../client/datagrid/_table.html.erb           |   2 +-
 7 files changed, 134 insertions(+), 176 deletions(-)
 delete mode 100644 lib/datagrid/renderer.rb

diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index ee5d89e..de2f969 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -9,7 +9,7 @@
         # Adding HTML classes based on condition
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
         "datagrid-order-active-desc": grid.ordered_by?(column, true),
-      )
+      ).presence
     ) do %>
       <%= column.header %>
       <% if column.supports_order? && options[:order] -%>
diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
index 2e9273e..88eeafb 100644
--- a/app/views/datagrid/_table.html.erb
+++ b/app/views/datagrid/_table.html.erb
@@ -7,7 +7,7 @@ Local variables:
 <% if grid.html_columns(*options[:columns]).any? %>
   <%= tag.table class: 'datagrid-table', **options.fetch(:html, {}) do %>
     <thead>
-      <%= datagrid_header(grid, options) %>
+      <%= datagrid_header(grid, **options) %>
     </thead>
     <tbody>
       <% if assets.any? %>
diff --git a/lib/datagrid/base.rb b/lib/datagrid/base.rb
index f904d16..f941241 100644
--- a/lib/datagrid/base.rb
+++ b/lib/datagrid/base.rb
@@ -14,8 +14,6 @@ module Datagrid
   autoload :Helper
   autoload :FormBuilder
 
-  autoload :Renderer
-
   autoload :Engine
 
   # Main datagrid class allowing to define columns and filters on your objects
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index bc42a56..74ca557 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -16,7 +16,9 @@ module Helper
     #     <% end %>
     #   </ul>
     def datagrid_value(grid, column, model)
-      datagrid_renderer.format_value(grid, column, model)
+      column = grid.column_by_name(column) if column.is_a?(String) || column.is_a?(Symbol)
+
+      grid.html_value(column, self, model)
     end
 
     # @!visibility private
@@ -44,7 +46,14 @@ def datagrid_format_value(grid, column, model)
     #   assets = grid.assets.page(params[:page])
     #   datagrid_table(grid, assets, options)
     def datagrid_table(grid, assets = grid.assets, **options)
-      datagrid_renderer.table(grid, assets, **options)
+      _render_partial(
+        "table", options[:partials],
+        {
+          grid: grid,
+          options: options,
+          assets: assets,
+        },
+      )
     end
 
     # Renders HTML table header for given grid instance using columns defined in it
@@ -60,8 +69,15 @@ def datagrid_table(grid, assets = grid.assets, **options)
     #   Default: 'datagrid'.
     # @param grid [Datagrid] grid object
     # @return [String] HTML table header tag markup
-    def datagrid_header(grid, options = {})
-      datagrid_renderer.header(grid, options)
+    def datagrid_header(grid, opts = :__unspecified__, **options)
+      unless opts == :__unspecified__
+        Datagrid::Utils.warn_once("datagrid_header now requires ** operator when passing options.")
+        options.reverse_merge!(opts)
+      end
+      options[:order] = true unless options.key?(:order)
+
+      _render_partial("head", options[:partials],
+        { grid: grid, options: options },)
     end
 
     # Renders HTML table rows using given grid definition using columns defined in it.
@@ -84,7 +100,11 @@ def datagrid_header(grid, options = {})
     #       %td= row.project_name
     #       %td.project-status{class: row.status}= row.status
     def datagrid_rows(grid, assets = grid.assets, **options, &block)
-      datagrid_renderer.rows(grid, assets, **options, &block)
+      safe_join(
+        assets.map do |asset|
+          datagrid_row(grid, asset, **options, &block)
+        end.to_a,
+      )
     end
 
     # @return [String] renders ordering controls for the given column name
@@ -99,7 +119,8 @@ def datagrid_order_for(grid, column, options = {})
         Put necessary code inline inside datagrid/head partial.
         See built-in partial for example.
       MSG
-      datagrid_renderer.order_for(grid, column, options)
+      _render_partial("order_for", options[:partials],
+        { grid: grid, column: column },)
     end
 
     # Renders HTML for grid with all filters inputs and labels defined in it
@@ -116,7 +137,10 @@ def datagrid_order_for(grid, column, options = {})
     def datagrid_form_with(**options)
       raise ArgumentError, "datagrid_form_with block argument is invalid. Use form_with instead." if block_given?
 
-      datagrid_renderer.form_with(**options)
+      grid = options[:model]
+      raise ArgumentError, "Grid has no available filters" if grid&.filters&.empty?
+
+      _render_partial("form", options[:partials], { grid: options[:model], options: options })
     end
 
     # Renders HTML for grid with all filters inputs and labels defined in it
@@ -131,7 +155,16 @@ def datagrid_form_with(**options)
     # @return [String] form HTML tag markup
     def datagrid_form_for(grid, options = {})
       Datagrid::Utils.warn_once("datagrid_form_for is deprecated if favor of datagrid_form_with.")
-      datagrid_renderer.form_for(grid, options)
+      _render_partial(
+        "form", options[:partials],
+        grid: grid,
+        options: {
+          method: :get,
+          as: grid.param_name,
+          local: true,
+          **options,
+        },
+      )
     end
 
     # Provides access to datagrid columns data.
@@ -154,8 +187,10 @@ def datagrid_form_for(grid, options = {})
     #   Last Name: <%= row.last_name %>
     # @example
     #   <%= datagrid_row(grid, user, columns: [:first_name, :last_name, :actions]) %>
-    def datagrid_row(grid, asset, ...)
-      datagrid_renderer.row(grid, asset, ...)
+    def datagrid_row(grid, asset, **options, &block)
+      Datagrid::Helper::HtmlRow.new(self, grid, asset, options).tap do |row|
+        return capture(row, &block) if block_given?
+      end
     end
 
     # Generates an ascending or descending order url for the given column
@@ -164,7 +199,12 @@ def datagrid_row(grid, asset, ...)
     # @param descending [Boolean] specifies order direction. Ascending if false, otherwise descending.
     # @return [String] order layout HTML markup
     def datagrid_order_path(grid, column, descending)
-      datagrid_renderer.order_path(grid, column, descending, request)
+      column = grid.column_by_name(column)
+      query = request&.query_parameters || {}
+      ActionDispatch::Http::URL.path_for(
+        path: request&.path || "/",
+        params: query.merge(grid.query_params(order: column.name, descending: descending)),
+      )
     end
 
     # @!visibility private
@@ -182,8 +222,71 @@ def datagrid_column_classes(grid, column)
 
     protected
 
-    def datagrid_renderer
-      Renderer.for(self)
+    def _render_partial(partial_name, partials_path, locals = {})
+      render({
+        partial: File.join(partials_path || "datagrid", partial_name),
+        locals: locals,
+      })
+    end
+
+    # Represents a datagrid row that provides access to column values for the given asset
+    # @example
+    #   row = datagrid_row(grid, user)
+    #   row.class      # => Datagrid::Helper::HtmlRow
+    #   row.first_name # => "<strong>Bogdan</strong>"
+    #   row.grid       # => Grid object
+    #   row.asset      # => User object
+    #   row.each do |value|
+    #     puts value
+    #   end
+    class HtmlRow
+      include Enumerable
+
+      attr_reader :grid, :asset, :options
+
+      # @!visibility private
+      def initialize(renderer, grid, asset, options)
+        @renderer = renderer
+        @grid = grid
+        @asset = asset
+        @options = options
+      end
+
+      # @return [Object] a column value for given column name
+      def get(column)
+        @renderer.datagrid_value(@grid, column, @asset)
+      end
+
+      # Iterates over all column values that are available in the row
+      # param block [Proc] column value iterator
+      def each(&block)
+        (@options[:columns] || @grid.html_columns).each do |column|
+          block.call(get(column))
+        end
+      end
+
+      # @return [String] HTML row format
+      def to_s
+        @renderer.send(:_render_partial, "row", options[:partials], {
+          grid: grid,
+          options: options,
+          asset: asset,
+        },)
+      end
+
+      protected
+
+      def method_missing(method, *args, &blk)
+        if (column = @grid.column_by_name(method))
+          get(column)
+        else
+          super
+        end
+      end
+
+      def respond_to_missing?(method, include_private = false)
+        !!@grid.column_by_name(method) || super
+      end
     end
   end
 end
diff --git a/lib/datagrid/renderer.rb b/lib/datagrid/renderer.rb
deleted file mode 100644
index e2fbbc6..0000000
--- a/lib/datagrid/renderer.rb
+++ /dev/null
@@ -1,158 +0,0 @@
-# frozen_string_literal: true
-
-require "action_view"
-
-module Datagrid
-  # @!visibility private
-  class Renderer
-    def self.for(template)
-      new(template)
-    end
-
-    def initialize(template)
-      @template = template
-    end
-
-    def format_value(grid, column, asset)
-      column = grid.column_by_name(column) if column.is_a?(String) || column.is_a?(Symbol)
-
-      grid.html_value(column, @template, asset)
-    end
-
-    def form_for(grid, options = {})
-      _render_partial(
-        "form", options[:partials],
-        grid: grid,
-        options: {
-          method: :get,
-          as: grid.param_name,
-          local: true,
-          **options,
-        },
-      )
-    end
-
-    def form_with(**options)
-      grid = options[:model]
-      raise ArgumentError, "Grid has no available filters" if grid&.filters&.empty?
-
-      _render_partial("form", options[:partials], { grid: options[:model], options: options })
-    end
-
-    def table(grid, assets, **options)
-      _render_partial(
-        "table", options[:partials],
-        {
-          grid: grid,
-          options: options,
-          assets: assets,
-        },
-      )
-    end
-
-    def header(grid, options = {})
-      options[:order] = true unless options.key?(:order)
-
-      _render_partial("head", options[:partials],
-        { grid: grid, options: options },)
-    end
-
-    def rows(grid, assets = grid.assets, **options, &block)
-      @template.safe_join(
-        assets.map do |asset|
-          row(grid, asset, **options, &block)
-        end.to_a,
-      )
-    end
-
-    def row(grid, asset, **options, &block)
-      Datagrid::Helper::HtmlRow.new(self, grid, asset, options).tap do |row|
-        return @template.capture(row, &block) if block_given?
-      end
-    end
-
-    def order_for(grid, column, options = {})
-      _render_partial("order_for", options[:partials],
-        { grid: grid, column: column },)
-    end
-
-    def order_path(grid, column, descending, request)
-      column = grid.column_by_name(column)
-      query = request&.query_parameters || {}
-      ActionDispatch::Http::URL.path_for(
-        path: request&.path || "/",
-        params: query.merge(grid.query_params(order: column.name, descending: descending)),
-      )
-    end
-
-    private
-
-    def _render_partial(partial_name, partials_path, locals = {})
-      @template.render({
-        partial: File.join(partials_path || "datagrid", partial_name),
-        locals: locals,
-      })
-    end
-  end
-
-  module Helper
-    # Represents a datagrid row that provides access to column values for the given asset
-    # @example
-    #   row = datagrid_row(grid, user)
-    #   row.class      # => Datagrid::Helper::HtmlRow
-    #   row.first_name # => "<strong>Bogdan</strong>"
-    #   row.grid       # => Grid object
-    #   row.asset      # => User object
-    #   row.each do |value|
-    #     puts value
-    #   end
-    class HtmlRow
-      include Enumerable
-
-      attr_reader :grid, :asset, :options
-
-      # @!visibility private
-      def initialize(renderer, grid, asset, options)
-        @renderer = renderer
-        @grid = grid
-        @asset = asset
-        @options = options
-      end
-
-      # @return [Object] a column value for given column name
-      def get(column)
-        @renderer.format_value(@grid, column, @asset)
-      end
-
-      # Iterates over all column values that are available in the row
-      # param block [Proc] column value iterator
-      def each(&block)
-        (@options[:columns] || @grid.html_columns).each do |column|
-          block.call(get(column))
-        end
-      end
-
-      def to_s
-        @renderer.send(:_render_partial, "row", options[:partials], {
-          grid: grid,
-          options: options,
-          asset: asset,
-        },)
-      end
-
-      protected
-
-      def method_missing(method, *args, &blk)
-        if (column = @grid.column_by_name(method))
-          get(column)
-        else
-          super
-        end
-      end
-
-      def respond_to_missing?(method, include_private = false)
-        !!@grid.column_by_name(method) || super
-      end
-    end
-  end
-end
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 37aaa34..6f88613 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -724,6 +724,21 @@ def param_name
         </tr>
        HTML
     end
+
+    it "supports deprecated options passing" do
+      grid = test_grid_column(:name)
+      silence_deprecator do
+        expect(
+          subject.datagrid_header(grid, {order: false})
+        ).to equal_to_dom(<<~HTML)
+        <tr>
+          <th data-column="name">
+            Name
+          </th>
+        </tr>
+        HTML
+      end
+    end
   end
 
   describe ".datagrid_column_classes" do
diff --git a/spec/support/test_partials/client/datagrid/_table.html.erb b/spec/support/test_partials/client/datagrid/_table.html.erb
index 84ff466..336ce88 100644
--- a/spec/support/test_partials/client/datagrid/_table.html.erb
+++ b/spec/support/test_partials/client/datagrid/_table.html.erb
@@ -7,7 +7,7 @@ Local variables:
 <p>Namespaced table partial.</p>
 <%= content_tag :table, options[:html] do %>
   <thead>
-    <%= datagrid_header(grid, options) %>
+    <%= datagrid_header(grid, **options) %>
   </thead>
   <tbody>
     <% if assets.empty? %>

From 6702193dad7d3e7b92c84c886efcfac2a23027c9 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 12:30:33 +0100
Subject: [PATCH 146/157] Cleanup

---
 app/views/datagrid/_head.html.erb | 4 ++--
 app/views/datagrid/_row.html.erb  | 4 ++--
 spec/datagrid/helper_spec.rb      | 4 +---
 3 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
index de2f969..2c59aa7 100644
--- a/app/views/datagrid/_head.html.erb
+++ b/app/views/datagrid/_head.html.erb
@@ -4,12 +4,12 @@
       # Consider maintaining consistency with datagrid/rows partial
       "data-column": column.name,
       **column.tag_options,
-      class: class_names(
+      class: [        
         column.tag_options[:class],
         # Adding HTML classes based on condition
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
         "datagrid-order-active-desc": grid.ordered_by?(column, true),
-      ).presence
+      ]
     ) do %>
       <%= column.header %>
       <% if column.supports_order? && options[:order] -%>
diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
index b493cfe..a2254b0 100644
--- a/app/views/datagrid/_row.html.erb
+++ b/app/views/datagrid/_row.html.erb
@@ -5,12 +5,12 @@
       # Consider maintaining consistency with datagrid/rows partial
       "data-column": column.name,
       **column.tag_options,
-      class: class_names(
+      class: [ 
         column.tag_options[:class],
         # Adding HTML classes based on condition
         "datagrid-order-active-asc": grid.ordered_by?(column, false),
         "datagrid-order-active-desc": grid.ordered_by?(column, true),
-      )
+      ]
     ) %>
   <% end %>
 </tr>
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 6f88613..429d56a 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -5,8 +5,6 @@
 require "active_support/core_ext/object"
 require "action_controller"
 
-require "datagrid/renderer"
-
 describe Datagrid::Helper do
   subject do
     action_view_template
@@ -732,7 +730,7 @@ def param_name
           subject.datagrid_header(grid, {order: false})
         ).to equal_to_dom(<<~HTML)
         <tr>
-          <th data-column="name">
+          <th data-column="name" class="">
             Name
           </th>
         </tr>

From b208fd78f657a76d75682688c698f213584497eb Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 13:05:15 +0100
Subject: [PATCH 147/157] Update views diff

---
 version-2/views.diff | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/version-2/views.diff b/version-2/views.diff
index 5d13642..b43cb66 100644
--- a/version-2/views.diff
+++ b/version-2/views.diff
@@ -40,7 +40,7 @@ index 7e175c1..fc4f4ae 100644
    </div>
  <% end -%>
 diff --git a/app/views/datagrid/_head.html.erb b/app/views/datagrid/_head.html.erb
-index e939128..ee5d89e 100644
+index e939128..2c59aa7 100644
 --- a/app/views/datagrid/_head.html.erb
 +++ b/app/views/datagrid/_head.html.erb
 @@ -1,8 +1,31 @@
@@ -51,12 +51,12 @@ index e939128..ee5d89e 100644
 +      # Consider maintaining consistency with datagrid/rows partial
 +      "data-column": column.name,
 +      **column.tag_options,
-+      class: class_names(
++      class: [        
 +        column.tag_options[:class],
 +        # Adding HTML classes based on condition
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
 +        "datagrid-order-active-desc": grid.ordered_by?(column, true),
-+      )
++      ]
 +    ) do %>
        <%= column.header %>
 -      <%= datagrid_order_for(grid, column, options) if column.supports_order? && options[:order]%>
@@ -92,7 +92,7 @@ index 7a8a123..faa2575 100644
 +<%# there is no duplicate id in DOM and click on label focuses the first input -%>
 +<%= form.datagrid_filter_input(filter, class: 'datagrid-range-to', **to_options, id: nil) %>
 diff --git a/app/views/datagrid/_row.html.erb b/app/views/datagrid/_row.html.erb
-index f54d21c..b493cfe 100644
+index f54d21c..a2254b0 100644
 --- a/app/views/datagrid/_row.html.erb
 +++ b/app/views/datagrid/_row.html.erb
 @@ -1,5 +1,16 @@
@@ -104,29 +104,30 @@ index f54d21c..b493cfe 100644
 +      # Consider maintaining consistency with datagrid/rows partial
 +      "data-column": column.name,
 +      **column.tag_options,
-+      class: class_names(
++      class: [ 
 +        column.tag_options[:class],
 +        # Adding HTML classes based on condition
 +        "datagrid-order-active-asc": grid.ordered_by?(column, false),
 +        "datagrid-order-active-desc": grid.ordered_by?(column, true),
-+      )
++      ]
 +    ) %>
    <% end %>
  </tr>
 diff --git a/app/views/datagrid/_table.html.erb b/app/views/datagrid/_table.html.erb
-index 8708c05..2e9273e 100644
+index 8708c05..88eeafb 100644
 --- a/app/views/datagrid/_table.html.erb
 +++ b/app/views/datagrid/_table.html.erb
-@@ -5,7 +5,7 @@ Local variables:
+@@ -5,18 +5,18 @@ Local variables:
  * options - passed options Hash
  %>
  <% if grid.html_columns(*options[:columns]).any? %>
 -  <%= content_tag :table, options[:html] do %>
 +  <%= tag.table class: 'datagrid-table', **options.fetch(:html, {}) do %>
      <thead>
-       <%= datagrid_header(grid, options) %>
+-      <%= datagrid_header(grid, options) %>
++      <%= datagrid_header(grid, **options) %>
      </thead>
-@@ -13,10 +13,10 @@ Local variables:
+     <tbody>
        <% if assets.any? %>
          <%= datagrid_rows(grid, assets, **options) %>
        <% else %>

From f110fd0ecd9dd0bf99768d434a181b8fe605bf11 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 14:02:58 +0100
Subject: [PATCH 148/157] Fix ci status icon

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index c5a0729..78e2b03 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ Datagrid Version 2.0.0 is here.
 
 [Migration Guide](./version-2).
 
-[![Build Status](https://github.com/bogdan/datagrid/workflows/CI/badge.svg?branch=master)](https://github.com/bogdan/datagrid/actions)
+[![Build Status](https://github.com/bogdan/datagrid/actions/workflows/ci.yml/badge.svg)](https://github.com/bogdan/datagrid/actions/workflows/ci.yml)
 
 A really mighty and flexible ruby library that generates reports
 including admin panels, analytics and data browsers:

From e9b1ba54e6032c32961d898cecc708a1484ae68e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 14:03:22 +0100
Subject: [PATCH 149/157] Fix changelog url

---
 datagrid.gemspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/datagrid.gemspec b/datagrid.gemspec
index e6904a7..f583ca4 100644
--- a/datagrid.gemspec
+++ b/datagrid.gemspec
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
     "homepage_uri" => s.homepage,
     "bug_tracker_uri" => "#{s.homepage}/issues",
     "documentation_uri" => "#{s.homepage}/wiki",
-    "changelog_uri" => "#{s.homepage}/blob/master/CHANGELOG.md",
+    "changelog_uri" => "#{s.homepage}/blob/main/CHANGELOG.md",
     "source_code_uri" => s.homepage,
     "rubygems_mfa_required" => "true",
   }

From 4ddd8585d6042d7c8d417fa481696afd96464d3b Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 18:30:53 +0100
Subject: [PATCH 150/157] Rubocop

---
 lib/datagrid/columns/column.rb                |  4 +--
 lib/datagrid/form_builder.rb                  |  1 +
 lib/datagrid/helper.rb                        |  2 ++
 .../datagrid/filters/date_time_filter_spec.rb |  2 +-
 spec/datagrid/filters/integer_filter_spec.rb  |  2 +-
 spec/datagrid/form_builder_spec.rb            |  2 +-
 spec/datagrid/helper_spec.rb                  | 26 +++++++++----------
 spec/spec_helper.rb                           |  2 +-
 8 files changed, 22 insertions(+), 19 deletions(-)

diff --git a/lib/datagrid/columns/column.rb b/lib/datagrid/columns/column.rb
index 65f4aad..f41d410 100644
--- a/lib/datagrid/columns/column.rb
+++ b/lib/datagrid/columns/column.rb
@@ -41,7 +41,7 @@ def initialize(grid_class, name, query, options = {}, &block)
         self.options = options
         if options[:class]
           Datagrid::Utils.warn_once(
-            "column[class] option is deprecated. Use {tag_options: {class: ...}} instead."
+            "column[class] option is deprecated. Use {tag_options: {class: ...}} instead.",
           )
           self.options[:tag_options] = {
             **self.options.fetch(:tag_options, {}),
@@ -123,7 +123,7 @@ def tag_options
 
       def html_class
         Datagrid::Utils.warn_once(
-          "Column#html_class is deprecated. Use Column#tag_options instead."
+          "Column#html_class is deprecated. Use Column#tag_options instead.",
         )
         options[:class]
       end
diff --git a/lib/datagrid/form_builder.rb b/lib/datagrid/form_builder.rb
index 1562298..078d739 100644
--- a/lib/datagrid/form_builder.rb
+++ b/lib/datagrid/form_builder.rb
@@ -39,6 +39,7 @@ def datagrid_label(filter_or_attribute, text = nil, **options, &block)
     #   * `type` - special attribute the determines an input tag to be made.
     #     Examples: `text`, `select`, `textarea`, `number`, `date` etc.
     # @return [String] an input tag for the corresponding filter name
+    # @param [Object] attribute_or_filter
     def datagrid_filter_input(attribute_or_filter, **options, &block)
       filter = datagrid_get_filter(attribute_or_filter)
       options = add_filter_options(filter, **options)
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index 7a8af0e..9cdb5f4 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -69,6 +69,7 @@ def datagrid_table(grid, assets = grid.assets, **options)
     # * <tt>:partials</tt> - Path for partials lookup.
     #   Default: 'datagrid'.
     # @param grid [Datagrid] grid object
+    # @param [Object] opts (deprecated) pass keyword arguments instead
     # @param [Hash] options
     # @return [String] HTML table header tag markup
     def datagrid_header(grid, opts = :__unspecified__, **options)
@@ -135,6 +136,7 @@ def datagrid_order_for(grid, column, options = {})
     # * <tt>:model</tt> - Datagrid object to be rendedred.
     # * All options supported by Rails <tt>form_with</tt> helper
     # @param grid [Datagrid] grid object
+    # @param [Hash{Symbol => Object}] options
     # @return [String] form HTML tag markup
     def datagrid_form_with(**options)
       raise ArgumentError, "datagrid_form_with block argument is invalid. Use form_with instead." if block_given?
diff --git a/spec/datagrid/filters/date_time_filter_spec.rb b/spec/datagrid/filters/date_time_filter_spec.rb
index 9b6f807..990c1be 100644
--- a/spec/datagrid/filters/date_time_filter_spec.rb
+++ b/spec/datagrid/filters/date_time_filter_spec.rb
@@ -168,7 +168,7 @@ def entry_dated(date)
   it "supports serialized range value" do
     from = Time.parse("2013-01-01 01:00")
     to = Time.parse("2013-01-02 02:00")
-    report  = test_grid_filter(:created_at, :datetime, range: true)
+    report = test_grid_filter(:created_at, :datetime, range: true)
 
     report.created_at = (from..to).as_json
     expect(report.created_at).to eq(from..to)
diff --git a/spec/datagrid/filters/integer_filter_spec.rb b/spec/datagrid/filters/integer_filter_spec.rb
index 4c310bb..c5b76fb 100644
--- a/spec/datagrid/filters/integer_filter_spec.rb
+++ b/spec/datagrid/filters/integer_filter_spec.rb
@@ -132,7 +132,7 @@
   end
 
   it "supports serialized range value" do
-    report  = test_grid_filter(:group_id, :integer, range: true)
+    report = test_grid_filter(:group_id, :integer, range: true)
 
     report.group_id = (1..5).as_json
     expect(report.group_id).to eq(1..5)
diff --git a/spec/datagrid/form_builder_spec.rb b/spec/datagrid/form_builder_spec.rb
index 4fbf5b5..cdccec7 100644
--- a/spec/datagrid/form_builder_spec.rb
+++ b/spec/datagrid/form_builder_spec.rb
@@ -102,7 +102,7 @@ class MyTemplate
       context "date filter type is text" do
         let(:_filter) { :created_at }
         let(:_grid) do
-          test_grid_filter(:created_at, :date, input_options: { type: 'text' })
+          test_grid_filter(:created_at, :date, input_options: { type: "text" })
         end
 
         it {
diff --git a/spec/datagrid/helper_spec.rb b/spec/datagrid/helper_spec.rb
index 8d0c4d5..d99a9e6 100644
--- a/spec/datagrid/helper_spec.rb
+++ b/spec/datagrid/helper_spec.rb
@@ -286,7 +286,7 @@
     it "should render argument-based html blocks with double arguments" do
       rp = test_grid_column(:name, html: lambda { |data, model|
         tag.h1 "#{data}-#{model.name.downcase}"
-      })
+      },)
       expect(subject.datagrid_rows(rp, [entry])).to match_css_pattern(
         "tr td[data-column=name] h1" => "Star-star",
       )
@@ -354,17 +354,17 @@
       report = test_grid(order: :name, descending: true) do
         scope { Entry }
         column(:name, tag_options: {
-          class: 'my-class',
+          class: "my-class",
           "data-column-group": "core",
           "data-column": nil,
-        })
+        },)
       end
 
       expect(subject.datagrid_rows(report, [entry])).to equal_to_dom(<<~HTML)
         <tr>
           <td class="my-class datagrid-order-active-desc" data-column-group="core">Star</td>
         </tr>
-       HTML
+      HTML
     end
 
     context "when grid has complicated columns" do
@@ -710,30 +710,30 @@ def param_name
       grid = test_grid(order: :name, descending: true) do
         scope { Entry }
         column(:name, order: false, tag_options: {
-          class: 'my-class',
+          class: "my-class",
           "data-column-group": "core",
           "data-column": nil,
-        })
+        },)
       end
 
       expect(subject.datagrid_header(grid)).to equal_to_dom(<<~HTML)
         <tr>
           <th class="my-class datagrid-order-active-desc" data-column-group="core">Name</th>
         </tr>
-       HTML
+      HTML
     end
 
     it "supports deprecated options passing" do
       grid = test_grid_column(:name)
       silence_deprecator do
         expect(
-          subject.datagrid_header(grid, {order: false})
+          subject.datagrid_header(grid, { order: false }),
         ).to equal_to_dom(<<~HTML)
-        <tr>
-          <th data-column="name" class="">
-            Name
-          </th>
-        </tr>
+          <tr>
+            <th data-column="name" class="">
+              Name
+            </th>
+          </tr>
         HTML
       end
     end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index cbcefb8..345c7c7 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -106,7 +106,7 @@ def silence_deprecator(&block)
   Datagrid::Utils.deprecator.silence(&block)
 end
 
-def expect_deprecated(message = /deprecated/, &block)
+def expect_deprecated(message = %r{deprecated}, &block)
   expect(Datagrid::Utils.deprecator).to receive(:warn).with(message)
   block.call
 end

From 1b1fc9c518bd246979d60f6c2d5bcfb1903e8ba9 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 20:38:40 +0100
Subject: [PATCH 151/157] Readme links to rdoc

---
 README.md | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 47a620b..8f5b067 100644
--- a/README.md
+++ b/README.md
@@ -33,9 +33,12 @@ including admin panels, analytics and data browsers:
 
 ## Documentation
 
-* Readme - this read-me for basic information.
-* [Wiki](https://github.com/bogdan/datagrid/wiki) - general reference on how to use the gem.
-* [Rdoc](https://rubydoc.info/gems/datagrid) - API reference.
+* [Rdoc](https://rubydoc.info/gems/datagrid) - full API reference
+* [Scope](https://rubydoc.info/gems/datagrid/Datagrid/Core) - working with datagrid scope
+* [Columns](https://rubydoc.info/gems/datagrid/Datagrid/Columns) - definging datagrid columns
+* [Filters](https://rubydoc.info/gems/datagrid/Datagrid/Filters) - defining datagrid filters
+* [Frontend](https://rubydoc.info/gems/datagrid/Datagrid/Helper) - building a frontend 
+* [Configuration](https://rubydoc.info/gems/datagrid/Datagrid/Configuration) - configuring the gem
 
 ### Live Demo
 
@@ -122,7 +125,7 @@ scope do
 end
 ```
 
-[More about scope](https://github.com/bogdan/datagrid/wiki/Scope)
+[More about scope](https://rubydoc.info/gems/datagrid/Datagrid/Core)
 
 ### Filters
 
@@ -146,7 +149,7 @@ Datagrid supports different type of filters including:
 * string
 * dynamic - build dynamic SQL condition
 
-[More about filters](https://github.com/bogdan/datagrid/wiki/Filters)
+[More about filters](https://rubydoc.info/gems/datagrid/Datagrid/Filters)
 
 ### Columns
 
@@ -195,7 +198,7 @@ rails g datagrid::views
 
 All advanced frontend things are described in:
 
-[Frontend section on wiki](https://github.com/bogdan/datagrid/wiki/Frontend)
+[Frontend section on wiki](https://rubydoc.info/gems/datagrid/Datagrid/Helper)
 
 ## Questions & Issues
 

From a26aa1a31060002864fe440672b1da9b31575c8a Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Mon, 18 Nov 2024 20:53:02 +0100
Subject: [PATCH 152/157] Update doc according to v2

---
 lib/datagrid/columns.rb |  4 +---
 lib/datagrid/filters.rb | 12 +++++-------
 lib/datagrid/helper.rb  | 36 +++++++++++++++---------------------
 3 files changed, 21 insertions(+), 31 deletions(-)

diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index bf3c4c8..87a3599 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -6,9 +6,7 @@
 module Datagrid
   # Defines a column to be used for displaying data in a Datagrid.
   #
-  #     class UserGrid
-  #       include Datagrid
-  #
+  #     class UserGrid < ApplicationGrid
   #       scope do
   #         User.order("users.created_at desc").joins(:group)
   #       end
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 67cdec6..f13540f 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -6,9 +6,7 @@ module Datagrid
   # Defines the accessible attribute that is used to filter
   # the scope by the specified value with specified code.
   #
-  #     class UserGrid
-  #       include Datagrid
-  #
+  #     class UserGrid < ApplicationGrid
   #       scope do
   #         User
   #       end
@@ -64,13 +62,13 @@ module Datagrid
   #
   # `:date` - Converts value to a date. Supports the `:range` option to accept date ranges.
   #
-  #     filter(:created_at, :date, range: true, default: proc { [1.month.ago.to_date, Date.today] })
+  #     filter(:created_at, :date, range: true, default: proc { 1.month.ago.to_date..Date.today })
   #
   # == Datetime
   #
   # `:datetime` - Converts value to a timestamp. Supports the `:range` option to accept time ranges.
   #
-  #     filter(:created_at, :datetime, range: true, default: proc { [1.hour.ago, Time.now] })
+  #     filter(:created_at, :datetime, range: true, default: proc { 1.hour.ago..Time.now })
   #
   # == Enum
   #
@@ -93,7 +91,7 @@ module Datagrid
   #
   # `:integer` - Converts value to an integer. Supports the `:range` option.
   #
-  #     filter(:posts_count, :integer, range: true, default: [1, nil])
+  #     filter(:posts_count, :integer, range: true, default: (1..nil))
   #
   # == String
   #
@@ -123,7 +121,7 @@ module Datagrid
   # Example:
   #
   #     filter(:id, :integer, header: "Identifier")
-  #     filter(:created_at, :date, range: true, default: proc { [1.month.ago.to_date, Date.today] })
+  #     filter(:created_at, :date, range: true, default: proc { 1.month.ago.to_date..Date.today })
   #
   # = Localization
   #
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index e5cd312..a298b0f 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -55,15 +55,15 @@ module Datagrid
   #
   # Use the built-in partial:
   #
-  #     = datagrid_form_for @grid, url: report_path, other_form_for_option: value
+  #     = datagrid_form_with model: @grid, url: report_path, other_form_for_option: value
   #
-  # {#datagrid_form_for} supports the same options as Rails `form_for`.
+  # {#datagrid_form_with} supports the same options as Rails `form_with`.
   #
   # === Advanced Method
   #
   # You can use Rails built-in tools to create a form. Additionally, Datagrid provides helpers to generate input/select elements for filters:
   #
-  #     - form_for UserGrid.new, method: :get, url: users_path do |f|
+  #     - form_with model: UserGrid.new, method: :get, url: users_path do |f|
   #       %div
   #         = f.datagrid_label :name
   #         = f.datagrid_filter :name # => <input name="grid[name]" type="text"/>
@@ -73,7 +73,7 @@ module Datagrid
   #
   # To create a report form:
   #
-  #     - form_for @report, method: :get, url: users_path do |f|
+  #     - form_with model: @report, method: :get, url: users_path do |f|
   #       - @report.filters.each do |filter|
   #         %div
   #           = f.datagrid_label filter
@@ -156,7 +156,7 @@ module Datagrid
   #
   # Modify the form for AJAX:
   #
-  #     = datagrid_form_for @grid, html: {class: 'js-datagrid-form'}
+  #     = datagrid_form_with model: @grid, html: {class: 'js-datagrid-form'}
   #     .js-datagrid-table
   #       = datagrid_table @grid
   #     .js-pagination
@@ -179,9 +179,8 @@ module Datagrid
   #
   #     app/views/datagrid/
   #     ├── _enum_checkboxes.html.erb # datagrid_filter for filter(name, :enum, checkboxes: true)
-  #     ├── _form.html.erb            # datagrid_form_for
+  #     ├── _form.html.erb            # datagrid_form_with
   #     ├── _head.html.erb            # datagrid_header
-  #     ├── _order_for.html.erb       # datagrid_order_for
   #     ├── _range_filter.html.erb    # datagrid_filter for filter(name, type, range: true)
   #     ├── _row.html.erb             # datagrid_rows/datagrid_rows
   #     └── _table.html.erb           # datagrid_table
@@ -198,18 +197,14 @@ module Datagrid
   #       category.orders.sum(:subtotal) / category.orders.count
   #     end
   #
-  # The `:description` option is not built into Datagrid, but you can implement it by modifying the column header
-  # partial `app/views/datagrid/_header.html.erb` like this:
+  # The `:description` option is not built into Datagrid, but you can implement it
+  # by adding the following to partial `app/views/datagrid/_header.html.erb`:
   #
-  #     %tr
-  #       - grid.html_columns(*options[:columns]).each do |column|
-  #         %th{class: datagrid_column_classes(grid, column)}
-  #           = column.header
-  #           - if column.options[:description]
-  #             %a{data: {toggle: 'tooltip', title: column.options[:description]}}
-  #               %i.icon-question-sign
-  #           - if column.order && options[:order]
-  #             = datagrid_order_for(grid, column, options)
+  #     <% if column.options[:description] %>
+  #       <a data-toggle="tooltip" title="<%= column.options[:description] %>">
+  #         <i class="icon-question-sign"></i>
+  #       </a>
+  #     <% end %>
   #
   # This modification allows the `:description` tooltip to work with your chosen UI and JavaScript library.
   # The same technique can be applied to filters by calling `filter.options` in corresponding partials.
@@ -228,8 +223,7 @@ module Datagrid
   #
   # This allows you to define a custom `row_class` method in your grid class, like this:
   #
-  #     class IssuesGrid
-  #       include Datagrid
+  #     class IssuesGrid < ApplicationGrid
   #       scope { Issue }
   #
   #       def row_class(issue)
@@ -396,7 +390,7 @@ def datagrid_form_with(**options)
     #
     # * <tt>:partials</tt> - Path for form partial lookup.
     #   Default: 'datagrid'.
-    # * All options supported by Rails <tt>form_for</tt> helper
+    # * All options supported by Rails <tt>form_with</tt> helper
     # @deprecated Use {#datagrid_form_with} instead.
     # @param grid [Datagrid] grid object
     # @param [Hash] options

From 08e236955f0ae6b024b1e811072d279f163f381e Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 19 Nov 2024 10:33:22 +0100
Subject: [PATCH 153/157] v2.0.0-alpha

---
 lib/datagrid/version.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/datagrid/version.rb b/lib/datagrid/version.rb
index 9b62e71..403ac57 100644
--- a/lib/datagrid/version.rb
+++ b/lib/datagrid/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 
 module Datagrid
-  VERSION = "2.0.0"
+  VERSION = "2.0.0-alpha"
 end

From 42aabb1e79fa21c3fb73e4367c1caef7276dfefe Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 19 Nov 2024 10:38:41 +0100
Subject: [PATCH 154/157] Add yardopts to gemspec

---
 datagrid.gemspec | 1 +
 1 file changed, 1 insertion(+)

diff --git a/datagrid.gemspec b/datagrid.gemspec
index f1aac51..be63ba3 100644
--- a/datagrid.gemspec
+++ b/datagrid.gemspec
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
     "CHANGELOG.md",
     "README.md",
     "datagrid.gemspec",
+    ".yardopts",
   ]
   s.files += `git ls-files | grep -E '^(app|lib|templates)'`.split("\n")
   s.homepage = "https://github.com/bogdan/datagrid"

From dff4a5f9273f8aa84209b84a13fddf82cc142658 Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 19 Nov 2024 19:29:17 +0100
Subject: [PATCH 155/157] Improve doc

---
 README.md                            |  2 --
 lib/datagrid/columns.rb              | 21 +++++++++++-------
 lib/datagrid/filters.rb              |  1 -
 lib/datagrid/filters/float_filter.rb |  1 -
 lib/datagrid/helper.rb               | 33 +++++++++++++++-------------
 5 files changed, 31 insertions(+), 27 deletions(-)

diff --git a/README.md b/README.md
index 8f5b067..24fa669 100644
--- a/README.md
+++ b/README.md
@@ -23,8 +23,6 @@ including admin panels, analytics and data browsers:
 * Sequel
 * Array (in-memory data of smaller scale)
 
-[Create an issue](https://github.com/bogdan/datagrid/issues/new) if you want more.
-
 ## Datagrid Philosophy
 
 1. Expressive DSL complements OOD instead of replacing it.
diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index f18d55d..8570396 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -403,14 +403,14 @@ def assets
       )
     end
 
-    # @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String, Symbol>] list of column names if you want to limit data only to specified columns
     # @return [Array<String>] human readable column names. See also "Localization" section
     def header(*column_names)
       data_columns(*column_names).map(&:header)
     end
 
     # @param asset [Object] asset from datagrid scope
-    # @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String, Symbol>] list of column names if you want to limit data only to specified columns
     # @return [Array<Object>] column values for given asset
     def row_for(asset, *column_names)
       data_columns(*column_names).map do |column|
@@ -428,7 +428,7 @@ def hash_for(asset)
       result
     end
 
-    # @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String,Symbol>] list of column names if you want to limit data only to specified columns
     # @return [Array<Array<Object>>] with data for each row in datagrid assets without header
     def rows(*column_names)
       map_with_batches do |asset|
@@ -436,7 +436,7 @@ def rows(*column_names)
       end
     end
 
-    # @param column_names [Array<String>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String, Symbol>] list of column names if you want to limit data only to specified columns
     # @return [Array<Array<Object>>] data for each row in datagrid assets with header.
     def data(*column_names)
       rows(*column_names).unshift(header(*column_names))
@@ -461,7 +461,7 @@ def data_hash
       end
     end
 
-    # @param column_names [Array<String>]
+    # @param column_names [Array<String,Symbol>]
     # @param options [Hash] CSV generation options
     # @return [String] a CSV representation of the data in the grid
     #
@@ -537,7 +537,7 @@ def column_by_name(name)
     #   end
     # @return [Datagrid::Columns::Column::ResponseFormat] Format object
     def format(value, &block)
-      if block_given?
+      if block
         self.class.format(value, &block)
       else
         # don't override Object#format method
@@ -545,6 +545,7 @@ def format(value, &block)
       end
     end
 
+    # @param [Object] asset one of the assets from grid scope
     # @return [Datagrid::Columns::DataRow] an object representing a grid row.
     # @example
     #  class MyGrid
@@ -564,7 +565,6 @@ def data_row(asset)
     end
 
     # Defines a column at instance level
-    #
     # @see Datagrid::Columns::ClassMethods#column
     def column(name, query = nil, **options, &block)
       self.class.define_column(columns_array, name, query, **options, &block)
@@ -578,7 +578,6 @@ def initialize(*)
 
     # @return [Array<Datagrid::Columns::Column>] all columns
     #   that are possible to be displayed for the current grid object
-    #
     # @example
     #   class MyGrid
     #     filter(:search) {|scope, value| scope.full_text_search(value)}
@@ -600,6 +599,8 @@ def available_columns
       end
     end
 
+    # @param [String,Symbol] column_name column name
+    # @param [Object] asset one of the assets from grid scope
     # @return [Object] a cell data value for given column name and asset
     def data_value(column_name, asset)
       column = column_by_name(column_name)
@@ -611,6 +612,9 @@ def data_value(column_name, asset)
       end
     end
 
+    # @param [String,Symbol] column_name column name
+    # @param [Object] asset one of the assets from grid scope
+    # @param [ActionView::Base] context view context object
     # @return [Object] a cell HTML value for given column name and asset and view context
     def html_value(column_name, context, asset)
       column = column_by_name(column_name)
@@ -624,6 +628,7 @@ def html_value(column_name, context, asset)
       end
     end
 
+    # @param [Object] model one of the assets from grid scope
     # @return [Object] a decorated version of given model if decorator is specified or the model otherwise.
     def decorate(model)
       self.class.decorate(model)
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 8e5a2cb..6856b77 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -161,7 +161,6 @@ module Filters
 
     extend ActiveSupport::Concern
 
-    # @!visibility private
     included do
       include Datagrid::Core
       class_attribute :filters_array, default: []
diff --git a/lib/datagrid/filters/float_filter.rb b/lib/datagrid/filters/float_filter.rb
index 2c9b164..d6b586c 100644
--- a/lib/datagrid/filters/float_filter.rb
+++ b/lib/datagrid/filters/float_filter.rb
@@ -1,6 +1,5 @@
 # frozen_string_literal: true
 
-# @!visibility private
 module Datagrid
   module Filters
     class FloatFilter < Datagrid::Filters::BaseFilter
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index 3bf4e75..dca8579 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -240,7 +240,7 @@ module Datagrid
   #
   # https://github.com/bogdan/datagrid/blob/master/lib/datagrid/locale/en.yml
   module Helper
-    # @param grid [Datagrid] grid object
+    # @param grid [Datagrid::Base] grid object
     # @param column [Datagrid::Columns::Column, String, Symbol] column name
     # @param model [Object] an object from grid scope
     # @return [Object] individual cell value from the given grid, column name and model
@@ -264,7 +264,7 @@ def datagrid_format_value(grid, column, model)
     # Renders html table with columns defined in grid class.
     # In the most common used you need to pass paginated collection
     # to datagrid table because datagrid do not have pagination compatibilities:
-    # @param grid [Datagrid] grid object
+    # @param grid [Datagrid::Base] grid object
     # @param assets [Array] objects from grid scope
     # @param [Hash{Symbol => Object}] options HTML attributes to be passed to `<table>` tag
     # @option options [Hash] html A hash of attributes for the `<table>` tag.
@@ -298,7 +298,7 @@ def datagrid_table(grid, assets = grid.assets, **options)
     #   Default: all defined columns.
     # @option options [String] partials The path for partials lookup.
     #   Default: `'datagrid'`.
-    # @param grid [Datagrid] grid object
+    # @param grid [Datagrid::Base] grid object
     # @param [Object] opts (deprecated) pass keyword arguments instead
     # @param [Hash] options
     # @return [String] HTML table header tag markup
@@ -329,6 +329,8 @@ def datagrid_header(grid, opts = :__unspecified__, **options)
     #     %tr
     #       %td= row.project_name
     #       %td.project-status{class: row.status}= row.status
+    # @param [Datagrid::Base] grid datagrid object
+    # @param [Array<Object>] assets assets as per defined in grid scope
     def datagrid_rows(grid, assets = grid.assets, **options, &block)
       safe_join(
         assets.map do |asset|
@@ -340,6 +342,10 @@ def datagrid_rows(grid, assets = grid.assets, **options, &block)
     # @return [String] renders ordering controls for the given column name
     # @option options [String] partials The path for partials lookup.
     #   Default: `'datagrid'`.
+    # @param [Datagrid::Base] grid datagrid object
+    # @param [Datagrid::Columns::Column] column
+    # @deprecated Put necessary code inline inside datagrid/head partial.
+    #   See built-in partial for example.
     def datagrid_order_for(grid, column, options = {})
       Datagrid::Utils.warn_once(<<~MSG)
         datagrid_order_for is deprecated.
@@ -351,15 +357,12 @@ def datagrid_order_for(grid, column, options = {})
     end
 
     # Renders HTML for grid with all filters inputs and labels defined in it
-    #
-    # Supported options:
-    #
-    # * <tt>:partials</tt> - Path for form partial lookup.
-    #   Default: 'datagrid' results in using `app/views/datagrid/` partials.
-    #   Example: 'datagrid_admin' results in using `app/views/datagrid_admin` partials.
-    # * <tt>:model</tt> - Datagrid object to be rendedred.
-    # * All options supported by Rails <tt>form_with</tt> helper
-    # @param grid [Datagrid] grid object
+    # @option options [String] partials Path for form partial lookup.
+    #   Default: `'datagrid'`, which uses `app/views/datagrid/` partials.
+    #   Example: `'datagrid_admin'` uses `app/views/datagrid_admin` partials.
+    # @option options [Object] model The Datagrid object to be rendered.
+    # @option options [Hash] All options supported by Rails `form_with` helper.
+    # @param grid [Datagrid::Base] grid object
     # @param [Hash{Symbol => Object}] options
     # @return [String] form HTML tag markup
     def datagrid_form_with(**options)
@@ -379,7 +382,7 @@ def datagrid_form_with(**options)
     #   Default: 'datagrid'.
     # * All options supported by Rails <tt>form_with</tt> helper
     # @deprecated Use {#datagrid_form_with} instead.
-    # @param grid [Datagrid] grid object
+    # @param grid [Datagrid::Base] grid object
     # @param [Hash] options
     # @return [String] form HTML tag markup
     def datagrid_form_for(grid, options = {})
@@ -398,7 +401,7 @@ def datagrid_form_for(grid, options = {})
 
     # Provides access to datagrid columns data.
     # Used in case you want to build html table completelly manually
-    # @param grid [Datagrid] grid object
+    # @param grid [Datagrid::Base] grid object
     # @param asset [Object] object from grid scope
     # @param block [Proc] block with Datagrid::Helper::HtmlRow as an argument returning a HTML markup as a String
     # @param [Hash{Symbol => Object}] options
@@ -423,7 +426,7 @@ def datagrid_row(grid, asset, **options, &block)
     end
 
     # Generates an ascending or descending order url for the given column
-    # @param grid [Datagrid] grid object
+    # @param grid [Datagrid::Base] grid object
     # @param column [Datagrid::Columns::Column, String, Symbol] column name
     # @param descending [Boolean] order direction, descending if true, otherwise ascending.
     # @return [String] order layout HTML markup

From 3558341687f4648f866cab87c8a931130e78dc7d Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 19 Nov 2024 20:11:04 +0100
Subject: [PATCH 156/157] Improve docs

---
 README.md                     |   2 +-
 datagrid.gemspec              |   2 +-
 lib/datagrid/columns.rb       | 128 ++++++++++++++++------------------
 lib/datagrid/configuration.rb |   6 +-
 lib/datagrid/filters.rb       |   4 +-
 lib/datagrid/helper.rb        |  11 +--
 version-2/Readme.markdown     |   8 +--
 7 files changed, 79 insertions(+), 82 deletions(-)

diff --git a/README.md b/README.md
index 24fa669..19d8fd2 100644
--- a/README.md
+++ b/README.md
@@ -162,7 +162,7 @@ end
 Some formatting options are also available.
 Each column is sortable.
 
-[More about columns](https://github.com/bogdan/datagrid/wiki/Columns)
+[More about columns](https://rubydoc.info/gems/datagrid/Datagrid/Columns)
 
 ### Front end
 
diff --git a/datagrid.gemspec b/datagrid.gemspec
index be63ba3..419c4d1 100644
--- a/datagrid.gemspec
+++ b/datagrid.gemspec
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
   s.metadata = {
     "homepage_uri" => s.homepage,
     "bug_tracker_uri" => "#{s.homepage}/issues",
-    "documentation_uri" => "#{s.homepage}/wiki",
+    "documentation_uri" => "https://rubydoc.info/gems/datagrid",
     "changelog_uri" => "#{s.homepage}/blob/main/CHANGELOG.md",
     "source_code_uri" => s.homepage,
     "rubygems_mfa_required" => "true",
diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index 8570396..795f1e7 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -2,6 +2,7 @@
 
 require "datagrid/utils"
 require "active_support/core_ext/class/attribute"
+require "datagrid/columns/column"
 
 module Datagrid
   # Defines a column to be used for displaying data in a Datagrid.
@@ -199,29 +200,26 @@ module Datagrid
   #       presenter.user.created_at
   #     end
   module Columns
-    require "datagrid/columns/column"
-
-    # @!method default_column_options=(value)
-    # @param [Hash] value default options passed to #column method call
-    # @return [Hash] default options passed to #column method call
-    # @example
-    #   # Disable default order
+    # @param [Hash] value default options passed to {#column} method call
+    # @return [Hash] default options passed to {#column} method call
+    # @example Disable default order
     #   self.default_column_options = { order: false }
-    #   # Makes entire report HTML
+    # @example Makes entire report HTML
     #   self.default_column_options = { html: true }
+    # @!method default_column_options=(value)
 
-    # @!method default_column_options
-    # @return [Hash]
+    # @return [Hash] default options passed to {#column} method call
     # @see #default_column_options=
+    # @!method default_column_options
 
     # @!method batch_size=(value)
-    # @param [Integer] value Specify a default batch size when generating CSV or just data. Default: 1000
-    # @return [Integer] Specify a default batch size when generating CSV or just data.
-    # @example
+    # Specify a default batch size when generating CSV or just data.
+    # @param [Integer] value a batch size when generating CSV or just data. Default: 1000
+    # @return [void]
+    # @example Set batch size to 500
     #   self.batch_size = 500
-    #   # Disable batches
+    # @example Disable batches
     #   self.batch_size = nil
-    #
 
     # @!method batch_size
     # @return [Integer]
@@ -253,37 +251,31 @@ def columns(*column_names, data: false, html: false)
         filter_columns(columns_array, *column_names, data: data, html: html)
       end
 
-      # Defines new datagrid column
-      #
+      # Defines a new datagrid column
       # @param name [Symbol] column name
       # @param query [String, nil] a string representing the query to select this column (supports only ActiveRecord)
-      # @param options [Hash{Symbol => Object}] hash of options
       # @param block [Block] proc to calculate a column value
+      # @option options [Boolean, String] html Determines if the column should be present
+      #   in the HTML table and how it is formatted.
+      # @option options [String, Array<Symbol>] order Determines if the column can be sortable and
+      #   specifies the ORM ordering method.
+      #   Example: `"created_at, id"` for ActiveRecord, `[:created_at, :id]` for Mongoid.
+      # @option options [String] order_desc Specifies a descending order for the column
+      #   (used when `:order` cannot be easily reversed by the ORM).
+      # @option options [Boolean, Proc] order_by_value Enables Ruby-level ordering for the column.
+      #   Warning: Sorting large datasets in Ruby is not recommended.
+      #   If `true`, Datagrid orders by the column value.
+      #   If a block is provided, Datagrid orders by the block's return value.
+      # @option options [Boolean] mandatory If `true`, the column will never be hidden by the `#column_names` selection.
+      # @option options [Symbol] before Places the column before the specified column when determining order.
+      # @option options [Symbol] after Places the column after the specified column when determining order.
+      # @option options [Boolean, Proc] if conditions when a column is available.
+      # @option options [Boolean, Proc] unless conditions when a column is not available.
+      # @option options [Symbol, Array<Symbol>] preload Specifies associations
+      #   to preload for the column within the scope.
+      # @option options [Hash] tag_options Specifies HTML attributes for the `<td>` or `<th>` of the column.
+      #   Example: `{ class: "content-align-right", "data-group": "statistics" }`.
       # @return [Datagrid::Columns::Column]
-      #
-      # Available options:
-      #
-      # * <tt>html</tt> - determines if current column should be present in html table and how is it formatted
-      # * <tt>order</tt> - determines if this column could be sortable and how.
-      #   The value of order is explicitly passed to ORM ordering method.
-      #   Example: <tt>"created_at, id"</tt> for ActiveRecord, <tt>[:created_at, :id]</tt> for Mongoid
-      # * <tt>order_desc</tt> - determines a descending order for given column
-      #   (only in case when <tt>:order</tt> can not be easily reversed by ORM)
-      # * <tt>order_by_value</tt> - used in case it is easier to perform ordering at ruby level not on database level.
-      #   Warning: using ruby to order large datasets is very unrecommended.
-      #   If set to true - datagrid will use column value to order by this column
-      #   If block is given - datagrid will use value returned from block
-      # * <tt>mandatory</tt> - if true, column will never be hidden with #column_names selection
-      # * <tt>url</tt> - a proc with one argument, pass this option to easily convert the value into an URL
-      # * <tt>before</tt> - determines the position of this column, by adding it before the column passed here
-      # * <tt>after</tt> - determines the position of this column, by adding it after the column passed here
-      # * <tt>if</tt> - the column is shown if the reult of calling this argument is true
-      # * <tt>unless</tt> - the column is shown unless the reult of calling this argument is true
-      # * <tt>preload</tt> - spefies which associations of the scope should be preloaded for this column
-      # * `tag_options` - specify HTML attributes to be set for `<td>` or `<th>` of a column
-      #   Example: `{ class: "content-align-right", "data-group": "statistics" }`
-      #
-      # @see https://github.com/bogdan/datagrid/wiki/Columns
       def column(name, query = nil, **options, &block)
         define_column(columns_array, name, query, **options, &block)
       end
@@ -333,10 +325,10 @@ def format(value, &block)
       # Defines a model decorator that will be used to define a column value.
       # All column blocks will be given a decorated version of the model.
       # @return [void]
-      # @example
+      # @example Wrapping a model with presenter
       #   decorate { |user| UserPresenter.new(user) }
-      #
-      #   decorate { UserPresenter } # a shortcut
+      # @example A shortcut for doing the same
+      #   decorate { UserPresenter }
       def decorate(model = nil, &block)
         if !model && !block
           raise ArgumentError, "decorate needs either a block to define decoration or a model to decorate"
@@ -403,14 +395,16 @@ def assets
       )
     end
 
-    # @param column_names [Array<String, Symbol>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String, Symbol>] list of column names
+    #   if you want to limit data only to specified columns
     # @return [Array<String>] human readable column names. See also "Localization" section
     def header(*column_names)
       data_columns(*column_names).map(&:header)
     end
 
     # @param asset [Object] asset from datagrid scope
-    # @param column_names [Array<String, Symbol>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String, Symbol>] list of column names
+    #   if you want to limit data only to specified columns
     # @return [Array<Object>] column values for given asset
     def row_for(asset, *column_names)
       data_columns(*column_names).map do |column|
@@ -419,7 +413,8 @@ def row_for(asset, *column_names)
     end
 
     # @param asset [Object] asset from datagrid scope
-    # @return [Hash] A mapping where keys are column names and values are column values for the given asset
+    # @return [Hash] A mapping where keys are column names and
+    #   values are column values for the given asset
     def hash_for(asset)
       result = {}
       data_columns.each do |column|
@@ -428,7 +423,8 @@ def hash_for(asset)
       result
     end
 
-    # @param column_names [Array<String,Symbol>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String,Symbol>] list of column names
+    #   if you want to limit data only to specified columns
     # @return [Array<Array<Object>>] with data for each row in datagrid assets without header
     def rows(*column_names)
       map_with_batches do |asset|
@@ -436,7 +432,8 @@ def rows(*column_names)
       end
     end
 
-    # @param column_names [Array<String, Symbol>] list of column names if you want to limit data only to specified columns
+    # @param column_names [Array<String, Symbol>] list of column names
+    #   if you want to limit data only to specified columns.
     # @return [Array<Array<Object>>] data for each row in datagrid assets with header.
     def data(*column_names)
       rows(*column_names).unshift(header(*column_names))
@@ -514,7 +511,7 @@ def html_columns(*column_names, data: false)
       columns(*column_names, data: data, html: true)
     end
 
-    # Finds a column definition by name
+    # Finds a column by name
     # @param name [String, Symbol] column name to be found
     # @return [Datagrid::Columns::Column, nil]
     def column_by_name(name)
@@ -522,17 +519,16 @@ def column_by_name(name)
     end
 
     # Gives ability to have a different formatting for CSV and HTML column value.
-    #
-    # @example
+    # @example Formating using Rails view helpers
     #   column(:name) do |model|
     #     format(model.name) do |value|
     #       tag.strong(value)
     #     end
     #   end
-    #
+    # @example Formatting using a separated view template
     #   column(:company) do |model|
     #     format(model.company.name) do
-    #       render partial: "company_with_logo", locals: {company: model.company }
+    #       render partial: "companies/company_with_logo", locals: {company: model.company }
     #     end
     #   end
     # @return [Datagrid::Columns::Column::ResponseFormat] Format object
@@ -548,18 +544,18 @@ def format(value, &block)
     # @param [Object] asset one of the assets from grid scope
     # @return [Datagrid::Columns::DataRow] an object representing a grid row.
     # @example
-    #  class MyGrid
-    #    scope { User }
-    #    column(:id)
-    #    column(:name)
-    #    column(:number_of_purchases) do |user|
-    #      user.purchases.count
-    #    end
-    #  end
+    #   class MyGrid
+    #     scope { User }
+    #     column(:id)
+    #     column(:name)
+    #     column(:number_of_purchases) do |user|
+    #       user.purchases.count
+    #     end
+    #   end
     #
-    #  row = MyGrid.new.data_row(User.last)
-    #  row.id # => user.id
-    #  row.number_of_purchases # => user.purchases.count
+    #   row = MyGrid.new.data_row(User.last)
+    #   row.id # => user.id
+    #   row.number_of_purchases # => user.purchases.count
     def data_row(asset)
       ::Datagrid::Columns::DataRow.new(self, asset)
     end
diff --git a/lib/datagrid/configuration.rb b/lib/datagrid/configuration.rb
index 5c64e3e..a80e8b2 100644
--- a/lib/datagrid/configuration.rb
+++ b/lib/datagrid/configuration.rb
@@ -25,12 +25,14 @@ def self.configure(&block)
   # Datagrid.configure do |config|
   #   # Defines date formats that can be used to parse dates.
   #   # Note: Multiple formats can be specified. The first format is used to format dates as strings,
-  #   # while other formats are used only for parsing dates from strings (e.g., if your app supports multiple formats).
+  #   # while other formats are used only for parsing dates
+  #   # from strings (e.g., if your app supports multiple formats).
   #   config.date_formats = "%m/%d/%Y", "%Y-%m-%d"
   #
   #   # Defines timestamp formats that can be used to parse timestamps.
   #   # Note: Multiple formats can be specified. The first format is used to format timestamps as strings,
-  #   # while other formats are used only for parsing timestamps from strings (e.g., if your app supports multiple formats).
+  #   # while other formats are used only for parsing timestamps
+  #   # from strings (e.g., if your app supports multiple formats).
   #   config.datetime_formats = ["%m/%d/%Y %h:%M", "%Y-%m-%d %h:%M:%s"]
   # end
   # ```
diff --git a/lib/datagrid/filters.rb b/lib/datagrid/filters.rb
index 6856b77..19637c8 100644
--- a/lib/datagrid/filters.rb
+++ b/lib/datagrid/filters.rb
@@ -204,8 +204,8 @@ def filter_by_name(attribute)
       #   Used with the `datagrid_form_for` helper.
       # @option options [Symbol] after Specifies the position of this filter by placing it after another filter.
       #   Used with the `datagrid_form_for` helper.
-      # @option options [Boolean] dummy If true, the filter is not applied automatically and is only displayed in the form.
-      #   Useful for manual application.
+      # @option options [Boolean] dummy If true, the filter is not applied automatically and
+      #   is only displayed in the form. Useful for manual application.
       # @option options [Proc, Symbol] if Specifies a condition under which the filter is displayed and used.
       #   Accepts a block or the name of an instance method.
       # @option options [Proc, Symbol] unless Specifies a condition under which the filter is NOT displayed or used.
diff --git a/lib/datagrid/helper.rb b/lib/datagrid/helper.rb
index dca8579..5a1bc37 100644
--- a/lib/datagrid/helper.rb
+++ b/lib/datagrid/helper.rb
@@ -17,7 +17,8 @@ module Datagrid
   # [built-in CSS](https://github.com/bogdan/datagrid/blob/master/app/assets/stylesheets/datagrid.sass).
   #
   # Datagrid includes helpers and a form builder for easy frontend generation.
-  # If you need a fully-featured custom GUI, create your templates manually with the help of the {Datagrid::Columns} API.
+  # If you need a fully-featured custom GUI, create your templates manually
+  # with the help of the {Datagrid::Columns} API.
   #
   # ## Controller and Routing
   #
@@ -63,7 +64,8 @@ module Datagrid
   #
   # ### Advanced Method
   #
-  # You can use Rails built-in tools to create a form. Additionally, Datagrid provides helpers to generate input/select elements for filters:
+  # You can use Rails built-in tools to create a form.
+  # Additionally, Datagrid provides helpers to generate input/select elements for filters:
   #
   # ``` haml
   # - form_with model: UserGrid.new, method: :get, url: users_path do |f|
@@ -360,9 +362,8 @@ def datagrid_order_for(grid, column, options = {})
     # @option options [String] partials Path for form partial lookup.
     #   Default: `'datagrid'`, which uses `app/views/datagrid/` partials.
     #   Example: `'datagrid_admin'` uses `app/views/datagrid_admin` partials.
-    # @option options [Object] model The Datagrid object to be rendered.
+    # @option options [Datagrid::Base] model a Datagrid object to be rendered.
     # @option options [Hash] All options supported by Rails `form_with` helper.
-    # @param grid [Datagrid::Base] grid object
     # @param [Hash{Symbol => Object}] options
     # @return [String] form HTML tag markup
     def datagrid_form_with(**options)
@@ -466,7 +467,7 @@ def _render_partial(partial_name, partials_path, locals = {})
     #   row = datagrid_row(grid, user)
     #   row.class      # => Datagrid::Helper::HtmlRow
     #   row.first_name # => "<strong>Bogdan</strong>"
-    #   row.grid       # => Grid object
+    #   row.grid       # => Datagrid::Base object
     #   row.asset      # => User object
     #   row.each do |value|
     #     puts value
diff --git a/version-2/Readme.markdown b/version-2/Readme.markdown
index 50487b3..50a8e12 100644
--- a/version-2/Readme.markdown
+++ b/version-2/Readme.markdown
@@ -63,8 +63,6 @@ Rails [deprecates form\_for in favor of form\_with](https://guides.rubyonrails.o
 
 `datagrid_form_for` is now deprecated if favor of `datagrid_form_with`.
 
-TODO: update the wiki
-
 ``` ruby
 # V1
 datagrid_form_for(@users_grid, url: users_path)
@@ -184,7 +182,7 @@ and avoid collisions with other libraries:
 | checkboxes   | datagrid-enum-checkboxes            |
 
 All classes are now explicitly assinged inside datagrid partials.
-[Modify built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
+[Modify built-in partials](https://rubydoc.info/gems/datagrid/Datagrid/Helper#modifying-built-in-partials)
 if you want to change them.
 
 Diff for [built-in partials between V1 and V2](./views.diff)
@@ -370,7 +368,7 @@ instead of classes for column names.
 * Column name `th[class], td[class]` implemented as `td[data-column], th[data-column]`.
 
 Note that the behavior change can be reverted by 
-[updating built-in partials](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials).
+[Modify built-in partials](https://rubydoc.info/gems/datagrid/Datagrid/Helper#modifying-built-in-partials)
 Version 2 makes it as easy as possible to override the defaults of the UI.
 
 ### Filters
@@ -451,7 +449,7 @@ Renders:
 <td class="short-column" data-column="name">John</td>
 ```
 
-[Modify built-in views](https://github.com/bogdan/datagrid/wiki/Frontend#modifying-built-in-partials)
+[Modify built-in partials](https://rubydoc.info/gems/datagrid/Datagrid/Helper#modifying-built-in-partials)
 if you want to change this behavior completely.
 
 ## Use column[tag\_options]

From cca04d990c7997005fbbb1db9c428d870ee59c3f Mon Sep 17 00:00:00 2001
From: Bogdan Gusiev <agresso@gmail.com>
Date: Tue, 19 Nov 2024 20:31:38 +0100
Subject: [PATCH 157/157] Fix meta methods doc

---
 lib/datagrid/columns.rb | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/lib/datagrid/columns.rb b/lib/datagrid/columns.rb
index 795f1e7..fdd23e9 100644
--- a/lib/datagrid/columns.rb
+++ b/lib/datagrid/columns.rb
@@ -200,30 +200,30 @@ module Datagrid
   #       presenter.user.created_at
   #     end
   module Columns
-    # @param [Hash] value default options passed to {#column} method call
-    # @return [Hash] default options passed to {#column} method call
-    # @example Disable default order
-    #   self.default_column_options = { order: false }
-    # @example Makes entire report HTML
-    #   self.default_column_options = { html: true }
     # @!method default_column_options=(value)
+    #   @param [Hash] value default options passed to {#column} method call
+    #   @return [Hash] default options passed to {#column} method call
+    #   @example Disable default order
+    #     self.default_column_options = { order: false }
+    #   @example Makes entire report HTML
+    #     self.default_column_options = { html: true }
 
-    # @return [Hash] default options passed to {#column} method call
-    # @see #default_column_options=
     # @!method default_column_options
+    #   @return [Hash] default options passed to {#column} method call
+    #   @see #default_column_options=
 
     # @!method batch_size=(value)
-    # Specify a default batch size when generating CSV or just data.
-    # @param [Integer] value a batch size when generating CSV or just data. Default: 1000
-    # @return [void]
-    # @example Set batch size to 500
-    #   self.batch_size = 500
-    # @example Disable batches
-    #   self.batch_size = nil
+    #   Specify a default batch size when generating CSV or just data.
+    #   @param [Integer] value a batch size when generating CSV or just data. Default: 1000
+    #   @return [void]
+    #   @example Set batch size to 500
+    #     self.batch_size = 500
+    #   @example Disable batches
+    #     self.batch_size = nil
 
     # @!method batch_size
-    # @return [Integer]
-    # @see #batch_size=
+    #   @return [Integer]
+    #   @see #batch_size=
 
     # @visibility private
     # @param [Object] base