active_element 0.0.18 → 0.0.20
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/Gemfile.lock +6 -6
- data/README.md +2 -28
- data/app/controllers/active_element/application_controller.rb +2 -2
- data/app/views/active_element/components/button.html.erb +1 -1
- data/app/views/active_element/components/form.html.erb +16 -6
- data/app/views/active_element/components/table/_collection_row.html.erb +1 -1
- data/app/views/active_element/components/table/collection.html.erb +6 -1
- data/app/views/active_element/default_views/edit.html.erb +2 -2
- data/app/views/active_element/default_views/index.html.erb +13 -2
- data/app/views/active_element/default_views/show.html.erb +2 -2
- data/app/views/layouts/active_element.html.erb +9 -1
- data/config/routes.rb +1 -0
- data/example_app/Gemfile.lock +1 -1
- data/lib/active_element/components/button.rb +18 -3
- data/lib/active_element/components/collection_table.rb +4 -2
- data/lib/active_element/components/form.rb +19 -3
- data/lib/active_element/components/util/default_display_value.rb +4 -0
- data/lib/active_element/components/util/form_field_mapping.rb +5 -2
- data/lib/active_element/components/util/record_mapping.rb +10 -1
- data/lib/active_element/controller_interface.rb +2 -1
- data/lib/active_element/controller_state.rb +1 -1
- data/lib/active_element/default_controller/controller.rb +37 -3
- data/lib/active_element/default_controller/search.rb +15 -1
- data/lib/active_element/field_options.rb +3 -3
- data/lib/active_element/version.rb +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 667ecfa2f26f661d4c0f760529fdc4ae944e242fdefecba10d6cc5b194b1e822
         | 
| 4 | 
            +
              data.tar.gz: 28f0326bb18b3bf7fb402a5b89e82c796621825779fdaa2a754817e5028fe8e0
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 1d7a79658f6ec7d58e2aac0492fe952d7df2c6d4873f2a4ad9390735b3f5c868947bb9722c92ec4bb640bf2909c3ad064e3ecaf7fccd5c07992810d9ee3344ee
         | 
| 7 | 
            +
              data.tar.gz: d48338e4f22be093bf6b3c6ef3ca2acce3150a3db3fde1b715408b4f8de3ce41f2c6a3074982b4426fc53de6f5609b5de443f8192c5b602008f37abe1cbf0621
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                active_element (0.0. | 
| 4 | 
            +
                active_element (0.0.20)
         | 
| 5 5 | 
             
                  bootstrap (~> 5.3.0alpha3)
         | 
| 6 6 | 
             
                  kaminari (~> 1.2)
         | 
| 7 7 | 
             
                  paintbrush (~> 0.1.2)
         | 
| @@ -83,7 +83,7 @@ GEM | |
| 83 83 | 
             
                autoprefixer-rails (10.4.16.0)
         | 
| 84 84 | 
             
                  execjs (~> 2)
         | 
| 85 85 | 
             
                bcrypt (3.1.18)
         | 
| 86 | 
            -
                bootstrap (5.3. | 
| 86 | 
            +
                bootstrap (5.3.3)
         | 
| 87 87 | 
             
                  autoprefixer-rails (>= 9.1.0)
         | 
| 88 88 | 
             
                  popper_js (>= 2.11.8, < 3)
         | 
| 89 89 | 
             
                brakeman (5.4.1)
         | 
| @@ -145,21 +145,21 @@ GEM | |
| 145 145 | 
             
                  net-imap
         | 
| 146 146 | 
             
                  net-pop
         | 
| 147 147 | 
             
                  net-smtp
         | 
| 148 | 
            -
                marcel (1.0. | 
| 148 | 
            +
                marcel (1.0.4)
         | 
| 149 149 | 
             
                method_source (1.0.0)
         | 
| 150 150 | 
             
                mini_mime (1.1.5)
         | 
| 151 151 | 
             
                mini_portile2 (2.8.2)
         | 
| 152 152 | 
             
                minitest (5.18.1)
         | 
| 153 | 
            -
                net-imap (0.4. | 
| 153 | 
            +
                net-imap (0.4.11)
         | 
| 154 154 | 
             
                  date
         | 
| 155 155 | 
             
                  net-protocol
         | 
| 156 156 | 
             
                net-pop (0.1.2)
         | 
| 157 157 | 
             
                  net-protocol
         | 
| 158 158 | 
             
                net-protocol (0.2.2)
         | 
| 159 159 | 
             
                  timeout
         | 
| 160 | 
            -
                net-smtp (0. | 
| 160 | 
            +
                net-smtp (0.5.0)
         | 
| 161 161 | 
             
                  net-protocol
         | 
| 162 | 
            -
                nio4r (2.7. | 
| 162 | 
            +
                nio4r (2.7.3)
         | 
| 163 163 | 
             
                nokogiri (1.15.2)
         | 
| 164 164 | 
             
                  mini_portile2 (~> 2.8.2)
         | 
| 165 165 | 
             
                  racc (~> 1.4)
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,31 +1,5 @@ | |
| 1 1 | 
             
            # ActiveElement
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 3 | 
            +
            ActiveElement is a framework for building admin applications in Rails.
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
            ## Installation
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
         | 
| 10 | 
            -
             | 
| 11 | 
            -
            Install the gem and add to the application's Gemfile by executing:
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
         | 
| 14 | 
            -
             | 
| 15 | 
            -
            If bundler is not being used to manage dependencies, install the gem by executing:
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            ## Usage
         | 
| 20 | 
            -
             | 
| 21 | 
            -
            TODO: Write usage instructions here
         | 
| 22 | 
            -
             | 
| 23 | 
            -
            ## Development
         | 
| 24 | 
            -
             | 
| 25 | 
            -
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         | 
| 26 | 
            -
             | 
| 27 | 
            -
            To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
         | 
| 28 | 
            -
             | 
| 29 | 
            -
            ## Contributing
         | 
| 30 | 
            -
             | 
| 31 | 
            -
            Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_element.
         | 
| 5 | 
            +
            See the [documentation](https://docs.bob.frl/active_element) to get started.
         | 
| @@ -27,8 +27,8 @@ module ActiveElement | |
| 27 27 | 
             
                helper_method :active_element
         | 
| 28 28 | 
             
                helper_method :render_active_element_hook
         | 
| 29 29 |  | 
| 30 | 
            -
                def render_active_element_hook(hook)
         | 
| 31 | 
            -
                  render_to_string partial: hook
         | 
| 30 | 
            +
                def render_active_element_hook(hook, locals: {})
         | 
| 31 | 
            +
                  render_to_string partial: hook, locals: locals
         | 
| 32 32 | 
             
                rescue ActionView::MissingTemplate
         | 
| 33 33 | 
             
                  nil
         | 
| 34 34 | 
             
                end
         | 
| @@ -59,13 +59,23 @@ | |
| 59 59 | 
             
                <% fields.each_slice(columns) do |field_group| %>
         | 
| 60 60 | 
             
                  <div class="row form-fields mb-3">
         | 
| 61 61 | 
             
                    <% field_group.each do |field, type, options| %>
         | 
| 62 | 
            -
                       | 
| 63 | 
            -
                         | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 62 | 
            +
                      <% if type != :hidden_field %>
         | 
| 63 | 
            +
                        <div class="col-sm-3">
         | 
| 64 | 
            +
                          <%= render partial: 'active_element/components/form/label',
         | 
| 65 | 
            +
                                     locals: {
         | 
| 66 | 
            +
                                       component: component,
         | 
| 67 | 
            +
                                       id: id,
         | 
| 68 | 
            +
                                       type: type,
         | 
| 69 | 
            +
                                       form: form,
         | 
| 70 | 
            +
                                       field: field,
         | 
| 71 | 
            +
                                       options: options
         | 
| 72 | 
            +
                                     } %>
         | 
| 73 | 
            +
                        </div>
         | 
| 74 | 
            +
                      <% end %>
         | 
| 66 75 |  | 
| 67 76 |  | 
| 68 | 
            -
             | 
| 77 | 
            +
             | 
| 78 | 
            +
                      <% if type != :hidden_field %><div class="col"><% end %>
         | 
| 69 79 | 
             
                        <%= render partial: 'active_element/components/form/field',
         | 
| 70 80 | 
             
                                   locals: {
         | 
| 71 81 | 
             
                                     id: id,
         | 
| @@ -76,7 +86,7 @@ | |
| 76 86 | 
             
                                     component: component,
         | 
| 77 87 | 
             
                                     record: record }
         | 
| 78 88 | 
             
                        %>
         | 
| 79 | 
            -
                       | 
| 89 | 
            +
                      <% if type != :hidden_field %></div><% end %>
         | 
| 80 90 | 
             
                    <% end %>
         | 
| 81 91 | 
             
                  </div>
         | 
| 82 92 | 
             
                <% end %>
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            <tr class="<%= (index % 2).zero? ? 'even' : 'odd' %> <%= row_class_mapper.call(item) %>">
         | 
| 2 2 | 
             
              <% fields.each do |field, class_mapper, label, value_mapper| %>
         | 
| 3 | 
            -
                <td class="align- | 
| 3 | 
            +
                <td class="align-top <%= class_mapper.call(item) %>">
         | 
| 4 4 | 
             
                  <% if component.secret_field?(field) %>
         | 
| 5 5 | 
             
                    <%= controller.helpers.render partial: 'active_element/components/secret/field',
         | 
| 6 6 | 
             
                                                  locals: { secret: value_mapper.call(item), label: label } %>
         | 
| @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            <%= active_element.component.page_title record.model_name.to_s.titleize %>
         | 
| 2 2 |  | 
| 3 | 
            -
            <%= render_active_element_hook "#{controller_path}/before_edit" %>
         | 
| 3 | 
            +
            <%= render_active_element_hook "#{controller_path}/before_edit", locals: { record: record } %>
         | 
| 4 4 |  | 
| 5 5 | 
             
            <%= active_element.component.form model: [namespace, record].compact,
         | 
| 6 6 | 
             
                                              destroy: active_element.state.deletable?,
         | 
| 7 7 | 
             
                                              fields: active_element.state.editable_fields %>
         | 
| 8 8 |  | 
| 9 | 
            -
            <%= render_active_element_hook "#{controller_path}/after_edit" %>
         | 
| 9 | 
            +
            <%= render_active_element_hook "#{controller_path}/after_edit", locals: { record: record } %>
         | 
| @@ -7,7 +7,17 @@ | |
| 7 7 | 
             
                                                fields: active_element.state.searchable_fields %>
         | 
| 8 8 | 
             
            <% end %>
         | 
| 9 9 |  | 
| 10 | 
            -
            <%= render_active_element_hook "#{controller_path}/before_index" %>
         | 
| 10 | 
            +
            <%= render_active_element_hook "#{controller_path}/before_index", locals: { collection: collection } %>
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            <% if nested_for.present? %>
         | 
| 13 | 
            +
              <%=
         | 
| 14 | 
            +
                active_element.component.page_section_title(
         | 
| 15 | 
            +
                  nested_for.map do |nested_for_record|
         | 
| 16 | 
            +
                    ActiveElement::Components::Util::DefaultDisplayValue.new(object: nested_for_record).value
         | 
| 17 | 
            +
                  end.join(', ')
         | 
| 18 | 
            +
                )
         | 
| 19 | 
            +
                %>
         | 
| 20 | 
            +
            <% end %>
         | 
| 11 21 |  | 
| 12 22 | 
             
            <% if active_element.state.search_required && search_filters.compact_blank.blank? %>
         | 
| 13 23 | 
             
              <% if active_element.state.creatable? %>
         | 
| @@ -20,8 +30,9 @@ | |
| 20 30 | 
             
                                                 show: active_element.state.viewable?,
         | 
| 21 31 | 
             
                                                 edit: active_element.state.editable?,
         | 
| 22 32 | 
             
                                                 destroy: active_element.state.deletable?,
         | 
| 33 | 
            +
                                                 nested_for: nested_for,
         | 
| 23 34 | 
             
                                                 collection: collection,
         | 
| 24 35 | 
             
                                                 fields: active_element.state.listable_fields %>
         | 
| 25 36 | 
             
            <% end %>
         | 
| 26 37 |  | 
| 27 | 
            -
            <%= render_active_element_hook "#{controller_path}/after_index" %>
         | 
| 38 | 
            +
            <%= render_active_element_hook "#{controller_path}/after_index", locals: { collection: collection } %>
         | 
| @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            <%= active_element.component.page_title record.model_name.to_s.titleize %>
         | 
| 2 2 |  | 
| 3 | 
            -
            <%= render_active_element_hook "#{controller_path}/before_show" %>
         | 
| 3 | 
            +
            <%= render_active_element_hook "#{controller_path}/before_show", locals: { record: record } %>
         | 
| 4 4 |  | 
| 5 5 | 
             
            <%= active_element.component.table item: record,
         | 
| 6 6 | 
             
                                               edit: active_element.state.editable?,
         | 
| 7 7 | 
             
                                               destroy: active_element.state.deletable?,
         | 
| 8 8 | 
             
                                               fields: active_element.state.viewable_fields %>
         | 
| 9 9 |  | 
| 10 | 
            -
            <%= render_active_element_hook "#{controller_path}/after_show" %>
         | 
| 10 | 
            +
            <%= render_active_element_hook "#{controller_path}/after_show", locals: { record: record } %>
         | 
| @@ -19,6 +19,15 @@ | |
| 19 19 | 
             
                  </script>
         | 
| 20 20 | 
             
                <% end %>
         | 
| 21 21 |  | 
| 22 | 
            +
                <% if respond_to?(:javascript_pack_tag) && defined? Webpacker %>
         | 
| 23 | 
            +
                  <%= begin
         | 
| 24 | 
            +
                        javascript_pack_tag 'application'
         | 
| 25 | 
            +
                      rescue Webpacker::Manifest::MissingEntryError
         | 
| 26 | 
            +
                        nil
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
                    %>
         | 
| 29 | 
            +
                <% end %>
         | 
| 30 | 
            +
             | 
| 22 31 | 
             
                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
         | 
| 23 32 | 
             
                <link rel="stylesheet"
         | 
| 24 33 | 
             
                      href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css">
         | 
| @@ -116,6 +125,5 @@ | |
| 116 125 | 
             
                  <%= javascript_include_tag 'active_element/active_element', 'data-turbo-track': 'reload', 'data-turbolinks-track': 'reload' %>
         | 
| 117 126 | 
             
                  <%= javascript_include_tag 'application', 'data-turbo-track': 'reload', 'data-turbolinks-track': 'reload' %>
         | 
| 118 127 | 
             
                <% end %>
         | 
| 119 | 
            -
             | 
| 120 128 | 
             
              </body>
         | 
| 121 129 | 
             
            </html>
         | 
    
        data/config/routes.rb
    CHANGED
    
    
    
        data/example_app/Gemfile.lock
    CHANGED
    
    
| @@ -6,7 +6,7 @@ module ActiveElement | |
| 6 6 | 
             
                class Button
         | 
| 7 7 | 
             
                  # rubocop:disable Metrics/MethodLength
         | 
| 8 8 | 
             
                  def initialize(controller, record, flag_or_options, confirm: false, type: :primary, method: nil,
         | 
| 9 | 
            -
                                 float: nil, icon: nil, tooltip: false, **kwargs, &block)
         | 
| 9 | 
            +
                                 float: nil, icon: nil, tooltip: false, nested_for: nil, **kwargs, &block)
         | 
| 10 10 | 
             
                    @controller = controller
         | 
| 11 11 | 
             
                    @record = record.is_a?(ActiveRecord::Relation) ? record.klass.new : record
         | 
| 12 12 | 
             
                    @flag_or_options = flag_or_options
         | 
| @@ -20,6 +20,7 @@ module ActiveElement | |
| 20 20 | 
             
                    @block_given = block_given?
         | 
| 21 21 | 
             
                    @content = block.call if block_given?
         | 
| 22 22 | 
             
                    @tooltip = tooltip
         | 
| 23 | 
            +
                    @nested_for = nested_for
         | 
| 23 24 | 
             
                  end
         | 
| 24 25 | 
             
                  # rubocop:enable Metrics/MethodLength
         | 
| 25 26 |  | 
| @@ -49,7 +50,7 @@ module ActiveElement | |
| 49 50 | 
             
                  private
         | 
| 50 51 |  | 
| 51 52 | 
             
                  attr_reader :controller, :record, :flag_or_options, :float, :kwargs, :kwargs_class, :type, :method, :icon,
         | 
| 52 | 
            -
                              :block_given, :content, :confirm, :tooltip
         | 
| 53 | 
            +
                              :block_given, :content, :confirm, :tooltip, :nested_for
         | 
| 53 54 |  | 
| 54 55 | 
             
                  def link_method
         | 
| 55 56 | 
             
                    return method if method.present?
         | 
| @@ -116,7 +117,21 @@ module ActiveElement | |
| 116 117 | 
             
                  def record_path
         | 
| 117 118 | 
             
                    return nil unless record.class.is_a?(ActiveModel::Naming)
         | 
| 118 119 |  | 
| 119 | 
            -
                    Util::RecordPath.new(record: record, controller: controller, type: type).path
         | 
| 120 | 
            +
                    Util::RecordPath.new(record: record, controller: controller, type: type).path(**nested_args)
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                  def nested_args
         | 
| 124 | 
            +
                    case type
         | 
| 125 | 
            +
                    when :new
         | 
| 126 | 
            +
                      nested_params
         | 
| 127 | 
            +
                    else
         | 
| 128 | 
            +
                      {}
         | 
| 129 | 
            +
                    end
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  def nested_params
         | 
| 133 | 
            +
                    route = controller.request.routes.recognize_path(controller.request.path)
         | 
| 134 | 
            +
                    route.reject { |key, _value| %w[controller action].include?(key.to_s) }
         | 
| 120 135 | 
             
                  end
         | 
| 121 136 | 
             
                end
         | 
| 122 137 | 
             
              end
         | 
| @@ -15,7 +15,7 @@ module ActiveElement | |
| 15 15 | 
             
                  # rubocop:disable Metrics/MethodLength
         | 
| 16 16 | 
             
                  def initialize(controller, class_name:, collection:, fields:, params:, model_name: nil, style: nil,
         | 
| 17 17 | 
             
                                 show: false, new: false, edit: false, destroy: false, paginate: true, group: nil,
         | 
| 18 | 
            -
                                 group_title: false, row_class: nil, title: nil, **_kwargs)
         | 
| 18 | 
            +
                                 group_title: false, nested_for: nil, row_class: nil, title: nil, **_kwargs)
         | 
| 19 19 | 
             
                    @controller = controller
         | 
| 20 20 | 
             
                    @class_name = class_name
         | 
| 21 21 | 
             
                    @model_name = model_name
         | 
| @@ -32,6 +32,7 @@ module ActiveElement | |
| 32 32 | 
             
                    @group_title = group_title
         | 
| 33 33 | 
             
                    @row_class = row_class
         | 
| 34 34 | 
             
                    @title = title
         | 
| 35 | 
            +
                    @nested_for = nested_for
         | 
| 35 36 | 
             
                    verify_paginate_and_group
         | 
| 36 37 | 
             
                  end
         | 
| 37 38 | 
             
                  # rubocop:enable Metrics/MethodLength
         | 
| @@ -54,6 +55,7 @@ module ActiveElement | |
| 54 55 | 
             
                      destroy: destroy,
         | 
| 55 56 | 
             
                      group: group,
         | 
| 56 57 | 
             
                      group_title: group_title,
         | 
| 58 | 
            +
                      nested_for: nested_for,
         | 
| 57 59 | 
             
                      display_pagination: display_pagination?,
         | 
| 58 60 | 
             
                      page_sizes: [5, 10, 25, 50, 75, 100, 200],
         | 
| 59 61 | 
             
                      page_size: page_size,
         | 
| @@ -78,7 +80,7 @@ module ActiveElement | |
| 78 80 |  | 
| 79 81 | 
             
                  attr_reader :class_name, :collection, :fields, :style, :row_class,
         | 
| 80 82 | 
             
                              :new, :show, :edit, :destroy,
         | 
| 81 | 
            -
                              :paginate, :params, :group, :group_title, :title
         | 
| 83 | 
            +
                              :paginate, :params, :group, :group_title, :title, :nested_for
         | 
| 82 84 |  | 
| 83 85 | 
             
                  def paginated_collection
         | 
| 84 86 | 
             
                    return collection unless paginate && collection.respond_to?(:page) && !limit?
         | 
| @@ -190,14 +190,30 @@ module ActiveElement | |
| 190 190 | 
             
                  end
         | 
| 191 191 |  | 
| 192 192 | 
             
                  def base_options_for_select(field, field_options)
         | 
| 193 | 
            -
                    return normalized_options(field_options.fetch(:options)) if field_options.key?(:options)
         | 
| 193 | 
            +
                    return normalized_options(field_options.fetch(:options), field_options) if field_options.key?(:options)
         | 
| 194 194 | 
             
                    return default_options_for_select(field, field_options) if record.class.is_a?(ActiveModel::Naming)
         | 
| 195 195 |  | 
| 196 196 | 
             
                    raise ArgumentError, "Must provide select options `[:#{field}, { options: [...] }]` or a record instance."
         | 
| 197 197 | 
             
                  end
         | 
| 198 198 |  | 
| 199 | 
            -
                  def normalized_options(options)
         | 
| 200 | 
            -
                    options.map  | 
| 199 | 
            +
                  def normalized_options(options, field_options)
         | 
| 200 | 
            +
                    options.map do |option|
         | 
| 201 | 
            +
                      next option if option.is_a?(Array)
         | 
| 202 | 
            +
                      next active_record_option(option, field_options) if option.is_a?(ActiveRecord::Base)
         | 
| 203 | 
            +
                      [option, option] if option.is_a?(String)
         | 
| 204 | 
            +
                    end
         | 
| 205 | 
            +
                  end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                  def active_record_option(option, field_options)
         | 
| 208 | 
            +
                    [active_record_display_value(option, field_options), option.send(option.class.primary_key)]
         | 
| 209 | 
            +
                  end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                  def active_record_display_value(option, field_options)
         | 
| 212 | 
            +
                    if field_options[:display_value].is_a?(Proc) && record.present?
         | 
| 213 | 
            +
                      field_options[:display_value].call(option)
         | 
| 214 | 
            +
                    else
         | 
| 215 | 
            +
                      Util::DefaultDisplayValue.new(object: option).value
         | 
| 216 | 
            +
                    end
         | 
| 201 217 | 
             
                  end
         | 
| 202 218 |  | 
| 203 219 | 
             
                  def default_class_name
         | 
| @@ -12,6 +12,10 @@ module ActiveElement | |
| 12 12 | 
             
                    end
         | 
| 13 13 |  | 
| 14 14 | 
             
                    def value
         | 
| 15 | 
            +
                      if object.respond_to?(:default_display_attribute)
         | 
| 16 | 
            +
                        return object.public_send(object.default_display_attribute)
         | 
| 17 | 
            +
                      end
         | 
| 18 | 
            +
             | 
| 15 19 | 
             
                      DEFAULT_FIELDS.each do |field|
         | 
| 16 20 | 
             
                        return object.public_send(field) if active_record_value?(field)
         | 
| 17 21 | 
             
                        return object[field] if hash_key(field) if hash_value?(field)
         | 
| @@ -59,10 +59,12 @@ module ActiveElement | |
| 59 59 | 
             
                    end
         | 
| 60 60 |  | 
| 61 61 | 
             
                    def inline_configured_field(field)
         | 
| 62 | 
            -
                      field_options = FieldOptions.from_state( | 
| 62 | 
            +
                      field_options = FieldOptions.from_state(
         | 
| 63 | 
            +
                        field, controller.active_element.state, record, controller
         | 
| 64 | 
            +
                      )
         | 
| 63 65 | 
             
                      return nil if field_options.blank?
         | 
| 64 66 |  | 
| 65 | 
            -
                      [field, field_options.type, field_options.options]
         | 
| 67 | 
            +
                      [field, field_options.type, field_options.options.reverse_merge({ value: field_options.value })]
         | 
| 66 68 | 
             
                    end
         | 
| 67 69 |  | 
| 68 70 | 
             
                    def field_with_provided_type_and_provided_options(field)
         | 
| @@ -201,6 +203,7 @@ module ActiveElement | |
| 201 203 | 
             
                        json: :json_field,
         | 
| 202 204 | 
             
                        jsonb: :json_field,
         | 
| 203 205 | 
             
                        geometry: :text_area,
         | 
| 206 | 
            +
                        text: :text_area,
         | 
| 204 207 | 
             
                        datetime: :datetime_field,
         | 
| 205 208 | 
             
                        date: :date_field,
         | 
| 206 209 | 
             
                        time: :time_field,
         | 
| @@ -80,13 +80,22 @@ module ActiveElement | |
| 80 80 | 
             
                    end
         | 
| 81 81 |  | 
| 82 82 | 
             
                    def value_from_config
         | 
| 83 | 
            -
                      field_options =  | 
| 83 | 
            +
                      field_options = field_options_from_state
         | 
| 84 84 | 
             
                      return nil if field_options.blank?
         | 
| 85 85 | 
             
                      return nil unless DATABASE_TYPES.include?(field_options.type.to_sym)
         | 
| 86 86 |  | 
| 87 87 | 
             
                      send("#{field_options.type}_value")
         | 
| 88 88 | 
             
                    end
         | 
| 89 89 |  | 
| 90 | 
            +
                    def field_options_from_state
         | 
| 91 | 
            +
                      FieldOptions.from_state(
         | 
| 92 | 
            +
                        field,
         | 
| 93 | 
            +
                        component.controller.active_element.state,
         | 
| 94 | 
            +
                        record,
         | 
| 95 | 
            +
                        component.controller
         | 
| 96 | 
            +
                      )
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
             | 
| 90 99 | 
             
                    # Override these methods as required in a class that includes this module:
         | 
| 91 100 |  | 
| 92 101 | 
             
                    def mapped_association_from_record
         | 
| @@ -25,8 +25,9 @@ module ActiveElement | |
| 25 25 | 
             
                  @authorize
         | 
| 26 26 | 
             
                end
         | 
| 27 27 |  | 
| 28 | 
            -
                def listable_fields(*args, order: nil)
         | 
| 28 | 
            +
                def listable_fields(*args, order: nil, scope: nil)
         | 
| 29 29 | 
             
                  state.list_order = order
         | 
| 30 | 
            +
                  state.list_scope = scope
         | 
| 30 31 | 
             
                  state.listable_fields.concat(args.map(&:to_sym)).uniq!
         | 
| 31 32 | 
             
                end
         | 
| 32 33 |  | 
| @@ -8,7 +8,7 @@ module ActiveElement | |
| 8 8 | 
             
                attr_reader :permissions, :listable_fields, :viewable_fields, :editable_fields, :searchable_fields,
         | 
| 9 9 | 
             
                            :field_options
         | 
| 10 10 | 
             
                attr_accessor :sign_in_path, :sign_in, :sign_in_method, :sign_out_path, :sign_out_method,
         | 
| 11 | 
            -
                              :deletable, :authorizor, :authenticator, :list_order, :search_required, :model
         | 
| 11 | 
            +
                              :deletable, :authorizor, :authenticator, :list_order, :list_scope, :search_required, :model
         | 
| 12 12 |  | 
| 13 13 | 
             
                def initialize(controller:)
         | 
| 14 14 | 
             
                  @controller = controller
         | 
| @@ -15,7 +15,8 @@ module ActiveElement | |
| 15 15 | 
             
                    controller.render 'active_element/default_views/index',
         | 
| 16 16 | 
             
                                      locals: {
         | 
| 17 17 | 
             
                                        collection: ordered(collection),
         | 
| 18 | 
            -
                                        search_filters: default_text_search.search_filters
         | 
| 18 | 
            +
                                        search_filters: default_text_search.search_filters,
         | 
| 19 | 
            +
                                        nested_for: nested_relations
         | 
| 19 20 | 
             
                                      }
         | 
| 20 21 | 
             
                  end
         | 
| 21 22 |  | 
| @@ -124,9 +125,12 @@ module ActiveElement | |
| 124 125 | 
             
                  end
         | 
| 125 126 |  | 
| 126 127 | 
             
                  def collection
         | 
| 127 | 
            -
                    return model. | 
| 128 | 
            +
                    return model.public_send(list_scope).where(nested_scope) unless default_text_search.text_search?
         | 
| 128 129 |  | 
| 129 | 
            -
                    model. | 
| 130 | 
            +
                    model.public_send(list_scope)
         | 
| 131 | 
            +
                         .left_outer_joins(default_text_search.search_relations)
         | 
| 132 | 
            +
                         .where(nested_scope)
         | 
| 133 | 
            +
                         .where(*default_text_search.text_search)
         | 
| 130 134 | 
             
                  end
         | 
| 131 135 |  | 
| 132 136 | 
             
                  def render_range_error(error:, action:)
         | 
| @@ -140,6 +144,36 @@ module ActiveElement | |
| 140 144 |  | 
| 141 145 | 
             
                    I18n.t('active_element.unexpected_error')
         | 
| 142 146 | 
             
                  end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  def list_scope
         | 
| 149 | 
            +
                    return :all if state.list_scope.blank?
         | 
| 150 | 
            +
                    return state.list_scope.call(request) if state.list_scope.is_a?(Proc)
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                    state.list_scope
         | 
| 153 | 
            +
                  end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  def nested_scope
         | 
| 156 | 
            +
                    nested_params.presence || noop
         | 
| 157 | 
            +
                  end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                  def noop
         | 
| 160 | 
            +
                    Arel::Nodes::True.new.eq(Arel::Nodes::True.new)
         | 
| 161 | 
            +
                  end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                  def nested_params
         | 
| 164 | 
            +
                    route = controller.request.routes.recognize_path(controller.request.path)
         | 
| 165 | 
            +
                    route.reject { |key, _value| %w[controller action].include?(key.to_s) }
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                  def nested_relations
         | 
| 169 | 
            +
                    return [] if nested_params.blank?
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                    nested_params.map do |key, value|
         | 
| 172 | 
            +
                      collection.model.reflections.values.find do |reflection|
         | 
| 173 | 
            +
                        reflection.foreign_key.to_s == key.to_s
         | 
| 174 | 
            +
                      end&.klass&.find(value)
         | 
| 175 | 
            +
                    end.compact
         | 
| 176 | 
            +
                  end
         | 
| 143 177 | 
             
                end
         | 
| 144 178 | 
             
              end
         | 
| 145 179 | 
             
            end
         | 
| @@ -25,6 +25,7 @@ module ActiveElement | |
| 25 25 | 
             
                    conditions = search_filters.to_h.map do |key, value|
         | 
| 26 26 | 
             
                      next relation_matches(key, value) if relation?(key)
         | 
| 27 27 | 
             
                      next datetime_between(key, value) if datetime?(key)
         | 
| 28 | 
            +
                      next join(key, value) if key.to_s.include?('.')
         | 
| 28 29 | 
             
                      next model.arel_table[key].matches("#{value}%") if string_like_column?(key)
         | 
| 29 30 |  | 
| 30 31 | 
             
                      model.arel_table[key].eq(value)
         | 
| @@ -36,7 +37,15 @@ module ActiveElement | |
| 36 37 | 
             
                  end
         | 
| 37 38 |  | 
| 38 39 | 
             
                  def search_relations
         | 
| 39 | 
            -
                    search_filters.to_h.keys.map { |key| relation?(key) ? key.to_sym : nil }.compact
         | 
| 40 | 
            +
                    relation_joins = search_filters.to_h.keys.map { |key| relation?(key) ? key.to_sym : nil }.compact
         | 
| 41 | 
            +
                    (relation_joins + shorthand_joins).uniq
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def shorthand_joins
         | 
| 45 | 
            +
                    search_filters.to_h
         | 
| 46 | 
            +
                                  .keys
         | 
| 47 | 
            +
                                  .select { |key| key.to_s.include?('.') }
         | 
| 48 | 
            +
                                  .map { |key| key.partition('.').first.to_sym }
         | 
| 40 49 | 
             
                  end
         | 
| 41 50 |  | 
| 42 51 | 
             
                  private
         | 
| @@ -66,6 +75,11 @@ module ActiveElement | |
| 66 75 | 
             
                    end.compact
         | 
| 67 76 | 
             
                  end
         | 
| 68 77 |  | 
| 78 | 
            +
                  def join(key, value)
         | 
| 79 | 
            +
                    table, _, column = key.to_s.partition('.')
         | 
| 80 | 
            +
                    relation(table).klass.arel_table[column].eq(value)
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 69 83 | 
             
                  def noop
         | 
| 70 84 | 
             
                    Arel::Nodes::True.new.eq(Arel::Nodes::True.new)
         | 
| 71 85 | 
             
                  end
         | 
| @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            module ActiveElement
         | 
| 2 2 | 
             
              class FieldOptions
         | 
| 3 | 
            -
                attr_accessor :type, :options
         | 
| 3 | 
            +
                attr_accessor :type, :options, :value
         | 
| 4 4 | 
             
                attr_reader :field
         | 
| 5 5 |  | 
| 6 | 
            -
                def self.from_state(field, state, record)
         | 
| 6 | 
            +
                def self.from_state(field, state, record, controller)
         | 
| 7 7 | 
             
                  block = state.field_options[field]
         | 
| 8 8 | 
             
                  return nil if block.blank?
         | 
| 9 9 |  | 
| 10 10 | 
             
                  field_options = new(field)
         | 
| 11 | 
            -
                  block.call(field_options, record)
         | 
| 11 | 
            +
                  block.call(field_options, record, controller)
         | 
| 12 12 | 
             
                  field_options
         | 
| 13 13 | 
             
                end
         | 
| 14 14 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: active_element
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.20
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Bob Farrell
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-05-10 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bootstrap
         |