activeadmin-searchable_select 1.3.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a0f1ed4c7ca3f0b20a9a1844aa46ecf2693b5b91f447874cbfbc013d5a84e31
4
- data.tar.gz: cdca70eae83da1caf578f39957b66fe30cec69a76c67f7f06d2a45bb3c680f7d
3
+ metadata.gz: 86f2b11107d464955292c9d5a51629757ff3fb213aafdeb9302b948f421796ee
4
+ data.tar.gz: 961efac5444a994afac0b8a13437bbd96ec8effedbbc6356b606a9d23bc80849
5
5
  SHA512:
6
- metadata.gz: b509e473b24b32cad8c401f197413c7b01c7de196aaf0a2a4a735f7f6bd40d4ea76ed7733f9b00f87ecf23e6d3aba0bceee599f85c4e39fb1a17639b4234d94f
7
- data.tar.gz: f224c6cc6fe60af0373b1d82c9d57a43282c3f327bf5c3c295d2b1d17ce5f9aace56aa0d4320405f40783a270fedaa8137496712091fc167712b9b1e1364a029
6
+ metadata.gz: 79b204c1f6af56e2d39c4ee658cfde5af5ca31733ed3d2eb657ab78a8de66511b9e7387dd5fa64a457768095c67858e64f3fd598058ef1458fceba0575880522
7
+ data.tar.gz: 9bcbd53a3504788a0c18ce8f701efefcc2f0495625eedf277a30c738c5083d4b3a4602fdd95c05ca904ad8b0034e551ea1706a46f077805c5b0146f0121ee2fc
@@ -1,5 +1,9 @@
1
1
  name: tests
2
- on: [push, pull_request]
2
+ on:
3
+ push:
4
+ pull_request:
5
+ schedule:
6
+ - cron: '45 2 * * *'
3
7
 
4
8
  jobs:
5
9
  rspec:
@@ -31,4 +35,4 @@ jobs:
31
35
  ruby-version: ${{ matrix.ruby-version }}
32
36
  bundler-cache: true
33
37
  - name: Run tests
34
- run: bundle exec rspec
38
+ run: bundle exec rspec
data/.rubocop.yml CHANGED
@@ -1,5 +1,7 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.3
3
+ Exclude:
4
+ - 'bin/**/*'
3
5
 
4
6
  # The default of 80 characters is a little to narrow.
5
7
  Metrics/LineLength:
data/CHANGELOG.md CHANGED
@@ -1,20 +1,14 @@
1
1
  # CHANGELOG
2
2
 
3
- ### Version 1.3.0
3
+ ### Version 1.6.0
4
4
 
5
- 2021-02-03
5
+ 2022-04-05
6
6
 
7
- [Compare changes](https://github.com/codevise/activeadmin-searchable_select/compare/1-2-stable...v1.3.0)
7
+ [Compare changes](https://github.com/codevise/activeadmin-searchable_select/compare/1-5-stable...v1.6.0)
8
8
 
9
- - Add support for Ruby 3
10
- ([#26](https://github.com/codevise/activeadmin-searchable_select/pull/26))
11
- - Add a small description of how to pass options to Select2
12
- ([#27](https://github.com/codevise/activeadmin-searchable_select/pull/27))
13
- - Update CI Setup
14
- ([#25](https://github.com/codevise/activeadmin-searchable_select/pull/25))
15
- - Allow Bundler 2 in development dependencies
16
- ([#18](https://github.com/codevise/activeadmin-searchable_select/pull/18))
9
+ - Add option to pass path params for nested resource fetching
10
+ ([#39](https://github.com/codevise/activeadmin-searchable_select/pull/39))
17
11
 
18
12
  See
19
- [1-2-stable branch](https://github.com/codevise/activeadmin-searchable_select/blob/1-2-stable/CHANGELOG.md)
13
+ [1-5-stable branch](https://github.com/codevise/activeadmin-searchable_select/blob/1-5-stable/CHANGELOG.md)
20
14
  for previous changes.
data/README.md CHANGED
@@ -193,6 +193,25 @@ build your query in a way that it can query multiple attributes at once.
193
193
 
194
194
  In this example, the `all` scope will query `email OR username`.
195
195
 
196
+ You can add the additional payload as dsl option:
197
+
198
+ ```ruby
199
+ ActiveAdmin.register Category do
200
+ searchable_select_options(scope: Category.all,
201
+ text_attribute: :name,
202
+ additional_payload: ->(record) { { foo: record.bar } } )
203
+ end
204
+ ```
205
+
206
+ response example which uses additional_payload:
207
+
208
+ ```json
209
+ {
210
+ "results": [{ "id": "1", "text": "Bicycles", "foo": "Bar" }],
211
+ "pagination": { "more": "false" }
212
+ }
213
+ ```
214
+
196
215
  #### Passing Parameters
197
216
 
198
217
  You can pass additional parameters to the options endpoint:
@@ -223,6 +242,67 @@ argument:
223
242
  end
224
243
  ```
225
244
 
245
+ #### Path options for nested resources
246
+
247
+ Example for the following setup:
248
+
249
+ ```ruby
250
+ # Models
251
+ class OptionType < ActiveRecord::Base; end
252
+
253
+ class OptionValue < ActiveRecord::Base
254
+ belongs_to :option_type
255
+ end
256
+
257
+ class Product < ActiveRecord::Base
258
+ belongs_to :option_type
259
+ has_many :variants
260
+ end
261
+
262
+ class Variant < ActiveRecord::Base
263
+ belongs_to :product
264
+ belongs_to :option_value
265
+ end
266
+
267
+ # ActiveAdmin
268
+ ActiveAdmin.register(OptionType)
269
+
270
+ ActiveAdmin.register(Product)
271
+
272
+ ActiveAdmin.register(OptionValue) do
273
+ belongs_to :option_type
274
+ searchable_select_options(scope: lambda do |params|
275
+ OptionValue.where(
276
+ option_type_id: params[:option_type_id]
277
+ )
278
+ end,
279
+ text_attribute: :value)
280
+ end
281
+ ```
282
+
283
+ It is possible to pass path parameters for correctly generating URLs for nested resources fetching via `path_params`
284
+
285
+ ```ruby
286
+ ActiveAdmin.register(Variant) do
287
+ belongs_to :product
288
+
289
+ form do |f|
290
+ ...
291
+ f.input(:option_value,
292
+ as: :searchable_select,
293
+ ajax: {
294
+ resource: OptionValue,
295
+ path_params: {
296
+ option_type_id: f.object.product.option_type_id
297
+ }
298
+ })
299
+ ...
300
+ end
301
+ end
302
+ ```
303
+
304
+ This will generate the path for fetching as `all_options_admin_option_type_option_values(option_type_id: f.object.product.option_type_id)` (e.g. `/admin/option_types/2/option_values/all_options`)
305
+
226
306
  #### Inlining Ajax Options in Feature Tests
227
307
 
228
308
  When writing UI driven feature specs (i.e. with Capybara),
@@ -238,7 +318,7 @@ for feature specs:
238
318
  ```ruby
239
319
  RSpec.configure do |config|
240
320
  config.before(:each) do |example|
241
- ActiveAdmin::Select2.inline_ajax_options = (example.metadata[:type] == :feature)
321
+ ActiveAdmin::SearchableSelect.inline_ajax_options = (example.metadata[:type] == :feature)
242
322
  end
243
323
  end
244
324
 
@@ -246,7 +326,7 @@ for feature specs:
246
326
 
247
327
  ### Passing options to Select2
248
328
 
249
- It is possible to pass and define configuration options to Select2
329
+ It is possible to pass and define configuration options to Select2
250
330
  via `data-attributes` using nested (subkey) options.
251
331
 
252
332
  Attributes need to be added to the `input_html` option in the form input.
@@ -8,6 +8,7 @@ module ActiveAdmin
8
8
  @display_text = extract_display_text_option(options)
9
9
  @filter = extract_filter_option(options)
10
10
  @per_page = options.fetch(:per_page, 10)
11
+ @additional_payload = options.fetch(:additional_payload, {})
11
12
  end
12
13
 
13
14
  def scope(template, params)
@@ -38,7 +39,7 @@ module ActiveAdmin
38
39
  {
39
40
  id: record.id,
40
41
  text: display_text(record)
41
- }
42
+ }.merge(hash_of_additional_payload(record) || {})
42
43
  end
43
44
 
44
45
  { results: results, pagination: { more: more } }
@@ -98,6 +99,21 @@ module ActiveAdmin
98
99
  ->(term, scope) { scope.ransack("#{text_attribute}_cont" => term).result }
99
100
  end
100
101
  end
102
+
103
+ def build_additional_payload(record)
104
+ case @additional_payload
105
+ when Proc
106
+ @additional_payload.call(record).to_h
107
+ else
108
+ {}
109
+ end
110
+ end
111
+
112
+ def hash_of_additional_payload(record)
113
+ return nil if @additional_payload.nil? && @additional_payload.empty?
114
+
115
+ build_additional_payload(record)
116
+ end
101
117
  end
102
118
  end
103
119
  end
@@ -26,6 +26,23 @@ module ActiveAdmin
26
26
  #
27
27
  # @param name [Symbol] Optional collection name if helper is
28
28
  # used multiple times within one resource.
29
+ #
30
+ # @param additional_payload [Proc]
31
+ # Adds additional attributes to the results array
32
+ # @example
33
+ #
34
+ # ActiveAdmin.register Tag do
35
+ # searchable_select_options(
36
+ # scope: Color,
37
+ # text_attributes: :title,
38
+ # additional_payload: lambda { |record| { color: record.color } }
39
+ # )
40
+ # end
41
+ # @json
42
+ # {
43
+ # "results": [{ "id": "1", "text": "Red", "color": "#FFF" }],
44
+ # "pagination": { "more": "false" }
45
+ # }
29
46
  def searchable_select_options(name: :all, **options)
30
47
  option_collection = OptionCollection.new(name, options)
31
48
  config.searchable_select_option_collections[name] = option_collection
@@ -20,6 +20,10 @@ module ActiveAdmin
20
20
  # - `params`: Hash of query parameters that shall be passed to the
21
21
  # options endpoint.
22
22
  #
23
+ # - `path_params`: Hash of parameters, which would be passed to the
24
+ # dynamic collection path generation for the resource.
25
+ # e.g `admin_articles_path(path_params)`
26
+ #
23
27
  # If the `ajax` option is present, the `collection` option is
24
28
  # ignored.
25
29
  module SelectInputExtension
@@ -45,9 +49,11 @@ module ActiveAdmin
45
49
 
46
50
  def ajax_url
47
51
  return unless options[:ajax]
48
- template.polymorphic_path([template.active_admin_namespace.route_prefix, ajax_resource_class],
49
- action: option_collection.collection_action_name,
50
- **ajax_params)
52
+ [ajax_resource.route_collection_path(path_params),
53
+ '/',
54
+ option_collection.collection_action_name,
55
+ '?',
56
+ ajax_params.to_query].join
51
57
  end
52
58
 
53
59
  def all_options_collection
@@ -122,6 +128,10 @@ module ActiveAdmin
122
128
  ajax_options.fetch(:params, {})
123
129
  end
124
130
 
131
+ def path_params
132
+ ajax_options.fetch(:path_params, {})
133
+ end
134
+
125
135
  def ajax_options
126
136
  options[:ajax] == true ? {} : options[:ajax]
127
137
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveAdmin
2
2
  module SearchableSelect
3
- VERSION = '1.3.0'.freeze
3
+ VERSION = '1.6.0'.freeze
4
4
  end
5
5
  end
@@ -5,60 +5,180 @@ require 'support/capybara'
5
5
  require 'support/active_admin_helpers'
6
6
 
7
7
  RSpec.describe 'end to end', type: :feature, js: true do
8
- before(:each) do
9
- ActiveAdminHelpers.setup do
10
- ActiveAdmin.register(Category) do
11
- searchable_select_options(scope: Category, text_attribute: :name)
12
- end
8
+ context 'class name without namespaces' do
9
+ before(:each) do
10
+ ActiveAdminHelpers.setup do
11
+ ActiveAdmin.register(Category) do
12
+ searchable_select_options(scope: Category, text_attribute: :name)
13
+ end
13
14
 
14
- ActiveAdmin.register(Post) do
15
- filter(:category, as: :searchable_select, ajax: true)
15
+ ActiveAdmin.register(Post) do
16
+ filter(:category, as: :searchable_select, ajax: true)
16
17
 
17
- form do |f|
18
- f.input(:category, as: :searchable_select, ajax: true)
18
+ form do |f|
19
+ f.input(:category, as: :searchable_select, ajax: true)
20
+ end
19
21
  end
22
+
23
+ ActiveAdmin.setup {}
20
24
  end
25
+ end
26
+
27
+ describe 'index page with searchable select filter' do
28
+ it 'loads filter input options' do
29
+ Category.create(name: 'Music')
30
+ Category.create(name: 'Travel')
31
+
32
+ visit '/admin/posts'
33
+
34
+ expand_select_box
35
+ wait_for_ajax
36
+
37
+ expect(select_box_items).to eq(%w(Music Travel))
38
+ end
39
+
40
+ it 'allows filtering options by term' do
41
+ Category.create(name: 'Music')
42
+ Category.create(name: 'Travel')
43
+
44
+ visit '/admin/posts'
21
45
 
22
- ActiveAdmin.setup {}
46
+ expand_select_box
47
+ enter_search_term('T')
48
+ wait_for_ajax
49
+
50
+ expect(select_box_items).to eq(%w(Travel))
51
+ end
52
+
53
+ it 'loads more items when scrolling down' do
54
+ 15.times { |i| Category.create(name: "Category #{i}") }
55
+ visit '/admin/posts'
56
+
57
+ expand_select_box
58
+ wait_for_ajax
59
+ scroll_select_box_list
60
+ wait_for_ajax
61
+
62
+ expect(select_box_items.size).to eq(15)
63
+ end
23
64
  end
24
65
  end
25
66
 
26
- describe 'index page with searchable select filter' do
27
- it 'loads filter input options' do
28
- Category.create(name: 'Music')
29
- Category.create(name: 'Travel')
30
-
31
- visit '/admin/posts'
67
+ context 'class name with namespace' do
68
+ before(:each) do
69
+ ActiveAdminHelpers.setup do
70
+ ActiveAdmin.register RGB::Color, as: 'color' do
71
+ searchable_select_options scope: RGB::Color, text_attribute: :code
72
+ end
32
73
 
33
- expand_select_box
34
- wait_for_ajax
74
+ ActiveAdmin.register Internal::TagName, as: 'Tag Name' do
75
+ filter :color, as: :searchable_select, ajax: { resource: RGB::Color }
76
+ end
35
77
 
36
- expect(select_box_items).to eq(%w(Music Travel))
78
+ ActiveAdmin.setup {}
79
+ end
37
80
  end
38
81
 
39
- it 'allows filtering options by term' do
40
- Category.create(name: 'Music')
41
- Category.create(name: 'Travel')
82
+ describe 'index page with searchable select filter' do
83
+ it 'loads filter input options' do
84
+ RGB::Color.create(code: '#eac112', description: 'Orange')
85
+ RGB::Color.create(code: '#19bf25', description: 'Green')
42
86
 
43
- visit '/admin/posts'
87
+ visit '/admin/tag_names'
44
88
 
45
- expand_select_box
46
- enter_search_term('T')
47
- wait_for_ajax
89
+ expand_select_box
90
+ wait_for_ajax
48
91
 
49
- expect(select_box_items).to eq(%w(Travel))
92
+ expect(select_box_items).to eq(%w(#eac112 #19bf25))
93
+ end
50
94
  end
95
+ end
51
96
 
52
- it 'loads more items when scrolling down' do
53
- 15.times { |i| Category.create(name: "Category #{i}") }
54
- visit '/admin/posts'
97
+ context 'class with nested belongs_to association' do
98
+ before(:all) do
99
+ ActiveAdminHelpers.setup do
100
+ ActiveAdmin.register(OptionType)
101
+
102
+ ActiveAdmin.register(Product)
103
+
104
+ ActiveAdmin.register(OptionValue) do
105
+ belongs_to :option_type
106
+ searchable_select_options(scope: lambda do |params|
107
+ OptionValue.where(
108
+ option_type_id: params[:option_type_id]
109
+ )
110
+ end,
111
+ text_attribute: :value)
112
+ end
55
113
 
56
- expand_select_box
57
- wait_for_ajax
58
- scroll_select_box_list
59
- wait_for_ajax
114
+ ActiveAdmin.register(Variant) do
115
+ belongs_to :product
116
+
117
+ form do |f|
118
+ input :price
119
+ input(:option_value,
120
+ as: :searchable_select,
121
+ ajax: {
122
+ resource: OptionValue,
123
+ path_params: {
124
+ option_type_id: f.object.product.option_type_id
125
+ }
126
+ })
127
+ end
128
+ end
60
129
 
61
- expect(select_box_items.size).to eq(15)
130
+ ActiveAdmin.setup {}
131
+ end
132
+ end
133
+
134
+ describe 'new page with searchable select filter' do
135
+ it 'loads filter input options' do
136
+ option_type = OptionType.create(name: 'Color')
137
+ ot = OptionType.create(name: 'Size')
138
+ OptionValue.create(value: 'Black', option_type: option_type)
139
+ OptionValue.create(value: 'Orange', option_type: option_type)
140
+ OptionValue.create(value: 'M', option_type: ot)
141
+ product = Product.create(name: 'Cap', option_type: option_type)
142
+
143
+ visit "/admin/products/#{product.id}/variants/new"
144
+
145
+ expand_select_box
146
+ wait_for_ajax
147
+
148
+ expect(select_box_items).to eq(%w(Black Orange))
149
+ end
150
+
151
+ it 'allows filtering options by term' do
152
+ option_type = OptionType.create(name: 'Color')
153
+ ot = OptionType.create(name: 'Size')
154
+ OptionValue.create(value: 'Black', option_type: option_type)
155
+ OptionValue.create(value: 'Orange', option_type: option_type)
156
+ OptionValue.create(value: 'M', option_type: ot)
157
+ product = Product.create(name: 'Cap', option_type: option_type)
158
+
159
+ visit "/admin/products/#{product.id}/variants/new"
160
+
161
+ expand_select_box
162
+ enter_search_term('O')
163
+ wait_for_ajax
164
+
165
+ expect(select_box_items).to eq(%w(Orange))
166
+ end
167
+
168
+ it 'loads more items when scrolling down' do
169
+ option_type = OptionType.create(name: 'Color')
170
+ 15.times { |i| OptionValue.create(value: "Black #{i}", option_type: option_type) }
171
+ product = Product.create(name: 'Cap', option_type: option_type)
172
+
173
+ visit "/admin/products/#{product.id}/variants/new"
174
+
175
+ expand_select_box
176
+ wait_for_ajax
177
+ scroll_select_box_list
178
+ wait_for_ajax
179
+
180
+ expect(select_box_items.size).to eq(15)
181
+ end
62
182
  end
63
183
  end
64
184
 
@@ -140,6 +140,60 @@ RSpec.describe 'searchable_select_options dsl', type: :request do
140
140
  end
141
141
  end
142
142
 
143
+ describe 'with additional_payload' do
144
+ context 'as lambda' do
145
+ before(:each) do
146
+ ActiveAdminHelpers.setup do
147
+ ActiveAdmin.register(Post) do
148
+ searchable_select_options(
149
+ scope: Post,
150
+ text_attribute: :title,
151
+ additional_payload: lambda do |record|
152
+ { published: record.published }
153
+ end
154
+ )
155
+ end
156
+ end
157
+ end
158
+ let!(:post) { Post.create!(title: 'A post', published: false) }
159
+
160
+ subject { get '/admin/posts/all_options' }
161
+
162
+ it 'returns options with our additional attribute' do
163
+ subject
164
+ expect(json_response).to match(
165
+ results: [{ text: 'A post', id: post.id, published: false }],
166
+ pagination: { more: false }
167
+ )
168
+ end
169
+ end
170
+
171
+ context 'as Proc' do
172
+ before(:each) do
173
+ ActiveAdminHelpers.setup do
174
+ ActiveAdmin.register(Post) do
175
+ searchable_select_options(
176
+ scope: Post,
177
+ text_attribute: :title,
178
+ additional_payload: proc { |record| { published: record.published } }
179
+ )
180
+ end
181
+ end
182
+ end
183
+ let!(:post) { Post.create!(title: 'A post', published: false) }
184
+
185
+ subject { get '/admin/posts/all_options' }
186
+
187
+ it 'returns options with our additional attribute' do
188
+ subject
189
+ expect(json_response).to match(
190
+ results: [{ text: 'A post', id: post.id, published: false }],
191
+ pagination: { more: false }
192
+ )
193
+ end
194
+ end
195
+ end
196
+
143
197
  it 'allows passing lambda as scope' do
144
198
  ActiveAdminHelpers.setup do
145
199
  ActiveAdmin.register(Post) do
@@ -20,6 +20,37 @@ ActiveRecord::Schema.define do
20
20
  t.boolean :published
21
21
  end
22
22
 
23
+ create_table(:rgb_colors, force: true) do |t|
24
+ t.string :code
25
+ t.text :description
26
+ end
27
+
28
+ create_table(:internal_tag_names, force: true) do |t|
29
+ t.string :name
30
+ t.text :description
31
+ t.integer :color_id
32
+ end
33
+
34
+ create_table(:option_types, force: true) do |t|
35
+ t.string :name
36
+ end
37
+
38
+ create_table(:option_values, force: true) do |t|
39
+ t.string :value
40
+ t.belongs_to :option_type
41
+ end
42
+
43
+ create_table(:products, force: true) do |t|
44
+ t.string :name
45
+ t.belongs_to :option_type
46
+ end
47
+
48
+ create_table(:variants, force: true) do |t|
49
+ t.integer :price
50
+ t.belongs_to :product
51
+ t.belongs_to :option_value
52
+ end
53
+
23
54
  create_table(:users, force: true) do |t|
24
55
  t.string :name
25
56
  end
@@ -1,9 +1,13 @@
1
1
  module ActiveAdminHelpers
2
2
  module_function
3
3
 
4
+ def reload_routes!
5
+ Rails.application.reload_routes!
6
+ end
7
+
4
8
  def setup
5
9
  ActiveAdmin.application = nil
6
10
  yield
7
- Rails.application.reload_routes!
11
+ reload_routes!
8
12
  end
9
13
  end
@@ -13,6 +13,36 @@ class Post < ActiveRecord::Base
13
13
  scope(:published, -> { where(published: true) })
14
14
  end
15
15
 
16
+ module RGB
17
+ class Color < ActiveRecord::Base
18
+ self.table_name = :rgb_colors
19
+ has_many :tags, class_name: 'Internal::TagName'
20
+ end
21
+ end
22
+
23
+ module Internal
24
+ class TagName < ActiveRecord::Base
25
+ self.table_name = :internal_tag_names
26
+ belongs_to :color, class_name: 'RGB::Color', foreign_key: :color_id
27
+ end
28
+ end
29
+
30
+ class OptionType < ActiveRecord::Base; end
31
+
32
+ class OptionValue < ActiveRecord::Base
33
+ belongs_to :option_type
34
+ end
35
+
36
+ class Product < ActiveRecord::Base
37
+ belongs_to :option_type
38
+ has_many :variants
39
+ end
40
+
41
+ class Variant < ActiveRecord::Base
42
+ belongs_to :product
43
+ belongs_to :option_value
44
+ end
45
+
16
46
  RSpec.configure do |config|
17
47
  config.after do
18
48
  DatabaseCleaner.strategy = :truncation
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeadmin-searchable_select
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Codevise Solutions Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-03 00:00:00.000000000 Z
11
+ date: 2022-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -375,7 +375,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
375
375
  - !ruby/object:Gem::Version
376
376
  version: '0'
377
377
  requirements: []
378
- rubygems_version: 3.2.3
378
+ rubygems_version: 3.0.8
379
379
  signing_key:
380
380
  specification_version: 4
381
381
  summary: Use searchable selects based on Select2 in Active Admin forms and filters.