activeadmin-searchable_select 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +10 -0
  5. data/.travis.yml +16 -0
  6. data/.yardopts +2 -0
  7. data/Appraisals +15 -0
  8. data/CHANGELOG.md +7 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE.txt +25 -0
  11. data/README.md +247 -0
  12. data/Rakefile +6 -0
  13. data/activeadmin-searchable_select.gemspec +37 -0
  14. data/app/assets/javascripts/active_admin/searchable_select.js.coffee +3 -0
  15. data/app/assets/javascripts/active_admin/searchable_select/init.js.coffee +29 -0
  16. data/app/assets/stylesheets/active_admin/searchable_select.scss +5 -0
  17. data/bin/rspec +17 -0
  18. data/gemfiles/rails_4.2_active_admin_1.0.0.pre4.gemfile +9 -0
  19. data/gemfiles/rails_5.1_active_admin_1.0.gemfile +8 -0
  20. data/gemfiles/rails_5.1_active_admin_1.1.gemfile +8 -0
  21. data/lib/activeadmin-searchable_select.rb +6 -0
  22. data/lib/activeadmin/inputs/filters/searchable_select_input.rb +13 -0
  23. data/lib/activeadmin/inputs/searchable_select_input.rb +11 -0
  24. data/lib/activeadmin/searchable_select.rb +20 -0
  25. data/lib/activeadmin/searchable_select/engine.rb +11 -0
  26. data/lib/activeadmin/searchable_select/option_collection.rb +103 -0
  27. data/lib/activeadmin/searchable_select/resource_dsl_extension.rb +39 -0
  28. data/lib/activeadmin/searchable_select/resource_extension.rb +10 -0
  29. data/lib/activeadmin/searchable_select/select_input_extension.rb +130 -0
  30. data/lib/activeadmin/searchable_select/version.rb +5 -0
  31. data/spec/features/ajax_params_spec.rb +53 -0
  32. data/spec/features/end_to_end_spec.rb +83 -0
  33. data/spec/features/filter_input_spec.rb +191 -0
  34. data/spec/features/form_input_spec.rb +178 -0
  35. data/spec/features/inline_ajax_setting_spec.rb +41 -0
  36. data/spec/features/input_errors_spec.rb +55 -0
  37. data/spec/features/options_dsl_spec.rb +248 -0
  38. data/spec/internal/app/assets/javascripts/active_admin.js +2 -0
  39. data/spec/internal/app/assets/stylesheets/active_admin.scss +2 -0
  40. data/spec/internal/app/controllers/application_controller.rb +5 -0
  41. data/spec/internal/config/database.yml +3 -0
  42. data/spec/internal/config/initializers/assets.rb +3 -0
  43. data/spec/internal/config/routes.rb +3 -0
  44. data/spec/internal/db/schema.rb +26 -0
  45. data/spec/internal/log/.gitignore +1 -0
  46. data/spec/internal/public/favicon.ico +0 -0
  47. data/spec/rails_helper.rb +13 -0
  48. data/spec/spec_helper.rb +96 -0
  49. data/spec/support/active_admin_helpers.rb +9 -0
  50. data/spec/support/capybara.rb +8 -0
  51. data/spec/support/models.rb +21 -0
  52. data/spec/support/pluck_polyfill.rb +12 -0
  53. data/spec/support/reset_settings.rb +5 -0
  54. metadata +311 -0
@@ -0,0 +1,41 @@
1
+ require 'rails_helper'
2
+
3
+ require 'support/models'
4
+ require 'support/capybara'
5
+ require 'support/active_admin_helpers'
6
+
7
+ RSpec.describe 'inline_ajax_options setting', type: :request do
8
+ describe 'when ajax option set to true ' 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
14
+
15
+ ActiveAdmin.register(Post) do
16
+ form do |f|
17
+ f.input(:category,
18
+ as: :searchable_select,
19
+ ajax: true)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ it 'renders all options statically' do
26
+ Category.create!(name: 'Travel')
27
+ Category.create!(name: 'Music')
28
+ Category.create!(name: 'Cooking')
29
+
30
+ ActiveAdmin::SearchableSelect.inline_ajax_options = true
31
+ get '/admin/posts/new'
32
+
33
+ expect(response.body).to have_selector('.searchable-select-input option',
34
+ text: 'Travel')
35
+ expect(response.body).to have_selector('.searchable-select-input option',
36
+ text: 'Music')
37
+ expect(response.body).to have_selector('.searchable-select-input option',
38
+ text: 'Cooking')
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,55 @@
1
+ require 'rails_helper'
2
+
3
+ require 'support/models'
4
+ require 'support/capybara'
5
+ require 'support/active_admin_helpers'
6
+
7
+ RSpec.describe 'searchable select', type: :request do
8
+ it 'fails with helpful error message if ajax resource cannot be auto detected' do
9
+ expect do
10
+ ActiveAdminHelpers.setup do
11
+ ActiveAdmin.register(Post) do
12
+ filter(:category_id_eq,
13
+ as: :searchable_select,
14
+ ajax: true)
15
+ end
16
+ end
17
+
18
+ get '/admin/posts'
19
+ end.to raise_error(/Cannot auto detect resource/)
20
+ end
21
+
22
+ it 'fails with helpful error message if named option collection does not exist' do
23
+ expect do
24
+ ActiveAdminHelpers.setup do
25
+ ActiveAdmin.register(Category) do
26
+ searchable_select_options(scope: Category, text_attribute: :name)
27
+ end
28
+
29
+ ActiveAdmin.register(Post) do
30
+ filter(:category,
31
+ as: :searchable_select,
32
+ ajax: {
33
+ collection_name: 'custom'
34
+ })
35
+ end
36
+ end
37
+
38
+ get '/admin/posts'
39
+ end.to raise_error(/No option collection named 'custom' defined in 'Category' admin./)
40
+ end
41
+
42
+ it 'fails with helpful error message if ajax resource does not have an admin' do
43
+ expect do
44
+ ActiveAdminHelpers.setup do
45
+ ActiveAdmin.register(Post) do
46
+ filter(:category,
47
+ as: :searchable_select,
48
+ ajax: true)
49
+ end
50
+ end
51
+
52
+ get '/admin/posts'
53
+ end.to raise_error(/No admin found for 'Category'/)
54
+ end
55
+ end
@@ -0,0 +1,248 @@
1
+ require 'rails_helper'
2
+
3
+ require 'support/models'
4
+ require 'support/pluck_polyfill'
5
+ require 'support/active_admin_helpers'
6
+
7
+ RSpec.describe 'searchable_select_options dsl', type: :request do
8
+ describe 'with text_attribute option' do
9
+ before(:each) do
10
+ ActiveAdminHelpers.setup do
11
+ ActiveAdmin.register(Post) do
12
+ searchable_select_options(scope: Post, text_attribute: :title)
13
+ end
14
+ end
15
+ end
16
+
17
+ describe 'creates JSON endpoint that' do
18
+ it 'returns options for searchable select' do
19
+ Post.create!(title: 'A post')
20
+
21
+ get '/admin/posts/all_options'
22
+
23
+ expect(json_response).to match(results: [a_hash_including(text: 'A post',
24
+ id: kind_of(Numeric))],
25
+ pagination: { more: false })
26
+ end
27
+
28
+ it 'supports filtering via term parameter' do
29
+ Post.create!(title: 'A post')
30
+ Post.create!(title: 'Other post')
31
+ Post.create!(title: 'Not matched')
32
+
33
+ get '/admin/posts/all_options?term=post'
34
+ titles = json_response[:results].pluck(:text)
35
+
36
+ expect(titles).to eq(['A post', 'Other post'])
37
+ end
38
+ end
39
+ end
40
+
41
+ describe 'with separate filter option' do
42
+ before(:each) do
43
+ ActiveAdminHelpers.setup do
44
+ ActiveAdmin.register(Post) do
45
+ searchable_select_options(scope: Post,
46
+ filter: ->(term, scope) { scope.where(title: term) },
47
+ text_attribute: :title)
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'creates JSON endpoint that' do
53
+ it 'returns options for searchable select' do
54
+ Post.create!(title: 'A post')
55
+
56
+ get '/admin/posts/all_options'
57
+
58
+ expect(json_response).to match(results: [a_hash_including(text: 'A post',
59
+ id: kind_of(Numeric))],
60
+ pagination: { more: false })
61
+ end
62
+
63
+ it 'supports filtering via term parameter' do
64
+ Post.create!(title: 'Post')
65
+ Post.create!(title: 'Not matched')
66
+
67
+ get '/admin/posts/all_options?term=Post'
68
+ titles = json_response[:results].pluck(:text)
69
+
70
+ expect(titles).to eq(['Post'])
71
+ end
72
+ end
73
+ end
74
+
75
+ describe 'with separate display_text option' do
76
+ before(:each) do
77
+ ActiveAdminHelpers.setup do
78
+ ActiveAdmin.register(Post) do
79
+ searchable_select_options(scope: Post,
80
+ display_text: ->(record) { record.title.upcase },
81
+ text_attribute: :title)
82
+ end
83
+ end
84
+ end
85
+
86
+ describe 'creates JSON endpoint that' do
87
+ it 'returns options for searchable select' do
88
+ Post.create!(title: 'A post')
89
+
90
+ get '/admin/posts/all_options'
91
+
92
+ expect(json_response).to match(results: [a_hash_including(text: 'A POST',
93
+ id: kind_of(Numeric))],
94
+ pagination: { more: false })
95
+ end
96
+
97
+ it 'supports filtering via term parameter' do
98
+ Post.create!(title: 'A post')
99
+ Post.create!(title: 'Not matched')
100
+
101
+ get '/admin/posts/all_options?term=post'
102
+ titles = json_response[:results].pluck(:text)
103
+
104
+ expect(titles).to eq(['A POST'])
105
+ end
106
+ end
107
+ end
108
+
109
+ describe 'pagination' do
110
+ before(:each) do
111
+ ActiveAdminHelpers.setup do
112
+ ActiveAdmin.register(Post) do
113
+ searchable_select_options(scope: Post,
114
+ text_attribute: :title,
115
+ per_page: 2)
116
+ end
117
+ end
118
+ end
119
+
120
+ it 'limits results and indicates that more results are available' do
121
+ Post.create!(title: 'A post')
122
+ Post.create!(title: 'Other post')
123
+ Post.create!(title: 'Yet another post')
124
+
125
+ get '/admin/posts/all_options'
126
+
127
+ expect(json_response[:results].size).to eq(2)
128
+ expect(json_response[:pagination][:more]).to eq(true)
129
+ end
130
+
131
+ it 'allows passing page param' do
132
+ Post.create!(title: 'A post')
133
+ Post.create!(title: 'Other post')
134
+ Post.create!(title: 'Yet another post')
135
+
136
+ get '/admin/posts/all_options?page=1'
137
+
138
+ expect(json_response[:results].size).to eq(1)
139
+ expect(json_response[:pagination][:more]).to eq(false)
140
+ end
141
+ end
142
+
143
+ it 'allows passing lambda as scope' do
144
+ ActiveAdminHelpers.setup do
145
+ ActiveAdmin.register(Post) do
146
+ searchable_select_options(scope: -> { Post.published },
147
+ text_attribute: :title)
148
+ end
149
+ end
150
+
151
+ Post.create!(title: 'Draft')
152
+ Post.create!(title: 'Published post', published: true)
153
+
154
+ get '/admin/posts/all_options'
155
+ titles = json_response[:results].pluck(:text)
156
+
157
+ expect(titles).to eq(['Published post'])
158
+ end
159
+
160
+ it 'allows passing lambda as scope that uses view helpers' do
161
+ ActiveAdminHelpers.setup do
162
+ ActiveAdmin.register(Post) do
163
+ searchable_select_options(scope: -> { Post.where(user: current_user) },
164
+ text_attribute: :title)
165
+ end
166
+ end
167
+
168
+ user = User.create!
169
+ Post.create!(title: 'By current user', user: user)
170
+ Post.create!(title: 'By other user', user: User.create!)
171
+
172
+ ApplicationController.current_user = user
173
+ get '/admin/posts/all_options'
174
+ titles = json_response[:results].pluck(:text)
175
+
176
+ expect(titles).to eq(['By current user'])
177
+ end
178
+
179
+ it 'allows passing lambda that takes params argument' do
180
+ ActiveAdminHelpers.setup do
181
+ ActiveAdmin.register(Post) do
182
+ searchable_select_options(scope: ->(params) { Post.where(user_id: params[:user]) },
183
+ text_attribute: :title)
184
+ end
185
+ end
186
+
187
+ user = User.create!
188
+ Post.create!(title: 'By given user', user: user)
189
+ Post.create!(title: 'By other user', user: User.create!)
190
+
191
+ get "/admin/posts/all_options?user=#{user.id}"
192
+ titles = json_response[:results].pluck(:text)
193
+
194
+ expect(titles).to eq(['By given user'])
195
+ end
196
+
197
+ it 'allows passing name prefix for collection action' do
198
+ ActiveAdminHelpers.setup do
199
+ ActiveAdmin.register(Post) do
200
+ searchable_select_options(name: :some,
201
+ scope: Post,
202
+ text_attribute: :title)
203
+ end
204
+ end
205
+
206
+ Post.create!(title: 'A post')
207
+
208
+ get '/admin/posts/some_options'
209
+
210
+ expect(json_response).to include(results: array_including(a_hash_including(text: 'A post')))
211
+ end
212
+
213
+ it 'fails with helpful message if scope option is missing' do
214
+ expect do
215
+ ActiveAdminHelpers.setup do
216
+ ActiveAdmin.register(Post) do
217
+ searchable_select_options(text_attribute: :title)
218
+ end
219
+ end
220
+ end.to raise_error(/Missing option: scope/)
221
+ end
222
+
223
+ it 'fails with helpful message if display_text are missing' do
224
+ expect do
225
+ ActiveAdminHelpers.setup do
226
+ ActiveAdmin.register(Post) do
227
+ searchable_select_options(scope: Post,
228
+ filter: ->(_term, scope) { scope })
229
+ end
230
+ end
231
+ end.to raise_error(/Missing option: display_text/)
232
+ end
233
+
234
+ it 'fails with helpful message if filter option is missing' do
235
+ expect do
236
+ ActiveAdminHelpers.setup do
237
+ ActiveAdmin.register(Post) do
238
+ searchable_select_options(scope: Post,
239
+ display_text: ->(_term, scope) { scope })
240
+ end
241
+ end
242
+ end.to raise_error(/Missing option: filter/)
243
+ end
244
+
245
+ def json_response
246
+ JSON.parse(response.body).with_indifferent_access
247
+ end
248
+ end
@@ -0,0 +1,2 @@
1
+ //= require active_admin/base
2
+ //= require active_admin/searchable_select
@@ -0,0 +1,2 @@
1
+ @import "active_admin/mixins";
2
+ @import "active_admin/base";
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ cattr_accessor :current_user
3
+
4
+ helper_method :current_user
5
+ end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: db/combustion_test.sqlite
@@ -0,0 +1,3 @@
1
+ Rails.application.config.assets.precompile += %w(active_admin.js
2
+ active_admin.css
3
+ active_admin/print.css)
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ ActiveAdmin.routes(self)
3
+ end
@@ -0,0 +1,26 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table(:active_admin_comments, force: true) do |t|
3
+ t.string :namespace
4
+ t.text :body
5
+ t.string :resource_id, null: false
6
+ t.string :resource_type, null: false
7
+ t.references :author, polymorphic: true
8
+ t.timestamps null: false
9
+ end
10
+
11
+ create_table(:categories, force: true) do |t|
12
+ t.string :name
13
+ t.belongs_to :created_by
14
+ end
15
+
16
+ create_table(:posts, force: true) do |t|
17
+ t.string :title
18
+ t.belongs_to :category
19
+ t.belongs_to :user
20
+ t.boolean :published
21
+ end
22
+
23
+ create_table(:users, force: true) do |t|
24
+ t.string :name
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ *.log
File without changes
@@ -0,0 +1,13 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+
3
+ require 'combustion'
4
+ Combustion.initialize!(:active_record, :action_controller, :action_view, :sprockets)
5
+
6
+ require 'rspec/rails'
7
+ require 'support/reset_settings'
8
+
9
+ RSpec.configure do |config|
10
+ config.infer_spec_type_from_file_location!
11
+
12
+ config.filter_rails_from_backtrace!
13
+ end
@@ -0,0 +1,96 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # This allows you to limit a spec run to individual examples or groups
48
+ # you care about by tagging them with `:focus` metadata. When nothing
49
+ # is tagged with `:focus`, all examples get run. RSpec also provides
50
+ # aliases for `it`, `describe`, and `context` that include `:focus`
51
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
52
+ config.filter_run_when_matching :focus
53
+
54
+ # Allows RSpec to persist some state between runs in order to support
55
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
56
+ # you configure your source control system to ignore this file.
57
+ config.example_status_persistence_file_path = 'spec/examples.txt'
58
+
59
+ # Limits the available syntax to the non-monkey patched syntax that is
60
+ # recommended. For more details, see:
61
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
62
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
63
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
64
+ config.disable_monkey_patching!
65
+
66
+ # This setting enables warnings. It's recommended, but in some cases may
67
+ # be too noisy due to issues in dependencies.
68
+ config.warnings = false
69
+
70
+ # Many RSpec users commonly either run the entire suite or an individual
71
+ # file, and it's useful to allow more verbose output when running an
72
+ # individual spec file.
73
+ if config.files_to_run.one?
74
+ # Use the documentation formatter for detailed output,
75
+ # unless a formatter has already been configured
76
+ # (e.g. via a command-line flag).
77
+ config.default_formatter = 'doc'
78
+ end
79
+
80
+ # Print the 10 slowest examples and example groups at the
81
+ # end of the spec run, to help surface which specs are running
82
+ # particularly slow.
83
+ config.profile_examples = 10
84
+
85
+ # Run specs in random order to surface order dependencies. If you find an
86
+ # order dependency and want to debug it, you can fix the order by providing
87
+ # the seed, which is printed after each run.
88
+ # --seed 1234
89
+ config.order = :random
90
+
91
+ # Seed global randomization in this process using the `--seed` CLI option.
92
+ # Setting this allows you to use `--seed` to deterministically reproduce
93
+ # test failures related to randomization by passing the same `--seed` value
94
+ # as the one that triggered the failure.
95
+ Kernel.srand config.seed
96
+ end