active_element 0.0.18 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- 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
|