super 0.0.14 → 0.18.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/README.md +2 -2
- data/app/assets/javascripts/super/application.js +148 -15
- data/app/assets/stylesheets/super/application.css +15 -22
- data/app/controllers/super/application_controller.rb +44 -42
- data/app/controllers/super/substructure_controller.rb +313 -0
- data/app/helpers/super/form_builder_helper.rb +7 -0
- data/app/views/layouts/super/application.html.erb +3 -21
- data/app/views/super/application/_collection_header.html.erb +2 -2
- data/app/views/super/application/_display_actions.html.erb +1 -1
- data/app/views/super/application/_display_index.html.erb +2 -2
- data/app/views/super/application/_display_show.html.erb +1 -1
- data/app/views/super/application/_filter.html.erb +62 -2
- data/app/views/super/application/_form_field.html.erb +5 -0
- data/app/views/super/application/_member_header.html.erb +2 -2
- data/app/views/super/application/_pagination.html.erb +1 -1
- data/app/views/super/application/_site_footer.html.erb +3 -0
- data/app/views/super/application/_site_header.html.erb +17 -0
- data/app/views/super/application/_sort_expression.html.erb +2 -2
- data/app/views/super/application/index.csv.erb +14 -0
- data/app/views/super/feather/README.md +0 -1
- data/frontend/super-frontend/dist/application.css +15 -22
- data/frontend/super-frontend/dist/application.js +148 -15
- data/lib/generators/super/install/install_generator.rb +0 -16
- data/lib/generators/super/install/templates/base_controller.rb.tt +0 -8
- data/lib/generators/super/resource/templates/resources_controller.rb.tt +4 -4
- data/lib/super/action_inquirer.rb +18 -3
- data/lib/super/badge.rb +60 -0
- data/lib/super/cheat.rb +6 -6
- data/lib/super/display/guesser.rb +1 -1
- data/lib/super/display/schema_types.rb +49 -1
- data/lib/super/display.rb +2 -1
- data/lib/super/error.rb +3 -0
- data/lib/super/filter/form_object.rb +74 -48
- data/lib/super/filter/guesser.rb +2 -0
- data/lib/super/filter/operator.rb +90 -64
- data/lib/super/filter/schema_types.rb +63 -80
- data/lib/super/filter.rb +1 -1
- data/lib/super/form/builder.rb +30 -39
- data/lib/super/form/field_transcript.rb +43 -0
- data/lib/super/form/guesser.rb +4 -4
- data/lib/super/form/schema_types.rb +66 -22
- data/lib/super/link.rb +2 -2
- data/lib/super/pagination.rb +2 -44
- data/lib/super/reset.rb +25 -0
- data/lib/super/schema.rb +4 -0
- data/lib/super/useful/builder.rb +4 -4
- data/lib/super/version.rb +1 -1
- data/lib/super.rb +3 -1
- data/lib/tasks/super/cheat.rake +1 -1
- metadata +25 -19
- data/app/views/super/application/_filter_type_select.html.erb +0 -21
- data/app/views/super/application/_filter_type_text.html.erb +0 -18
- data/app/views/super/application/_filter_type_timestamp.html.erb +0 -24
- data/app/views/super/application/_form_field_checkbox.html.erb +0 -1
- data/app/views/super/application/_form_field_flatpickr_date.html.erb +0 -8
- data/app/views/super/application/_form_field_flatpickr_datetime.html.erb +0 -8
- data/app/views/super/application/_form_field_flatpickr_time.html.erb +0 -8
- data/app/views/super/application/_form_field_rich_text_area.html.erb +0 -1
- data/app/views/super/application/_form_field_select.html.erb +0 -1
- data/app/views/super/application/_form_field_text.html.erb +0 -1
- data/app/views/super/feather/_chevron_down.html +0 -1
- data/docs/cheat.md +0 -41
- data/lib/super/controls/optional.rb +0 -113
- data/lib/super/controls/steps.rb +0 -106
- data/lib/super/controls/view.rb +0 -55
- data/lib/super/controls.rb +0 -22
| @@ -19,12 +19,27 @@ module Super | |
| 19 19 | 
             
                #
         | 
| 20 20 | 
             
                def self.default_for_resources
         | 
| 21 21 | 
             
                  {
         | 
| 22 | 
            -
                    read | 
| 23 | 
            -
                    write | 
| 24 | 
            -
                    delete | 
| 22 | 
            +
                    "read" => %w[index show new edit],
         | 
| 23 | 
            +
                    "write" => %w[create update destroy],
         | 
| 24 | 
            +
                    "delete" => %w[destroy],
         | 
| 25 25 | 
             
                  }
         | 
| 26 26 | 
             
                end
         | 
| 27 27 |  | 
| 28 | 
            +
                # @return [ActionInquirer]
         | 
| 29 | 
            +
                def self.index!; new(default_for_resources, "index"); end
         | 
| 30 | 
            +
                # @return [ActionInquirer]
         | 
| 31 | 
            +
                def self.show!; new(default_for_resources, "show"); end
         | 
| 32 | 
            +
                # @return [ActionInquirer]
         | 
| 33 | 
            +
                def self.new!; new(default_for_resources, "new"); end
         | 
| 34 | 
            +
                # @return [ActionInquirer]
         | 
| 35 | 
            +
                def self.edit!; new(default_for_resources, "edit"); end
         | 
| 36 | 
            +
                # @return [ActionInquirer]
         | 
| 37 | 
            +
                def self.create!; new(default_for_resources, "create"); end
         | 
| 38 | 
            +
                # @return [ActionInquirer]
         | 
| 39 | 
            +
                def self.update!; new(default_for_resources, "update"); end
         | 
| 40 | 
            +
                # @return [ActionInquirer]
         | 
| 41 | 
            +
                def self.destroy!; new(default_for_resources, "destroy"); end
         | 
| 42 | 
            +
             | 
| 28 43 | 
             
                def initialize(categories_and_their_actions, action)
         | 
| 29 44 | 
             
                  @categories = categories_and_their_actions.keys.map(&:to_s)
         | 
| 30 45 | 
             
                  @actions = categories_and_their_actions.values.flatten.uniq.map(&:to_s)
         | 
    
        data/lib/super/badge.rb
    ADDED
    
    | @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Super
         | 
| 4 | 
            +
              class Badge
         | 
| 5 | 
            +
                STYLES = {
         | 
| 6 | 
            +
                  light: "bg-gray-100 text-black",
         | 
| 7 | 
            +
                  dark: "bg-gray-900 text-white",
         | 
| 8 | 
            +
                  red: "bg-red-700 text-white",
         | 
| 9 | 
            +
                  yellow: "bg-yellow-400 text-black",
         | 
| 10 | 
            +
                  green: "bg-green-700 text-white",
         | 
| 11 | 
            +
                  blue: "bg-blue-700 text-white",
         | 
| 12 | 
            +
                  purple: "bg-purple-800 text-white",
         | 
| 13 | 
            +
                }
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def initialize(text, styles: nil)
         | 
| 16 | 
            +
                  @text = text
         | 
| 17 | 
            +
                  @requested_styles = Array(styles.presence).flatten
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def styles
         | 
| 21 | 
            +
                  return @styles if instance_variable_defined?(:@styles)
         | 
| 22 | 
            +
                  @styles =
         | 
| 23 | 
            +
                    if requested_styles.delete(:reset)
         | 
| 24 | 
            +
                      []
         | 
| 25 | 
            +
                    else
         | 
| 26 | 
            +
                      COMMON_STYLES
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  if requested_styles.empty?
         | 
| 30 | 
            +
                    @styles += [STYLES[:light]]
         | 
| 31 | 
            +
                  else
         | 
| 32 | 
            +
                    requested_styles.each do |style|
         | 
| 33 | 
            +
                      @styles +=
         | 
| 34 | 
            +
                        if STYLES.key?(style)
         | 
| 35 | 
            +
                          [STYLES[style]]
         | 
| 36 | 
            +
                        else
         | 
| 37 | 
            +
                          [style]
         | 
| 38 | 
            +
                        end
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  @styles
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def to_s
         | 
| 46 | 
            +
                  ActionController::Base.helpers.content_tag(
         | 
| 47 | 
            +
                    :span,
         | 
| 48 | 
            +
                    @text,
         | 
| 49 | 
            +
                    class: styles.join(" ")
         | 
| 50 | 
            +
                  )
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                private
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                COMMON_STYLES = %w[rounded px-2 py-1 text-xs leading-none font-bold]
         | 
| 56 | 
            +
                private_constant :COMMON_STYLES
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                attr_reader :requested_styles
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
    
        data/lib/super/cheat.rb
    CHANGED
    
    | @@ -2,15 +2,15 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Super
         | 
| 4 4 | 
             
              class Cheat
         | 
| 5 | 
            -
                def  | 
| 6 | 
            -
                  paths = %w[ | 
| 5 | 
            +
                def controller
         | 
| 6 | 
            +
                  paths = %w[../../app/controllers/super/substructure_controller.rb]
         | 
| 7 7 | 
             
                  methods =
         | 
| 8 8 | 
             
                    paths
         | 
| 9 | 
            -
                    .map { |f| File.read(File. | 
| 10 | 
            -
                    .flat_map { |content| content.scan(/^\s+def .*$/) }
         | 
| 11 | 
            -
                    .map { |method| method.strip.sub(/^def /, "#") }
         | 
| 9 | 
            +
                    .map { |f| File.read(File.expand_path(f, __dir__)) }
         | 
| 10 | 
            +
                    .flat_map { |content| content.scan(/^\s+(?:helper_method )?def .*$/) }
         | 
| 11 | 
            +
                    .map { |method| method.strip.sub(/^(?:helper_method )?def /, "#") }
         | 
| 12 12 |  | 
| 13 | 
            -
                  puts "== Super:: | 
| 13 | 
            +
                  puts "== Super::ApplicationController"
         | 
| 14 14 | 
             
                  puts methods.join("\n")
         | 
| 15 15 | 
             
                end
         | 
| 16 16 | 
             
              end
         | 
| @@ -61,6 +61,47 @@ module Super | |
| 61 61 | 
             
                    end
         | 
| 62 62 | 
             
                  end
         | 
| 63 63 |  | 
| 64 | 
            +
                  class Badge
         | 
| 65 | 
            +
                    extend Useful::Builder
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    def initialize(builder)
         | 
| 68 | 
            +
                      @builder = builder
         | 
| 69 | 
            +
                      @whens = {}
         | 
| 70 | 
            +
                      format_for_lookup(&:itself)
         | 
| 71 | 
            +
                      format_for_display(&:itself)
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    builder_with_block def when(*patterns, &block)
         | 
| 75 | 
            +
                      patterns.each do |pattern|
         | 
| 76 | 
            +
                        @whens[pattern] = block
         | 
| 77 | 
            +
                      end
         | 
| 78 | 
            +
                    end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                    builder_with_block def else(&block)
         | 
| 81 | 
            +
                      @else = block
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    builder_with_block def format_for_lookup(&block)
         | 
| 85 | 
            +
                      @format_for_lookup = block
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    builder_with_block def format_for_display(&block)
         | 
| 89 | 
            +
                      @format_for_display = block
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    def build
         | 
| 93 | 
            +
                      @builder.transform do |value|
         | 
| 94 | 
            +
                        lookup_value = @format_for_lookup.call(value)
         | 
| 95 | 
            +
                        block = @whens[lookup_value] || @else
         | 
| 96 | 
            +
                        Super::Badge.new(
         | 
| 97 | 
            +
                          @format_for_display.call(value),
         | 
| 98 | 
            +
                          styles: block&.call
         | 
| 99 | 
            +
                        )
         | 
| 100 | 
            +
                      end
         | 
| 101 | 
            +
                      @builder.build
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 64 105 | 
             
                  def initialize(fields:)
         | 
| 65 106 | 
             
                    @actions_called = false
         | 
| 66 107 | 
             
                    @fields = fields
         | 
| @@ -89,7 +130,6 @@ module Super | |
| 89 130 | 
             
                  end
         | 
| 90 131 |  | 
| 91 132 | 
             
                  def string; real(&:to_s); end
         | 
| 92 | 
            -
                  alias text string
         | 
| 93 133 |  | 
| 94 134 | 
             
                  def timestamp; real(&:to_s); end
         | 
| 95 135 | 
             
                  def time; real { |value| value.strftime("%H:%M:%S") }; end
         | 
| @@ -100,6 +140,14 @@ module Super | |
| 100 140 | 
             
                    end
         | 
| 101 141 | 
             
                  end
         | 
| 102 142 |  | 
| 143 | 
            +
                  def badge(*builder_methods)
         | 
| 144 | 
            +
                    builder_methods = %i[real ignore_nil column] if builder_methods.empty?
         | 
| 145 | 
            +
                    builder = builder_methods.each_with_object(Builder.new) do |builder_method, builder|
         | 
| 146 | 
            +
                      builder.public_send(builder_method)
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
                    Badge.new(builder)
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
             | 
| 103 151 | 
             
                  def actions
         | 
| 104 152 | 
             
                    @actions_called = true
         | 
| 105 153 | 
             
                    Builder.new.computed.none.transform do
         | 
    
        data/lib/super/display.rb
    CHANGED
    
    | @@ -32,10 +32,11 @@ module Super | |
| 32 32 | 
             
                  yield(@fields, @schema_types)
         | 
| 33 33 | 
             
                end
         | 
| 34 34 |  | 
| 35 | 
            -
                def apply(action:)
         | 
| 35 | 
            +
                def apply(action:, format:)
         | 
| 36 36 | 
             
                  @action_inquirer = action
         | 
| 37 37 | 
             
                  return self if !@action_inquirer.index?
         | 
| 38 38 | 
             
                  return self if @schema_types.actions_called?
         | 
| 39 | 
            +
                  return self if !format.html?
         | 
| 39 40 | 
             
                  @fields[:actions] = @schema_types.actions
         | 
| 40 41 | 
             
                  self
         | 
| 41 42 | 
             
                end
         | 
    
        data/lib/super/error.rb
    CHANGED
    
    | @@ -28,6 +28,9 @@ module Super | |
| 28 28 | 
             
                # a more specific error
         | 
| 29 29 | 
             
                class Initalization < Error; end
         | 
| 30 30 | 
             
                class ArgumentError < Error; end
         | 
| 31 | 
            +
                class AlreadyRegistered < Error; end
         | 
| 32 | 
            +
                class AlreadyTranscribed < Error; end
         | 
| 33 | 
            +
                class NotImplementedError < Error; end
         | 
| 31 34 |  | 
| 32 35 | 
             
                class Enum < Error
         | 
| 33 36 | 
             
                  class ImpossibleValue < Enum; end
         | 
| @@ -3,58 +3,95 @@ | |
| 3 3 | 
             
            module Super
         | 
| 4 4 | 
             
              class Filter
         | 
| 5 5 | 
             
                class FormObject
         | 
| 6 | 
            -
                  class  | 
| 7 | 
            -
                    def initialize( | 
| 8 | 
            -
                      @ | 
| 6 | 
            +
                  class AttributeForm
         | 
| 7 | 
            +
                    def initialize(model:, field_name:, operators:, params:)
         | 
| 8 | 
            +
                      @model = model
         | 
| 9 9 | 
             
                      @field_name = field_name
         | 
| 10 | 
            -
                      @ | 
| 11 | 
            -
                      @params = params
         | 
| 10 | 
            +
                      @operators = operators
         | 
| 11 | 
            +
                      @params = params || {}
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    attr_reader :model
         | 
| 15 | 
            +
                    attr_reader :field_name
         | 
| 16 | 
            +
                    attr_reader :operators
         | 
| 17 | 
            +
                    attr_reader :params
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    def each_operator
         | 
| 20 | 
            +
                      return enum_for(:each_operator) if !block_given?
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      @operators.each do |operator|
         | 
| 23 | 
            +
                        operator_form = OperatorForm.new(
         | 
| 24 | 
            +
                          operator: operator,
         | 
| 25 | 
            +
                          params: @params[operator.identifier]
         | 
| 26 | 
            +
                        )
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                        yield(operator_form)
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    def humanized_attribute_name
         | 
| 33 | 
            +
                      @model.human_attribute_name(@field_name)
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  class OperatorForm
         | 
| 38 | 
            +
                    NULLARY = :_apply
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def initialize(operator:, params:)
         | 
| 41 | 
            +
                      @operator = operator
         | 
| 42 | 
            +
                      @params = params || {}
         | 
| 43 | 
            +
                      query_parameter_keys = operator.query_parameter_keys
         | 
| 44 | 
            +
                      query_parameter_keys = [NULLARY] if query_parameter_keys.empty?
         | 
| 12 45 | 
             
                      @specified_values =
         | 
| 13 | 
            -
                         | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
                              query_field_name,
         | 
| 17 | 
            -
                              (params || {})[query_field_name],
         | 
| 18 | 
            -
                            ]
         | 
| 19 | 
            -
                          end
         | 
| 20 | 
            -
                          .to_h
         | 
| 46 | 
            +
                        query_parameter_keys
         | 
| 47 | 
            +
                        .map { |key| [key, @params[key].presence&.strip] }
         | 
| 48 | 
            +
                        .to_h
         | 
| 21 49 |  | 
| 22 50 | 
             
                      @specified_values.each do |key, value|
         | 
| 23 51 | 
             
                        define_singleton_method(key) { value }
         | 
| 24 52 | 
             
                      end
         | 
| 25 53 | 
             
                    end
         | 
| 26 54 |  | 
| 27 | 
            -
                     | 
| 28 | 
            -
             | 
| 29 | 
            -
                     | 
| 55 | 
            +
                    def specified?
         | 
| 56 | 
            +
                      @specified_values.any? { |_key, value| value }
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    attr_reader :operator
         | 
| 30 60 | 
             
                    attr_reader :specified_values
         | 
| 31 61 |  | 
| 32 | 
            -
                    def  | 
| 33 | 
            -
                       | 
| 62 | 
            +
                    def identifier
         | 
| 63 | 
            +
                      @operator.identifier
         | 
| 34 64 | 
             
                    end
         | 
| 35 65 |  | 
| 36 | 
            -
                    def  | 
| 37 | 
            -
                       | 
| 38 | 
            -
                        .map { |o| [o.name, o.identifier] }
         | 
| 39 | 
            -
                        .to_h
         | 
| 40 | 
            -
                    end
         | 
| 66 | 
            +
                    def each_field
         | 
| 67 | 
            +
                      return enum_for(:each_field) if !block_given?
         | 
| 41 68 |  | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 69 | 
            +
                      @specified_values.each do |key, _value|
         | 
| 70 | 
            +
                        yield(key)
         | 
| 71 | 
            +
                      end
         | 
| 44 72 | 
             
                    end
         | 
| 45 73 | 
             
                  end
         | 
| 46 74 |  | 
| 47 75 | 
             
                  def initialize(model:, params:, schema:)
         | 
| 48 76 | 
             
                    @model = model
         | 
| 49 | 
            -
                    @params = params
         | 
| 77 | 
            +
                    @params = params || {}
         | 
| 50 78 | 
             
                    @schema = schema
         | 
| 51 79 |  | 
| 52 80 | 
             
                    @form_fields = {}
         | 
| 53 81 | 
             
                  end
         | 
| 54 82 |  | 
| 55 | 
            -
                  def  | 
| 56 | 
            -
                     | 
| 57 | 
            -
             | 
| 83 | 
            +
                  def each_attribute
         | 
| 84 | 
            +
                    return enum_for(:each_attribute) if !block_given?
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    @schema.fields.each do |field_name, field_operators|
         | 
| 87 | 
            +
                      attribute_form = AttributeForm.new(
         | 
| 88 | 
            +
                        model: @model,
         | 
| 89 | 
            +
                        field_name: field_name,
         | 
| 90 | 
            +
                        operators: field_operators,
         | 
| 91 | 
            +
                        params: @params[field_name]
         | 
| 92 | 
            +
                      )
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                      yield(attribute_form)
         | 
| 58 95 | 
             
                    end
         | 
| 59 96 | 
             
                  end
         | 
| 60 97 |  | 
| @@ -63,32 +100,21 @@ module Super | |
| 63 100 | 
             
                  end
         | 
| 64 101 |  | 
| 65 102 | 
             
                  def apply_changes(relation)
         | 
| 66 | 
            -
                     | 
| 67 | 
            -
                       | 
| 68 | 
            -
             | 
| 103 | 
            +
                    each_attribute do |attribute_form|
         | 
| 104 | 
            +
                      attribute_form.each_operator do |operator_form|
         | 
| 105 | 
            +
                        next if operator_form.specified_values.values.map(&:to_s).map(&:presence).none?
         | 
| 69 106 |  | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 107 | 
            +
                        operator_behavior = operator_form.operator.behavior
         | 
| 108 | 
            +
                        updated_relation = operator_behavior.call(relation, attribute_form.field_name, **operator_form.specified_values)
         | 
| 72 109 |  | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 110 | 
            +
                        if updated_relation.is_a?(ActiveRecord::Relation)
         | 
| 111 | 
            +
                          relation = updated_relation
         | 
| 112 | 
            +
                        end
         | 
| 75 113 | 
             
                      end
         | 
| 76 114 | 
             
                    end
         | 
| 77 115 |  | 
| 78 116 | 
             
                    relation
         | 
| 79 117 | 
             
                  end
         | 
| 80 | 
            -
             | 
| 81 | 
            -
                  private
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                  def form_field_for(field_name)
         | 
| 84 | 
            -
                    @form_fields[field_name] ||=
         | 
| 85 | 
            -
                      FilterFormField.new(
         | 
| 86 | 
            -
                        humanized_field_name: @model.human_attribute_name(field_name),
         | 
| 87 | 
            -
                        field_name: field_name,
         | 
| 88 | 
            -
                        type: @schema.fields[field_name],
         | 
| 89 | 
            -
                        params: (@params || {})[field_name]
         | 
| 90 | 
            -
                      )
         | 
| 91 | 
            -
                  end
         | 
| 92 118 | 
             
                end
         | 
| 93 119 | 
             
              end
         | 
| 94 120 | 
             
            end
         | 
    
        data/lib/super/filter/guesser.rb
    CHANGED
    
    
| @@ -2,100 +2,126 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Super
         | 
| 4 4 | 
             
              class Filter
         | 
| 5 | 
            -
                 | 
| 6 | 
            -
                  class Definition
         | 
| 7 | 
            -
                    def initialize(identifier, name, filter)
         | 
| 8 | 
            -
                      @identifier = identifier
         | 
| 9 | 
            -
                      @name = name
         | 
| 10 | 
            -
                      @filter = filter
         | 
| 11 | 
            -
                    end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                    attr_reader :identifier
         | 
| 14 | 
            -
                    attr_reader :name
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                    def filter(*args)
         | 
| 17 | 
            -
                      @filter.call(args)
         | 
| 18 | 
            -
                    end
         | 
| 19 | 
            -
                  end
         | 
| 20 | 
            -
             | 
| 5 | 
            +
                class Operator
         | 
| 21 6 | 
             
                  class << self
         | 
| 22 7 | 
             
                    def registry
         | 
| 23 8 | 
             
                      @registry ||= {}
         | 
| 24 9 | 
             
                    end
         | 
| 25 10 |  | 
| 26 | 
            -
                    def  | 
| 27 | 
            -
                       | 
| 28 | 
            -
                        registry["between"],
         | 
| 29 | 
            -
                      ]
         | 
| 11 | 
            +
                    def [](key)
         | 
| 12 | 
            +
                      registry.fetch(key.to_s)
         | 
| 30 13 | 
             
                    end
         | 
| 31 14 |  | 
| 32 | 
            -
                    def  | 
| 33 | 
            -
                       | 
| 34 | 
            -
             | 
| 35 | 
            -
                         | 
| 36 | 
            -
                       | 
| 15 | 
            +
                    def register(identifier, operator)
         | 
| 16 | 
            +
                      identifier = identifier.to_s
         | 
| 17 | 
            +
                      if registry.key?(identifier)
         | 
| 18 | 
            +
                        raise Error::AlreadyRegistered, "Already registered: #{identifier}"
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      registry[identifier] = operator
         | 
| 37 22 | 
             
                    end
         | 
| 38 23 |  | 
| 39 | 
            -
                    def  | 
| 40 | 
            -
                       | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
                        registry["contain"],
         | 
| 44 | 
            -
                        registry["ncontain"],
         | 
| 45 | 
            -
                        registry["start"],
         | 
| 46 | 
            -
                        registry["end"],
         | 
| 47 | 
            -
                      ]
         | 
| 24 | 
            +
                    def define(identifier, display, &block)
         | 
| 25 | 
            +
                      operator = new(identifier, display, &block).freeze
         | 
| 26 | 
            +
                      register(identifier, operator)
         | 
| 27 | 
            +
                      operator
         | 
| 48 28 | 
             
                    end
         | 
| 29 | 
            +
                  end
         | 
| 49 30 |  | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 31 | 
            +
                  def initialize(identifier, display, &behavior)
         | 
| 32 | 
            +
                    @identifier = identifier.to_s
         | 
| 33 | 
            +
                    @humanized_operator_name = display
         | 
| 34 | 
            +
                    self.behavior = behavior
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def behavior=(behavior)
         | 
| 38 | 
            +
                    behavior_params = behavior.parameters
         | 
| 39 | 
            +
                    if behavior_params.size < 2
         | 
| 40 | 
            +
                      raise Error::ArgumentError, "Operator behavior must include `column_name` and `relation`"
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                    if behavior_params[0][0] != :req && behavior_params[0][0] != :opt
         | 
| 43 | 
            +
                      raise Error::ArgumentError, "First argument `relation` must be a required, positional argument"
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                    if behavior_params[1][0] != :req && behavior_params[1][0] != :opt
         | 
| 46 | 
            +
                      raise Error::ArgumentError, "Second argument `column_name` must be a required, positional argument"
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    if !behavior_params[2..-1].all? { |(type, _name)| type == :keyreq }
         | 
| 49 | 
            +
                      raise Error::ArgumentError, "All query parameter keys must be required, keyword arguments"
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                    @behavior = behavior
         | 
| 52 | 
            +
                    @query_parameter_keys = behavior_params[2..-1].map(&:last)
         | 
| 53 | 
            +
                  end
         | 
| 53 54 |  | 
| 54 | 
            -
             | 
| 55 | 
            +
                  attr_reader :identifier
         | 
| 56 | 
            +
                  attr_reader :query_parameter_keys
         | 
| 57 | 
            +
                  attr_reader :humanized_operator_name
         | 
| 55 58 |  | 
| 56 | 
            -
             | 
| 59 | 
            +
                  def behavior(&block)
         | 
| 60 | 
            +
                    self.behavior = block if block_given?
         | 
| 61 | 
            +
                    @behavior
         | 
| 62 | 
            +
                  end
         | 
| 57 63 |  | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
                      end
         | 
| 61 | 
            -
                    end
         | 
| 64 | 
            +
                  define("eq", "Equals") do |relation, field, q:|
         | 
| 65 | 
            +
                    relation.where(field => q)
         | 
| 62 66 | 
             
                  end
         | 
| 63 67 |  | 
| 64 | 
            -
                  define(" | 
| 65 | 
            -
                    relation.where(field =>  | 
| 68 | 
            +
                  define("neq", "Doesn't equal") do |relation, field, q:|
         | 
| 69 | 
            +
                    relation.where.not(field => q)
         | 
| 66 70 | 
             
                  end
         | 
| 67 71 |  | 
| 68 | 
            -
                  define(" | 
| 69 | 
            -
                    relation.where | 
| 72 | 
            +
                  define("null", "Is NULL") do |relation, field|
         | 
| 73 | 
            +
                    relation.where(field => nil)
         | 
| 70 74 | 
             
                  end
         | 
| 71 75 |  | 
| 72 | 
            -
                  define(" | 
| 73 | 
            -
                     | 
| 74 | 
            -
                    relation.where("#{field} LIKE ?", "%#{query}%")
         | 
| 76 | 
            +
                  define("nnull", "Isn't NULL") do |relation, field|
         | 
| 77 | 
            +
                    relation.where.not(field => nil)
         | 
| 75 78 | 
             
                  end
         | 
| 76 79 |  | 
| 77 | 
            -
                  define(" | 
| 78 | 
            -
                     | 
| 79 | 
            -
             | 
| 80 | 
            +
                  define("true", "Is true") do |relation, field|
         | 
| 81 | 
            +
                    relation.where(field => true)
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  define("false", "Is false") do |relation, field|
         | 
| 85 | 
            +
                    relation.where(field => false)
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  define("empty", "Is empty") do |relation, field|
         | 
| 89 | 
            +
                    relation.where(field => "")
         | 
| 80 90 | 
             
                  end
         | 
| 81 91 |  | 
| 82 | 
            -
                  define(" | 
| 83 | 
            -
                     | 
| 84 | 
            -
                    relation.where("#{field} LIKE ?", query)
         | 
| 92 | 
            +
                  define("nempty", "Isn't empty") do |relation, field|
         | 
| 93 | 
            +
                    relation.where.not(field => "")
         | 
| 85 94 | 
             
                  end
         | 
| 86 95 |  | 
| 87 | 
            -
                  define(" | 
| 88 | 
            -
                     | 
| 89 | 
            -
             | 
| 96 | 
            +
                  define("blank", "Is blank") do |relation, field|
         | 
| 97 | 
            +
                    relation.where(field => [nil, ""])
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  define("nblank", "Isn't blank") do |relation, field|
         | 
| 101 | 
            +
                    relation.where.not(field => [nil, ""])
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  define("contain", "Contains") do |relation, field, q:|
         | 
| 105 | 
            +
                    query = "%#{Compatability.sanitize_sql_like(q)}%"
         | 
| 106 | 
            +
                    if relation.connection.adapter_name == "PostgreSQL"
         | 
| 107 | 
            +
                      relation.where("#{field} ILIKE ?", query)
         | 
| 108 | 
            +
                    else
         | 
| 109 | 
            +
                      relation.where("#{field} LIKE ?", query)
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  define("ncontain", "Doesn't contain") do |relation, field, q:|
         | 
| 114 | 
            +
                    query = "%#{Compatability.sanitize_sql_like(q)}%"
         | 
| 115 | 
            +
                    relation.where("#{field} NOT LIKE ?", query)
         | 
| 90 116 | 
             
                  end
         | 
| 91 117 |  | 
| 92 | 
            -
                  define("between", " | 
| 93 | 
            -
                    if  | 
| 94 | 
            -
                      relation = relation.where("#{field} >= ?",  | 
| 118 | 
            +
                  define("between", "Between") do |relation, field, q0:, q1:|
         | 
| 119 | 
            +
                    if q0.present?
         | 
| 120 | 
            +
                      relation = relation.where("#{field} >= ?", q0)
         | 
| 95 121 | 
             
                    end
         | 
| 96 122 |  | 
| 97 | 
            -
                    if  | 
| 98 | 
            -
                      relation = relation.where("#{field} <= ?",  | 
| 123 | 
            +
                    if q1.present?
         | 
| 124 | 
            +
                      relation = relation.where("#{field} <= ?", q1)
         | 
| 99 125 | 
             
                    end
         | 
| 100 126 |  | 
| 101 127 | 
             
                    relation
         |