datagrid 1.6.2 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Readme.markdown +5 -4
- data/datagrid.gemspec +10 -14
- data/lib/datagrid/active_model.rb +25 -29
- data/lib/datagrid/column_names_attribute.rb +21 -20
- data/lib/datagrid/columns/column.rb +7 -1
- data/lib/datagrid/columns.rb +157 -189
- data/lib/datagrid/configuration.rb +6 -1
- data/lib/datagrid/core.rb +43 -22
- data/lib/datagrid/drivers/abstract_driver.rb +2 -1
- data/lib/datagrid/drivers/active_record.rb +2 -1
- data/lib/datagrid/drivers/array.rb +2 -1
- data/lib/datagrid/drivers/mongo_mapper.rb +2 -1
- data/lib/datagrid/drivers/mongoid.rb +3 -2
- data/lib/datagrid/drivers/sequel.rb +2 -1
- data/lib/datagrid/drivers.rb +2 -1
- data/lib/datagrid/engine.rb +3 -2
- data/lib/datagrid/filters/base_filter.rb +5 -2
- data/lib/datagrid/filters/boolean_enum_filter.rb +5 -3
- data/lib/datagrid/filters/boolean_filter.rb +2 -1
- data/lib/datagrid/filters/composite_filters.rb +9 -15
- data/lib/datagrid/filters/extended_boolean_filter.rb +2 -1
- data/lib/datagrid/filters/select_options.rb +34 -1
- data/lib/datagrid/filters.rb +38 -15
- data/lib/datagrid/form_builder.rb +17 -8
- data/lib/datagrid/helper.rb +49 -23
- data/lib/datagrid/ordering.rb +14 -14
- data/lib/datagrid/renderer.rb +3 -4
- data/lib/datagrid/scaffold.rb +1 -1
- data/lib/datagrid/utils.rb +1 -0
- data/lib/datagrid/version.rb +1 -1
- data/lib/datagrid.rb +2 -1
- metadata +8 -8
    
        data/lib/datagrid/core.rb
    CHANGED
    
    | @@ -4,6 +4,7 @@ require "active_support/core_ext/class/attribute" | |
| 4 4 | 
             
            module Datagrid
         | 
| 5 5 | 
             
              module Core
         | 
| 6 6 |  | 
| 7 | 
            +
                # @!visibility private
         | 
| 7 8 | 
             
                def self.included(base)
         | 
| 8 9 | 
             
                  base.extend         ClassMethods
         | 
| 9 10 | 
             
                  base.class_eval do
         | 
| @@ -12,7 +13,7 @@ module Datagrid | |
| 12 13 | 
             
                    class_attribute :datagrid_attributes, instance_writer: false
         | 
| 13 14 | 
             
                    self.datagrid_attributes = []
         | 
| 14 15 |  | 
| 15 | 
            -
                    class_attribute :dynamic_block, : | 
| 16 | 
            +
                    class_attribute :dynamic_block, instance_writer: false
         | 
| 16 17 | 
             
                    class_attribute :forbidden_attributes_protection, instance_writer: false
         | 
| 17 18 | 
             
                    self.forbidden_attributes_protection = false
         | 
| 18 19 | 
             
                    if defined?(::ActiveModel::AttributeAssignment)
         | 
| @@ -20,11 +21,12 @@ module Datagrid | |
| 20 21 | 
             
                    end
         | 
| 21 22 | 
             
                  end
         | 
| 22 23 | 
             
                  base.send :include, InstanceMethods
         | 
| 23 | 
            -
                end | 
| 24 | 
            +
                end
         | 
| 24 25 |  | 
| 25 26 | 
             
                module ClassMethods
         | 
| 26 27 |  | 
| 27 | 
            -
                   | 
| 28 | 
            +
                  # @!visibility private
         | 
| 29 | 
            +
                  def datagrid_attribute(name, &block)
         | 
| 28 30 | 
             
                    unless datagrid_attributes.include?(name)
         | 
| 29 31 | 
             
                      block ||= lambda do |value|
         | 
| 30 32 | 
             
                        value
         | 
| @@ -41,6 +43,11 @@ module Datagrid | |
| 41 43 | 
             
                  end
         | 
| 42 44 |  | 
| 43 45 | 
             
                  # Defines a scope at class level
         | 
| 46 | 
            +
                  # @return [void]
         | 
| 47 | 
            +
                  # @example
         | 
| 48 | 
            +
                  #   scope { User }
         | 
| 49 | 
            +
                  #   scope { Project.where(deleted: false) }
         | 
| 50 | 
            +
                  #   scope { Project.preload(:stages) }
         | 
| 44 51 | 
             
                  def scope(&block)
         | 
| 45 52 | 
             
                    if block
         | 
| 46 53 | 
             
                      current_scope = scope_value
         | 
| @@ -54,12 +61,17 @@ module Datagrid | |
| 54 61 | 
             
                    end
         | 
| 55 62 | 
             
                  end
         | 
| 56 63 |  | 
| 57 | 
            -
                   | 
| 64 | 
            +
                  # @!visibility private
         | 
| 65 | 
            +
                  def driver
         | 
| 58 66 | 
             
                    @driver ||= Drivers::AbstractDriver.guess_driver(scope).new
         | 
| 59 67 | 
             
                  end
         | 
| 60 68 |  | 
| 61 69 | 
             
                  # Allows dynamic columns definition, that could not be defined at class level
         | 
| 62 | 
            -
                  #
         | 
| 70 | 
            +
                  # Columns that depend on the database state or third party service
         | 
| 71 | 
            +
                  # can be defined this way.
         | 
| 72 | 
            +
                  # @param block [Proc] block that defines dynamic columns
         | 
| 73 | 
            +
                  # @return [void]
         | 
| 74 | 
            +
                  # @example
         | 
| 63 75 | 
             
                  #   class MerchantsGrid
         | 
| 64 76 | 
             
                  #
         | 
| 65 77 | 
             
                  #     scope { Merchant }
         | 
| @@ -69,12 +81,15 @@ module Datagrid | |
| 69 81 | 
             
                  #     dynamic do
         | 
| 70 82 | 
             
                  #       PurchaseCategory.all.each do |category|
         | 
| 71 83 | 
             
                  #         column(:"#{category.name.underscore}_sales") do |merchant|
         | 
| 72 | 
            -
                  #           merchant.purchases.where(: | 
| 84 | 
            +
                  #           merchant.purchases.where(category_id: category.id).count
         | 
| 73 85 | 
             
                  #         end
         | 
| 74 86 | 
             
                  #       end
         | 
| 75 87 | 
             
                  #     end
         | 
| 76 88 | 
             
                  #   end
         | 
| 77 89 | 
             
                  #
         | 
| 90 | 
            +
                  #   ProductCategory.create!(name: 'Swimwear')
         | 
| 91 | 
            +
                  #   ProductCategory.create!(name: 'Sportswear')
         | 
| 92 | 
            +
                  #
         | 
| 78 93 | 
             
                  #   grid = MerchantsGrid.new
         | 
| 79 94 | 
             
                  #   grid.data # => [
         | 
| 80 95 | 
             
                  #             #      [ "Name",   "Swimwear Sales", "Sportswear Sales", ... ]
         | 
| @@ -95,6 +110,7 @@ module Datagrid | |
| 95 110 | 
             
                  end
         | 
| 96 111 |  | 
| 97 112 | 
             
                  protected
         | 
| 113 | 
            +
             | 
| 98 114 | 
             
                  def check_scope_defined!(message = nil)
         | 
| 99 115 | 
             
                    message ||= "#{self}.scope is not defined"
         | 
| 100 116 | 
             
                    raise(Datagrid::ConfigurationError, message) unless scope_value
         | 
| @@ -105,7 +121,7 @@ module Datagrid | |
| 105 121 | 
             
                    child_class.datagrid_attributes = self.datagrid_attributes.clone
         | 
| 106 122 | 
             
                  end
         | 
| 107 123 |  | 
| 108 | 
            -
                end | 
| 124 | 
            +
                end
         | 
| 109 125 |  | 
| 110 126 | 
             
                module InstanceMethods
         | 
| 111 127 |  | 
| @@ -122,8 +138,7 @@ module Datagrid | |
| 122 138 | 
             
                    end
         | 
| 123 139 | 
             
                  end
         | 
| 124 140 |  | 
| 125 | 
            -
                  #  | 
| 126 | 
            -
                  # and ordering values
         | 
| 141 | 
            +
                  # @return [Hash<Symbol, Object>] grid attributes including filter values and ordering values
         | 
| 127 142 | 
             
                  def attributes
         | 
| 128 143 | 
             
                    result = {}
         | 
| 129 144 | 
             
                    self.datagrid_attributes.each do |name|
         | 
| @@ -132,21 +147,24 @@ module Datagrid | |
| 132 147 | 
             
                    result
         | 
| 133 148 | 
             
                  end
         | 
| 134 149 |  | 
| 135 | 
            -
                  #  | 
| 150 | 
            +
                  # @return [Object] Any datagrid attribute value
         | 
| 136 151 | 
             
                  def [](attribute)
         | 
| 137 152 | 
             
                    self.send(attribute)
         | 
| 138 153 | 
             
                  end
         | 
| 139 154 |  | 
| 155 | 
            +
                  # Assigns any datagrid attribute
         | 
| 156 | 
            +
                  # @param attribute [Symbol, String] Datagrid attribute name
         | 
| 157 | 
            +
                  # @param value [Object] Datagrid attribute value
         | 
| 158 | 
            +
                  # @return [void]
         | 
| 140 159 | 
             
                  def []=(attribute, value)
         | 
| 141 160 | 
             
                    self.send(:"#{attribute}=", value)
         | 
| 142 161 | 
             
                  end
         | 
| 143 162 |  | 
| 144 | 
            -
                  #  | 
| 163 | 
            +
                  # @return [Object] a scope relation (e.g ActiveRecord::Relation) with all applied filters
         | 
| 145 164 | 
             
                  def assets
         | 
| 146 165 | 
             
                    driver.to_scope(scope)
         | 
| 147 166 | 
             
                  end
         | 
| 148 167 |  | 
| 149 | 
            -
             | 
| 150 168 | 
             
                  # Updates datagrid attributes with a passed hash argument
         | 
| 151 169 | 
             
                  def attributes=(attributes)
         | 
| 152 170 | 
             
                    if respond_to?(:assign_attributes)
         | 
| @@ -163,7 +181,7 @@ module Datagrid | |
| 163 181 | 
             
                  end
         | 
| 164 182 |  | 
| 165 183 | 
             
                  # Returns serializable query arguments skipping all nil values
         | 
| 166 | 
            -
                  #
         | 
| 184 | 
            +
                  # @example
         | 
| 167 185 | 
             
                  #   grid = ProductsGrid.new(category: 'dresses', available: true)
         | 
| 168 186 | 
             
                  #   grid.as_query # => {category: 'dresses', available: true}
         | 
| 169 187 | 
             
                  def as_query
         | 
| @@ -174,8 +192,8 @@ module Datagrid | |
| 174 192 | 
             
                    attributes
         | 
| 175 193 | 
             
                  end
         | 
| 176 194 |  | 
| 177 | 
            -
                  #  | 
| 178 | 
            -
                  #
         | 
| 195 | 
            +
                  # @return [Hash<Symbol, Hash<Symbol, Object>>] query parameters to link this grid from a page
         | 
| 196 | 
            +
                  # @example
         | 
| 179 197 | 
             
                  #   grid = ProductsGrid.new(category: 'dresses', available: true)
         | 
| 180 198 | 
             
                  #   Rails.application.routes.url_helpers.products_path(grid.query_params)
         | 
| 181 199 | 
             
                  #     # => "/products?products_grid[category]=dresses&products_grid[available]=true"
         | 
| @@ -184,19 +202,18 @@ module Datagrid | |
| 184 202 | 
             
                  end
         | 
| 185 203 |  | 
| 186 204 | 
             
                  # Redefines scope at instance level
         | 
| 187 | 
            -
                  #
         | 
| 205 | 
            +
                  # @example
         | 
| 188 206 | 
             
                  #   class MyGrid
         | 
| 189 207 | 
             
                  #     scope { Article.order('created_at desc') }
         | 
| 190 208 | 
             
                  #   end
         | 
| 191 209 | 
             
                  #
         | 
| 192 210 | 
             
                  #   grid = MyGrid.new
         | 
| 193 211 | 
             
                  #   grid.scope do |scope|
         | 
| 194 | 
            -
                  #     scope.where(: | 
| 212 | 
            +
                  #     scope.where(author_id: current_user.id)
         | 
| 195 213 | 
             
                  #   end
         | 
| 196 214 | 
             
                  #   grid.assets
         | 
| 197 215 | 
             
                  #       # => SELECT * FROM articles WHERE author_id = ?
         | 
| 198 216 | 
             
                  #       #    ORDER BY created_at desc
         | 
| 199 | 
            -
                  #
         | 
| 200 217 | 
             
                  def scope(&block)
         | 
| 201 218 | 
             
                    if block_given?
         | 
| 202 219 | 
             
                      current_scope = scope
         | 
| @@ -211,23 +228,27 @@ module Datagrid | |
| 211 228 | 
             
                  end
         | 
| 212 229 |  | 
| 213 230 | 
             
                  # Resets current instance scope to default scope defined in a class
         | 
| 231 | 
            +
                  # @return [void]
         | 
| 214 232 | 
             
                  def reset_scope
         | 
| 215 233 | 
             
                    self.scope_value = self.class.scope_value
         | 
| 216 234 | 
             
                  end
         | 
| 217 235 |  | 
| 218 | 
            -
                  #  | 
| 236 | 
            +
                  # @return [Boolean] true if the scope was redefined for this instance of grid object
         | 
| 219 237 | 
             
                  def redefined_scope?
         | 
| 220 238 | 
             
                    self.class.scope_value != scope_value
         | 
| 221 239 | 
             
                  end
         | 
| 222 240 |  | 
| 223 | 
            -
                   | 
| 241 | 
            +
                  # @!visibility private
         | 
| 242 | 
            +
                  def driver
         | 
| 224 243 | 
             
                    self.class.driver
         | 
| 225 244 | 
             
                  end
         | 
| 226 245 |  | 
| 227 | 
            -
                   | 
| 246 | 
            +
                  # @!visibility private
         | 
| 247 | 
            +
                  def check_scope_defined!(message = nil)
         | 
| 228 248 | 
             
                    self.class.send :check_scope_defined!, message
         | 
| 229 249 | 
             
                  end
         | 
| 230 250 |  | 
| 251 | 
            +
                  # @return [String] a datagrid attributes and their values in inspection form
         | 
| 231 252 | 
             
                  def inspect
         | 
| 232 253 | 
             
                    attrs = attributes.map do |key, value|
         | 
| 233 254 | 
             
                      "#{key}: #{value.inspect}"
         | 
| @@ -240,6 +261,6 @@ module Datagrid | |
| 240 261 | 
             
                      attributes == other.attributes &&
         | 
| 241 262 | 
             
                      scope == other.scope
         | 
| 242 263 | 
             
                  end
         | 
| 243 | 
            -
                end | 
| 264 | 
            +
                end
         | 
| 244 265 | 
             
              end
         | 
| 245 266 | 
             
            end
         | 
| @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            module Datagrid
         | 
| 2 2 | 
             
              module Drivers
         | 
| 3 | 
            -
                 | 
| 3 | 
            +
                # @!visibility private
         | 
| 4 | 
            +
                class Mongoid < AbstractDriver
         | 
| 4 5 |  | 
| 5 6 | 
             
                  def self.match?(scope)
         | 
| 6 7 | 
             
                    return false unless defined?(::Mongoid)
         | 
| @@ -63,7 +64,7 @@ module Datagrid | |
| 63 64 | 
             
                    return nil unless type
         | 
| 64 65 | 
             
                    {
         | 
| 65 66 | 
             
                      [BigDecimal , String, Symbol, Range, Array, Hash, ] => :string,
         | 
| 66 | 
            -
                      [Boolean] => :boolean,
         | 
| 67 | 
            +
                      [::Mongoid::Boolean] => :boolean,
         | 
| 67 68 |  | 
| 68 69 | 
             
                      [Date] => :date,
         | 
| 69 70 |  | 
    
        data/lib/datagrid/drivers.rb
    CHANGED
    
    
    
        data/lib/datagrid/engine.rb
    CHANGED
    
    | @@ -1,12 +1,13 @@ | |
| 1 1 | 
             
            require "rails/engine"
         | 
| 2 2 |  | 
| 3 3 | 
             
            module Datagrid
         | 
| 4 | 
            +
              # @!private
         | 
| 4 5 | 
             
              class Engine < ::Rails::Engine
         | 
| 5 6 | 
             
                initializer "datagrid.helpers" do
         | 
| 6 7 | 
             
                  #TODO: check why it doesn't work
         | 
| 7 8 | 
             
                  ActiveSupport.on_load :action_view do
         | 
| 8 9 | 
             
                    include Datagrid::Helper
         | 
| 9 | 
            -
                  end | 
| 10 | 
            -
                end | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 11 12 | 
             
              end
         | 
| 12 13 | 
             
            end
         | 
| @@ -1,7 +1,10 @@ | |
| 1 | 
            +
            # An error raise when datagrid filter is defined incorrectly and
         | 
| 2 | 
            +
            # causes filtering chain to be broken
         | 
| 1 3 | 
             
            class Datagrid::FilteringError < StandardError
         | 
| 2 4 | 
             
            end
         | 
| 3 5 |  | 
| 4 | 
            -
             | 
| 6 | 
            +
            # @!visibility private
         | 
| 7 | 
            +
            class Datagrid::Filters::BaseFilter
         | 
| 5 8 |  | 
| 6 9 | 
             
              attr_accessor :grid_class, :options, :block, :name
         | 
| 7 10 |  | 
| @@ -26,7 +29,7 @@ class Datagrid::Filters::BaseFilter #:nodoc: | |
| 26 29 | 
             
                result = execute(value, scope, grid_object)
         | 
| 27 30 |  | 
| 28 31 | 
             
                return scope unless result
         | 
| 29 | 
            -
                if result | 
| 32 | 
            +
                if result == Datagrid::Filters::DEFAULT_FILTER_BLOCK
         | 
| 30 33 | 
             
                  result = default_filter(value, scope, grid_object)
         | 
| 31 34 | 
             
                end
         | 
| 32 35 | 
             
                unless grid_object.driver.match?(result)
         | 
| @@ -1,17 +1,19 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # @!visibility private
         | 
| 2 | 
            +
            class Datagrid::Filters::BooleanEnumFilter < Datagrid::Filters::EnumFilter
         | 
| 2 3 |  | 
| 3 4 | 
             
              YES = "YES"
         | 
| 4 5 | 
             
              NO = "NO"
         | 
| 5 6 |  | 
| 6 7 | 
             
              def initialize(report, attribute, options = {}, &block)
         | 
| 8 | 
            +
                Datagrid::Utils.warn_once("Datagrid eboolean filter is deprecated in favor of xboolean filter")
         | 
| 7 9 | 
             
                options[:select] = [YES, NO].map do |key, value|
         | 
| 8 | 
            -
                  [I18n.t("datagrid.filters.eboolean.#{key.downcase}", : | 
| 10 | 
            +
                  [I18n.t("datagrid.filters.eboolean.#{key.downcase}", default: key.downcase.capitalize), key]
         | 
| 9 11 | 
             
                end
         | 
| 10 12 | 
             
                super(report, attribute, options, &block)
         | 
| 11 13 | 
             
              end
         | 
| 12 14 |  | 
| 13 15 |  | 
| 14 16 | 
             
              def checkbox_id(value)
         | 
| 15 | 
            -
                [object_name, name, value].join('_').underscore | 
| 17 | 
            +
                [object_name, name, value].join('_').underscore
         | 
| 16 18 | 
             
              end
         | 
| 17 19 | 
             
            end
         | 
| @@ -1,25 +1,25 @@ | |
| 1 1 | 
             
            module Datagrid
         | 
| 2 2 | 
             
              module Filters
         | 
| 3 | 
            -
                 | 
| 3 | 
            +
                # @!visibility private
         | 
| 4 | 
            +
                module CompositeFilters
         | 
| 4 5 |  | 
| 5 6 | 
             
                  def self.included(base)
         | 
| 6 7 | 
             
                    base.extend         ClassMethods
         | 
| 7 8 | 
             
                    base.class_eval do
         | 
| 8 | 
            -
             | 
| 9 9 | 
             
                    end
         | 
| 10 | 
            -
             | 
| 11 | 
            -
                  end # self.included
         | 
| 10 | 
            +
                  end
         | 
| 12 11 |  | 
| 12 | 
            +
                  # @!visibility private
         | 
| 13 13 | 
             
                  module ClassMethods
         | 
| 14 14 |  | 
| 15 15 | 
             
                    def date_range_filters(field, from_options = {}, to_options = {})
         | 
| 16 16 | 
             
                      from_options = normalize_composite_filter_options(from_options, field)
         | 
| 17 17 | 
             
                      to_options = normalize_composite_filter_options(to_options, field)
         | 
| 18 18 |  | 
| 19 | 
            -
                      filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :date, from_options) do |date, scope, grid|
         | 
| 19 | 
            +
                      filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :date, **from_options) do |date, scope, grid|
         | 
| 20 20 | 
             
                        grid.driver.greater_equal(scope, field, date)
         | 
| 21 21 | 
             
                      end
         | 
| 22 | 
            -
                      filter(to_options[:name] || :"to_#{field.to_s.tr('.', '_')}", :date, to_options) do |date, scope, grid|
         | 
| 22 | 
            +
                      filter(to_options[:name] || :"to_#{field.to_s.tr('.', '_')}", :date, **to_options) do |date, scope, grid|
         | 
| 23 23 | 
             
                        grid.driver.less_equal(scope, field, date)
         | 
| 24 24 | 
             
                      end
         | 
| 25 25 | 
             
                    end
         | 
| @@ -27,10 +27,10 @@ module Datagrid | |
| 27 27 | 
             
                    def integer_range_filters(field, from_options = {}, to_options = {})
         | 
| 28 28 | 
             
                      from_options = normalize_composite_filter_options(from_options, field)
         | 
| 29 29 | 
             
                      to_options = normalize_composite_filter_options(to_options, field)
         | 
| 30 | 
            -
                      filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :integer, from_options) do |value, scope, grid|
         | 
| 30 | 
            +
                      filter(from_options[:name] || :"from_#{field.to_s.tr('.', '_')}", :integer, **from_options) do |value, scope, grid|
         | 
| 31 31 | 
             
                        grid.driver.greater_equal(scope, field, value)
         | 
| 32 32 | 
             
                      end
         | 
| 33 | 
            -
                      filter(to_options[:name] || :"to_#{field.to_s.tr('.', '_')}", :integer, to_options) do |value, scope, grid|
         | 
| 33 | 
            +
                      filter(to_options[:name] || :"to_#{field.to_s.tr('.', '_')}", :integer, **to_options) do |value, scope, grid|
         | 
| 34 34 | 
             
                        grid.driver.less_equal(scope, field, value)
         | 
| 35 35 | 
             
                      end
         | 
| 36 36 | 
             
                    end
         | 
| @@ -41,13 +41,7 @@ module Datagrid | |
| 41 41 | 
             
                      end
         | 
| 42 42 | 
             
                      options
         | 
| 43 43 | 
             
                    end
         | 
| 44 | 
            -
                  end | 
| 45 | 
            -
             | 
| 46 | 
            -
                  module InstanceMethods
         | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
                  end # InstanceMethods
         | 
| 50 | 
            -
             | 
| 44 | 
            +
                  end
         | 
| 51 45 | 
             
                end
         | 
| 52 46 | 
             
              end
         | 
| 53 47 | 
             
            end
         | 
| @@ -1,5 +1,4 @@ | |
| 1 1 | 
             
            module Datagrid::Filters::SelectOptions
         | 
| 2 | 
            -
             | 
| 3 2 | 
             
              def select(object)
         | 
| 4 3 | 
             
                select = self.options[:select]
         | 
| 5 4 | 
             
                if select.is_a?(Symbol)
         | 
| @@ -11,6 +10,18 @@ module Datagrid::Filters::SelectOptions | |
| 11 10 | 
             
                end
         | 
| 12 11 | 
             
              end
         | 
| 13 12 |  | 
| 13 | 
            +
              def select_values(object)
         | 
| 14 | 
            +
                options = select(object)
         | 
| 15 | 
            +
                groups_used = grouped_choices?(options)
         | 
| 16 | 
            +
                options.map do |option|
         | 
| 17 | 
            +
                  if groups_used
         | 
| 18 | 
            +
                    option[1].map {|o| option_text_and_value(o)}
         | 
| 19 | 
            +
                  else
         | 
| 20 | 
            +
                    option_text_and_value(option)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end.map(&:last)
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 14 25 | 
             
              def include_blank
         | 
| 15 26 | 
             
                unless prompt
         | 
| 16 27 | 
             
                  options.has_key?(:include_blank) ?
         | 
| @@ -21,4 +32,26 @@ module Datagrid::Filters::SelectOptions | |
| 21 32 | 
             
              def prompt
         | 
| 22 33 | 
             
                options.has_key?(:prompt) ? Datagrid::Utils.callable(options[:prompt]) : false
         | 
| 23 34 | 
             
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              protected
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              # Rails built-in method:
         | 
| 39 | 
            +
              # https://github.com/rails/rails/blob/94e80269e36caf7b2d22a7ab68e6898d3a824122/actionview/lib/action_view/helpers/form_options_helper.rb#L789
         | 
| 40 | 
            +
              # Code reuse is difficult, so it is easier to duplicate it
         | 
| 41 | 
            +
              def option_text_and_value(option)
         | 
| 42 | 
            +
                # Options are [text, value] pairs or strings used for both.
         | 
| 43 | 
            +
                if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
         | 
| 44 | 
            +
                  option = option.reject { |e| Hash === e } if Array === option
         | 
| 45 | 
            +
                  [option.first, option.last]
         | 
| 46 | 
            +
                else
         | 
| 47 | 
            +
                  [option, option]
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              # Rails built-in method:
         | 
| 52 | 
            +
              # https://github.com/rails/rails/blob/f95c0b7e96eb36bc3efc0c5beffbb9e84ea664e4/actionview/lib/action_view/helpers/tags/select.rb#L36
         | 
| 53 | 
            +
              # Code reuse is difficult, so it is easier to duplicate it
         | 
| 54 | 
            +
              def grouped_choices?(choices)
         | 
| 55 | 
            +
                !choices.blank? && choices.first.respond_to?(:last) && Array === choices.first.last
         | 
| 56 | 
            +
              end
         | 
| 24 57 | 
             
            end
         | 
    
        data/lib/datagrid/filters.rb
    CHANGED
    
    | @@ -31,10 +31,10 @@ module Datagrid | |
| 31 31 | 
             
                  :dynamic => Filters::DynamicFilter
         | 
| 32 32 | 
             
                }
         | 
| 33 33 |  | 
| 34 | 
            -
                 | 
| 35 | 
            -
                end
         | 
| 34 | 
            +
                DEFAULT_FILTER_BLOCK = Object.new
         | 
| 36 35 |  | 
| 37 | 
            -
                 | 
| 36 | 
            +
                # @!visibility private
         | 
| 37 | 
            +
                def self.included(base)
         | 
| 38 38 | 
             
                  base.extend         ClassMethods
         | 
| 39 39 | 
             
                  base.class_eval do
         | 
| 40 40 |  | 
| @@ -45,13 +45,18 @@ module Datagrid | |
| 45 45 |  | 
| 46 46 | 
             
                  end
         | 
| 47 47 | 
             
                  base.send :include, InstanceMethods
         | 
| 48 | 
            -
                end | 
| 48 | 
            +
                end
         | 
| 49 49 |  | 
| 50 50 | 
             
                module ClassMethods
         | 
| 51 51 |  | 
| 52 52 | 
             
                  # Returns filter definition object by name
         | 
| 53 53 | 
             
                  def filter_by_name(attribute)
         | 
| 54 | 
            -
                     | 
| 54 | 
            +
                    if attribute.is_a?(Datagrid::Filters::BaseFilter)
         | 
| 55 | 
            +
                      unless ancestors.include?(attribute.grid_class)
         | 
| 56 | 
            +
                        raise "#{attribute.grid_class}##{attribute.name} filter doen't belong to #{self.class}"
         | 
| 57 | 
            +
                      end
         | 
| 58 | 
            +
                      return attribute
         | 
| 59 | 
            +
                    end
         | 
| 55 60 | 
             
                    self.filters.find do |filter|
         | 
| 56 61 | 
             
                      filter.name == attribute.to_sym
         | 
| 57 62 | 
             
                    end
         | 
| @@ -108,7 +113,7 @@ module Datagrid | |
| 108 113 | 
             
                  end
         | 
| 109 114 |  | 
| 110 115 | 
             
                  def default_filter
         | 
| 111 | 
            -
                     | 
| 116 | 
            +
                    DEFAULT_FILTER_BLOCK
         | 
| 112 117 | 
             
                  end
         | 
| 113 118 |  | 
| 114 119 | 
             
                  def inspect
         | 
| @@ -132,11 +137,12 @@ module Datagrid | |
| 132 137 | 
             
                      "#{filter.name}: #{filter.type}"
         | 
| 133 138 | 
             
                    end.join(", ")
         | 
| 134 139 | 
             
                  end
         | 
| 135 | 
            -
                end | 
| 140 | 
            +
                end
         | 
| 136 141 |  | 
| 137 142 | 
             
                module InstanceMethods
         | 
| 138 143 |  | 
| 139 | 
            -
                   | 
| 144 | 
            +
                  # @!visibility private
         | 
| 145 | 
            +
                  def initialize(*args, &block)
         | 
| 140 146 | 
             
                    self.filters_array = self.class.filters_array.clone
         | 
| 141 147 | 
             
                    self.filters_array.each do |filter|
         | 
| 142 148 | 
             
                      self[filter.name] = filter.default(self)
         | 
| @@ -144,7 +150,8 @@ module Datagrid | |
| 144 150 | 
             
                    super(*args, &block)
         | 
| 145 151 | 
             
                  end
         | 
| 146 152 |  | 
| 147 | 
            -
                   | 
| 153 | 
            +
                  # @!visibility private
         | 
| 154 | 
            +
                  def assets
         | 
| 148 155 | 
             
                    apply_filters(super, filters)
         | 
| 149 156 | 
             
                  end
         | 
| 150 157 |  | 
| @@ -178,11 +185,18 @@ module Datagrid | |
| 178 185 | 
             
                  # Returns select options for specific filter or filter name
         | 
| 179 186 | 
             
                  # If given filter doesn't support select options raises `ArgumentError`
         | 
| 180 187 | 
             
                  def select_options(filter)
         | 
| 181 | 
            -
                     | 
| 182 | 
            -
             | 
| 183 | 
            -
             | 
| 184 | 
            -
             | 
| 185 | 
            -
             | 
| 188 | 
            +
                    find_select_filter(filter).select(self)
         | 
| 189 | 
            +
                  end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                  # Sets all options as selected for a filter that has options
         | 
| 192 | 
            +
                  def select_all(filter)
         | 
| 193 | 
            +
                    filter = find_select_filter(filter)
         | 
| 194 | 
            +
                    self[filter.name] = select_values(filter)
         | 
| 195 | 
            +
                  end
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                  # Returns all values that can be set to a filter with select options
         | 
| 198 | 
            +
                  def select_values(filter)
         | 
| 199 | 
            +
                    find_select_filter(filter).select_values(self)
         | 
| 186 200 | 
             
                  end
         | 
| 187 201 |  | 
| 188 202 | 
             
                  def default_filter
         | 
| @@ -198,12 +212,21 @@ module Datagrid | |
| 198 212 |  | 
| 199 213 | 
             
                  protected
         | 
| 200 214 |  | 
| 215 | 
            +
                  def find_select_filter(filter)
         | 
| 216 | 
            +
                    filter = filter_by_name(filter)
         | 
| 217 | 
            +
                    unless filter.class.included_modules.include?(::Datagrid::Filters::SelectOptions)
         | 
| 218 | 
            +
                      raise ::Datagrid::ArgumentError,
         | 
| 219 | 
            +
                        "#{self.class.name}##{filter.name} with type #{FILTER_TYPES.invert[filter.class].inspect} can not have select options"
         | 
| 220 | 
            +
                    end
         | 
| 221 | 
            +
                    filter
         | 
| 222 | 
            +
                  end
         | 
| 223 | 
            +
             | 
| 201 224 | 
             
                  def apply_filters(current_scope, filters)
         | 
| 202 225 | 
             
                    filters.inject(current_scope) do |result, filter|
         | 
| 203 226 | 
             
                      filter.apply(self, result, filter_value(filter))
         | 
| 204 227 | 
             
                    end
         | 
| 205 228 | 
             
                  end
         | 
| 206 | 
            -
                end | 
| 229 | 
            +
                end
         | 
| 207 230 |  | 
| 208 231 | 
             
              end
         | 
| 209 232 | 
             
            end
         | 
| @@ -2,20 +2,27 @@ require "action_view" | |
| 2 2 |  | 
| 3 3 | 
             
            module Datagrid
         | 
| 4 4 | 
             
              module FormBuilder
         | 
| 5 | 
            -
             | 
| 6 | 
            -
                #  | 
| 7 | 
            -
                 | 
| 5 | 
            +
                # @param filter_or_attribute [Datagrid::Filters::BaseFilter, String, Symbol] filter object or filter name
         | 
| 6 | 
            +
                # @param options [Hash] options of rails form input helper
         | 
| 7 | 
            +
                # @return [String] a form input html for the corresponding filter name
         | 
| 8 | 
            +
                #   * <tt>select</tt> for enum, xboolean filter types
         | 
| 9 | 
            +
                #   * <tt>check_box</tt> for boolean filter type
         | 
| 10 | 
            +
                #   * <tt>text_field</tt> for other filter types
         | 
| 11 | 
            +
                def datagrid_filter(filter_or_attribute, options = {}, &block)
         | 
| 8 12 | 
             
                  filter = datagrid_get_filter(filter_or_attribute)
         | 
| 9 13 | 
             
                  options = {
         | 
| 10 14 | 
             
                    **filter.input_options,
         | 
| 11 | 
            -
                    **add_html_classes(options, filter.name, datagrid_filter_html_class(filter))
         | 
| 15 | 
            +
                    **add_html_classes(options, filter.name, datagrid_filter_html_class(filter)),
         | 
| 12 16 | 
             
                  }
         | 
| 13 17 | 
             
                  # Prevent partials option from appearing in HTML attributes
         | 
| 14 18 | 
             
                  options.delete(:partials) # Legacy option
         | 
| 15 19 | 
             
                  self.send(filter.form_builder_helper_name, filter, options, &block)
         | 
| 16 20 | 
             
                end
         | 
| 17 21 |  | 
| 18 | 
            -
                #  | 
| 22 | 
            +
                # @param filter_or_attribute [Datagrid::Filters::BaseFilter, String, Symbol] filter object or filter name
         | 
| 23 | 
            +
                # @param text [String, nil] label text, defaults to <tt>filter.header</tt>
         | 
| 24 | 
            +
                # @param options [Hash] options of rails <tt>label</tt> helper
         | 
| 25 | 
            +
                # @return [String] a form label html for the corresponding filter name
         | 
| 19 26 | 
             
                def datagrid_label(filter_or_attribute, text = nil, **options, &block)
         | 
| 20 27 | 
             
                  filter = datagrid_get_filter(filter_or_attribute)
         | 
| 21 28 | 
             
                  label(filter.name, text || filter.header, **filter.label_options, **options, &block)
         | 
| @@ -70,9 +77,11 @@ module Datagrid | |
| 70 77 | 
             
                    select(
         | 
| 71 78 | 
             
                      filter.name,
         | 
| 72 79 | 
             
                      object.select_options(filter) || [],
         | 
| 73 | 
            -
                      { | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 80 | 
            +
                      {
         | 
| 81 | 
            +
                        include_blank: filter.include_blank,
         | 
| 82 | 
            +
                        prompt: filter.prompt,
         | 
| 83 | 
            +
                        include_hidden: false
         | 
| 84 | 
            +
                      },
         | 
| 76 85 | 
             
                       multiple: filter.multiple?,
         | 
| 77 86 | 
             
                       **options,
         | 
| 78 87 | 
             
                       &block
         |