activeadmin-tom_select 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.actrc +20 -0
- data/.claude/commands/fix-tests.md +203 -0
- data/.github/workflows/ci.yml +174 -0
- data/.github/workflows/npm-publish.yml +50 -0
- data/.gitignore +35 -0
- data/.npmignore +58 -0
- data/.rspec +1 -0
- data/.rubocop.yml +75 -0
- data/.yardopts +2 -0
- data/AGENTS.md +39 -0
- data/Appraisals +9 -0
- data/CHANGELOG.md +64 -0
- data/CLAUDE.md +157 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +368 -0
- data/LICENSE.txt +25 -0
- data/README.md +483 -0
- data/Rakefile +4 -0
- data/activeadmin-tom_select.gemspec +43 -0
- data/bin/rspec +17 -0
- data/config/database.yml +16 -0
- data/docs/activeadmin-4-detailed-reference.md +932 -0
- data/docs/activeadmin-4-gem-migration-guide.md +313 -0
- data/docs/combustion.md +213 -0
- data/docs/fail.png +0 -0
- data/docs/guide-update-your-app.md +283 -0
- data/docs/normal.png +0 -0
- data/docs/propshaft-readme.md +320 -0
- data/docs/propshaft-upgrade.md +484 -0
- data/docs/setup-activeadmin-app.md +552 -0
- data/docs/setup-activeadmin-gem.md +535 -0
- data/docs/tailwind/blog-page.md +341 -0
- data/docs/tailwind/upgrade-guide-enhanced.md +438 -0
- data/docs/tailwind/upgrade-guide.md +416 -0
- data/docs/tailwind-4/active_admin.rake +38 -0
- data/docs/tailwind-4/active_admin.tailwind.css +415 -0
- data/docs/tailwind-4/tailwind-active_admin.config.js +18 -0
- data/docs/test-app-change.md +154 -0
- data/docs/test-environment-fixes.md +58 -0
- data/docs/update-tom-select.md +184 -0
- data/docs/upload-system.md +225 -0
- data/gemfiles/rails_7.x_active_admin_4.x.gemfile +10 -0
- data/gemfiles/rails_7.x_active_admin_4.x.gemfile.lock +377 -0
- data/gemfiles/rails_8.x_active_admin_4.x.gemfile +10 -0
- data/gemfiles/rails_8.x_active_admin_4.x.gemfile.lock +372 -0
- data/lefthook.yml +17 -0
- data/lib/activeadmin/inputs/filters/searchable_select_input.rb +19 -0
- data/lib/activeadmin/inputs/searchable_select_input.rb +16 -0
- data/lib/activeadmin/tom_select/engine.rb +17 -0
- data/lib/activeadmin/tom_select/option_collection.rb +128 -0
- data/lib/activeadmin/tom_select/resource_dsl_extension.rb +56 -0
- data/lib/activeadmin/tom_select/resource_extension.rb +10 -0
- data/lib/activeadmin/tom_select/select_input_extension.rb +168 -0
- data/lib/activeadmin/tom_select/version.rb +5 -0
- data/lib/activeadmin/tom_select.rb +20 -0
- data/lib/activeadmin-tom_select.rb +5 -0
- data/lib/generators/active_admin/tom_select/install/install_generator.rb +180 -0
- data/npm-package/package-lock.json +51 -0
- data/npm-package/package.json +43 -0
- data/npm-package/src/index.js +153 -0
- data/npm-package/src/tom-select-tailwind.css +392 -0
- data/sonar-project.properties +25 -0
- data/spec/features/ajax_params_spec.rb +31 -0
- data/spec/features/asset_pipeline_diagnostic_spec.rb +155 -0
- data/spec/features/end_to_end_spec.rb +273 -0
- data/spec/features/filter_input_spec.rb +144 -0
- data/spec/features/form_input_spec.rb +122 -0
- data/spec/features/inline_ajax_setting_spec.rb +26 -0
- data/spec/features/input_errors_spec.rb +76 -0
- data/spec/features/input_html_options_spec.rb +30 -0
- data/spec/features/options_dsl_spec.rb +230 -0
- data/spec/features/production_build_spec.rb +108 -0
- data/spec/internal/.node-version +1 -0
- data/spec/internal/Gemfile +43 -0
- data/spec/internal/Gemfile.lock +333 -0
- data/spec/internal/Procfile.dev +3 -0
- data/spec/internal/README.md +24 -0
- data/spec/internal/Rakefile +6 -0
- data/spec/internal/app/admin/categories.rb +26 -0
- data/spec/internal/app/admin/dashboard.rb +29 -0
- data/spec/internal/app/admin/option_types.rb +19 -0
- data/spec/internal/app/admin/option_values.rb +30 -0
- data/spec/internal/app/admin/posts.rb +27 -0
- data/spec/internal/app/admin/products.rb +22 -0
- data/spec/internal/app/admin/rgb_colors.rb +25 -0
- data/spec/internal/app/admin/tag_names.rb +21 -0
- data/spec/internal/app/admin/test_ajax_params_category.rb +10 -0
- data/spec/internal/app/admin/test_ajax_params_post.rb +20 -0
- data/spec/internal/app/admin/test_form_post_class.rb +7 -0
- data/spec/internal/app/admin/test_form_post_custom.rb +11 -0
- data/spec/internal/app/admin/test_form_post_resource.rb +11 -0
- data/spec/internal/app/admin/test_form_post_resource_custom.rb +12 -0
- data/spec/internal/app/admin/test_inline_ajax_post.rb +9 -0
- data/spec/internal/app/admin/test_input_html_post.rb +11 -0
- data/spec/internal/app/admin/test_posts_display_text.rb +9 -0
- data/spec/internal/app/admin/test_posts_filter.rb +9 -0
- data/spec/internal/app/admin/test_posts_named.rb +9 -0
- data/spec/internal/app/admin/test_posts_pagination.rb +9 -0
- data/spec/internal/app/admin/test_posts_payload_lambda.rb +11 -0
- data/spec/internal/app/admin/test_posts_payload_proc.rb +9 -0
- data/spec/internal/app/admin/test_posts_scope_lambda.rb +8 -0
- data/spec/internal/app/admin/test_posts_scope_params.rb +8 -0
- data/spec/internal/app/admin/test_posts_scope_user.rb +8 -0
- data/spec/internal/app/admin/test_posts_text_attr.rb +5 -0
- data/spec/internal/app/admin/users.rb +23 -0
- data/spec/internal/app/admin/variants.rb +31 -0
- data/spec/internal/app/assets/config/manifest.js +2 -0
- data/spec/internal/app/assets/images/.keep +0 -0
- data/spec/internal/app/assets/stylesheets/active_admin.tailwind.css +16 -0
- data/spec/internal/app/assets/stylesheets/application.tailwind.css +15 -0
- data/spec/internal/app/controllers/application_controller.rb +9 -0
- data/spec/internal/app/controllers/concerns/.keep +0 -0
- data/spec/internal/app/helpers/application_helper.rb +2 -0
- data/spec/internal/app/javascript/active_admin.js +19 -0
- data/spec/internal/app/javascript/application.js +2 -0
- data/spec/internal/app/jobs/application_job.rb +7 -0
- data/spec/internal/app/mailers/application_mailer.rb +4 -0
- data/spec/internal/app/models/admin_user.rb +9 -0
- data/spec/internal/app/models/application_record.rb +3 -0
- data/spec/internal/app/models/article.rb +12 -0
- data/spec/internal/app/models/category.rb +12 -0
- data/spec/internal/app/models/color.rb +9 -0
- data/spec/internal/app/models/concerns/.keep +0 -0
- data/spec/internal/app/models/internal/tag_name.rb +14 -0
- data/spec/internal/app/models/internal_tag_name.rb +11 -0
- data/spec/internal/app/models/option_type.rb +12 -0
- data/spec/internal/app/models/option_value.rb +4 -0
- data/spec/internal/app/models/post.rb +15 -0
- data/spec/internal/app/models/product.rb +12 -0
- data/spec/internal/app/models/rgb_color.rb +16 -0
- data/spec/internal/app/models/tag.rb +12 -0
- data/spec/internal/app/models/tagging.rb +12 -0
- data/spec/internal/app/models/user.rb +12 -0
- data/spec/internal/app/models/variant.rb +12 -0
- data/spec/internal/app/views/layouts/application.html.erb +28 -0
- data/spec/internal/app/views/layouts/mailer.html.erb +13 -0
- data/spec/internal/app/views/layouts/mailer.text.erb +1 -0
- data/spec/internal/app/views/pwa/manifest.json.erb +22 -0
- data/spec/internal/app/views/pwa/service-worker.js +26 -0
- data/spec/internal/bin/bundle +117 -0
- data/spec/internal/bin/dev +11 -0
- data/spec/internal/bin/rackup +27 -0
- data/spec/internal/bin/rails +4 -0
- data/spec/internal/bin/rake +4 -0
- data/spec/internal/bin/setup +37 -0
- data/spec/internal/config/application.rb +50 -0
- data/spec/internal/config/boot.rb +3 -0
- data/spec/internal/config/credentials.yml.enc +1 -0
- data/spec/internal/config/database.yml +32 -0
- data/spec/internal/config/environment.rb +5 -0
- data/spec/internal/config/environments/development.rb +63 -0
- data/spec/internal/config/environments/production.rb +86 -0
- data/spec/internal/config/environments/test.rb +50 -0
- data/spec/internal/config/initializers/active_admin.rb +54 -0
- data/spec/internal/config/initializers/assets.rb +8 -0
- data/spec/internal/config/initializers/content_security_policy.rb +25 -0
- data/spec/internal/config/initializers/devise.rb +315 -0
- data/spec/internal/config/initializers/filter_parameter_logging.rb +8 -0
- data/spec/internal/config/initializers/inflections.rb +16 -0
- data/spec/internal/config/initializers/searchable_select.rb +6 -0
- data/spec/internal/config/locales/devise.en.yml +65 -0
- data/spec/internal/config/locales/en.yml +31 -0
- data/spec/internal/config/master.key +1 -0
- data/spec/internal/config/puma.rb +38 -0
- data/spec/internal/config/routes.rb +17 -0
- data/spec/internal/config.ru +6 -0
- data/spec/internal/db/schema.rb +174 -0
- data/spec/internal/db/seeds.rb +167 -0
- data/spec/internal/esbuild.config.js +34 -0
- data/spec/internal/lib/tasks/.keep +0 -0
- data/spec/internal/lib/tasks/active_admin.rake +55 -0
- data/spec/internal/log/.keep +0 -0
- data/spec/internal/package-lock.json +1954 -0
- data/spec/internal/package.json +21 -0
- data/spec/internal/public/400.html +114 -0
- data/spec/internal/public/404.html +114 -0
- data/spec/internal/public/406-unsupported-browser.html +114 -0
- data/spec/internal/public/422.html +114 -0
- data/spec/internal/public/500.html +114 -0
- data/spec/internal/public/icon.png +0 -0
- data/spec/internal/public/icon.svg +3 -0
- data/spec/internal/public/robots.txt +1 -0
- data/spec/internal/script/.keep +0 -0
- data/spec/internal/storage/.keep +0 -0
- data/spec/internal/tailwind.config.js +23 -0
- data/spec/internal/vendor/.keep +0 -0
- data/spec/internal/yarn.lock +824 -0
- data/spec/rails_helper.rb +62 -0
- data/spec/spec_helper.rb +138 -0
- data/spec/support/active_admin_helpers.rb +17 -0
- data/spec/support/capybara.rb +8 -0
- data/spec/support/models.rb +11 -0
- data/spec/support/pluck_polyfill.rb +12 -0
- data/spec/support/reset_settings.rb +5 -0
- metadata +497 -0
@@ -0,0 +1,283 @@
|
|
1
|
+
# Migration Guide: ActiveAdmin Tom Select
|
2
|
+
|
3
|
+
This guide will help you migrate from the original `activeadmin-searchable_select` gem (or its fork) to the new `activeadmin-tom_select` gem, which uses Tom Select instead of Select2.
|
4
|
+
|
5
|
+
## Quick Start (New Installation)
|
6
|
+
|
7
|
+
For new installations without a previous version:
|
8
|
+
|
9
|
+
```bash
|
10
|
+
# Add to Gemfile
|
11
|
+
echo "gem 'activeadmin-tom_select', '~> 4.1.0'" >> Gemfile
|
12
|
+
bundle install
|
13
|
+
|
14
|
+
# Install NPM packages
|
15
|
+
npm install @rocket-sensei/activeadmin-tom_select tom-select
|
16
|
+
|
17
|
+
# Then follow Step 3 and onward below
|
18
|
+
```
|
19
|
+
|
20
|
+
## Why Update?
|
21
|
+
|
22
|
+
Our fork provides:
|
23
|
+
- ✅ Full ActiveAdmin 4.0 support
|
24
|
+
- ✅ Rails 8 and Propshaft compatibility
|
25
|
+
- ✅ Modern JavaScript bundler support (esbuild/webpack/importmap)
|
26
|
+
- ✅ **Tom Select** instead of Select2 (no jQuery dependency!)
|
27
|
+
- ✅ Virtual scroll for large datasets with pagination
|
28
|
+
- ✅ Proper NPM package distribution under @rocket-sensei scope
|
29
|
+
- ✅ Improved test suite with static ActiveAdmin registrations
|
30
|
+
- ✅ Active maintenance and support
|
31
|
+
|
32
|
+
## What's New in Version 4.1.0
|
33
|
+
|
34
|
+
- 🎉 **Tom Select**: Migrated from Select2 to Tom Select - no jQuery dependency!
|
35
|
+
- 📜 **Virtual Scroll**: Automatic pagination for large datasets
|
36
|
+
- 🚀 **Better Performance**: Lighter bundle size without jQuery
|
37
|
+
- 🔧 **Rails 8 Ready**: Full compatibility with Propshaft and Rails 8
|
38
|
+
- 📊 **98% Test Coverage**: Comprehensive test suite with SimpleCov
|
39
|
+
- 📦 **Modern JavaScript**: ES6 modules, vanilla JavaScript
|
40
|
+
|
41
|
+
## Migration Steps
|
42
|
+
|
43
|
+
### Step 1: Update your Gemfile
|
44
|
+
|
45
|
+
Replace the old gem with our fork:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
# Remove these:
|
49
|
+
# gem 'activeadmin-searchable_select'
|
50
|
+
# gem 'rs-activeadmin-searchable_select'
|
51
|
+
|
52
|
+
# Add this:
|
53
|
+
gem 'activeadmin-tom_select', '~> 4.1.0'
|
54
|
+
|
55
|
+
# For Rails 7, also ensure you have Propshaft:
|
56
|
+
gem 'propshaft' # Rails 8 includes this by default
|
57
|
+
```
|
58
|
+
|
59
|
+
Then run:
|
60
|
+
```bash
|
61
|
+
bundle install
|
62
|
+
```
|
63
|
+
|
64
|
+
### Step 2: Update JavaScript Dependencies
|
65
|
+
|
66
|
+
Remove old packages and install the new ones:
|
67
|
+
|
68
|
+
```bash
|
69
|
+
# Remove old packages if present
|
70
|
+
npm uninstall @codevise/activeadmin-searchable_select @rocket-sensei/activeadmin-searchable_select jquery select2
|
71
|
+
|
72
|
+
# Install new packages (Tom Select instead of Select2)
|
73
|
+
npm install @rocket-sensei/activeadmin-tom_select tom-select
|
74
|
+
```
|
75
|
+
|
76
|
+
### Step 3: Update JavaScript Imports
|
77
|
+
|
78
|
+
Update your `app/javascript/active_admin.js`:
|
79
|
+
|
80
|
+
#### Option A: Auto-initialization (Recommended)
|
81
|
+
|
82
|
+
```javascript
|
83
|
+
import "@activeadmin/activeadmin";
|
84
|
+
|
85
|
+
// Import Tom Select (no jQuery required!)
|
86
|
+
import TomSelect from 'tom-select';
|
87
|
+
window.TomSelect = TomSelect;
|
88
|
+
|
89
|
+
// Import and auto-initialize searchable selects
|
90
|
+
import { setupAutoInit } from '@rocket-sensei/activeadmin-tom_select';
|
91
|
+
setupAutoInit();
|
92
|
+
```
|
93
|
+
|
94
|
+
#### Option B: Manual initialization
|
95
|
+
|
96
|
+
```javascript
|
97
|
+
import "@activeadmin/activeadmin";
|
98
|
+
|
99
|
+
// Import Tom Select (no jQuery required!)
|
100
|
+
import TomSelect from 'tom-select';
|
101
|
+
window.TomSelect = TomSelect;
|
102
|
+
|
103
|
+
// Import the initialization function
|
104
|
+
import { initSearchableSelects } from '@rocket-sensei/activeadmin-tom_select';
|
105
|
+
|
106
|
+
// Initialize manually when needed
|
107
|
+
document.addEventListener('DOMContentLoaded', function() {
|
108
|
+
const selects = document.querySelectorAll('.searchable-select-input');
|
109
|
+
initSearchableSelects(selects);
|
110
|
+
});
|
111
|
+
```
|
112
|
+
|
113
|
+
### Step 4: Add Tom Select CSS
|
114
|
+
|
115
|
+
Add to your ActiveAdmin stylesheet (`app/assets/stylesheets/active_admin.css` or `.scss`):
|
116
|
+
|
117
|
+
```css
|
118
|
+
/* Import Tom Select styles */
|
119
|
+
@import 'tom-select/dist/css/tom-select.css';
|
120
|
+
|
121
|
+
/* Or if using CDN: */
|
122
|
+
@import url('https://cdn.jsdelivr.net/npm/tom-select@2.4.3/dist/css/tom-select.css');
|
123
|
+
|
124
|
+
/* Optional: Custom styling for searchable selects */
|
125
|
+
.searchable-select-input {
|
126
|
+
width: 100%;
|
127
|
+
}
|
128
|
+
|
129
|
+
/* Fix for ActiveAdmin form layout */
|
130
|
+
.ts-wrapper {
|
131
|
+
min-width: 50%;
|
132
|
+
}
|
133
|
+
```
|
134
|
+
|
135
|
+
### Step 5: For Importmap Users
|
136
|
+
|
137
|
+
If you're using importmap instead of esbuild, add to `config/importmap.rb`:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
pin "tom-select", to: "https://ga.jspm.io/npm:tom-select@2.4.3/dist/js/tom-select.complete.min.js"
|
141
|
+
pin "@rocket-sensei/activeadmin-tom_select", to: "@rocket-sensei--activeadmin-tom_select.js"
|
142
|
+
```
|
143
|
+
|
144
|
+
And update your `app/javascript/active_admin.js`:
|
145
|
+
|
146
|
+
```javascript
|
147
|
+
import "@activeadmin/activeadmin";
|
148
|
+
import TomSelect from "tom-select";
|
149
|
+
|
150
|
+
// Make Tom Select available globally
|
151
|
+
window.TomSelect = TomSelect;
|
152
|
+
|
153
|
+
// Import and auto-initialize searchable selects
|
154
|
+
import { setupAutoInit } from "@rocket-sensei/activeadmin-tom_select";
|
155
|
+
setupAutoInit();
|
156
|
+
```
|
157
|
+
|
158
|
+
### Step 6: Update Your ActiveAdmin Resources
|
159
|
+
|
160
|
+
The API remains the same, but ensure your resources are configured correctly:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
ActiveAdmin.register Product do
|
164
|
+
# For forms
|
165
|
+
form do |f|
|
166
|
+
f.inputs do
|
167
|
+
f.input :category, as: :searchable_select, ajax: true
|
168
|
+
f.input :tags, as: :searchable_select, ajax: true, multiple: true
|
169
|
+
end
|
170
|
+
f.actions
|
171
|
+
end
|
172
|
+
|
173
|
+
# For filters
|
174
|
+
filter :category, as: :searchable_select, ajax: true
|
175
|
+
filter :tags, as: :searchable_select, ajax: true, multiple: true
|
176
|
+
end
|
177
|
+
|
178
|
+
# Define searchable select options endpoint
|
179
|
+
ActiveAdmin.register Category do
|
180
|
+
searchable_select_options(
|
181
|
+
scope: Category.all,
|
182
|
+
text_attribute: :name
|
183
|
+
)
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
## Troubleshooting
|
188
|
+
|
189
|
+
### "TomSelect is not defined" Error
|
190
|
+
|
191
|
+
This usually happens when Tom Select isn't properly imported. Ensure:
|
192
|
+
|
193
|
+
1. Tom Select is imported in your JavaScript file
|
194
|
+
2. `window.TomSelect = TomSelect` is set to make it globally available
|
195
|
+
3. The NPM package is installed: `npm install tom-select`
|
196
|
+
|
197
|
+
### Styles Not Loading
|
198
|
+
|
199
|
+
1. Verify the Tom Select CSS import is in your stylesheet
|
200
|
+
2. Check that your build process includes the stylesheets
|
201
|
+
3. Clear your browser cache and restart Rails server
|
202
|
+
|
203
|
+
### Ajax Options Not Loading
|
204
|
+
|
205
|
+
1. Ensure `searchable_select_options` is defined in the target resource
|
206
|
+
2. Check browser console for any JavaScript errors
|
207
|
+
3. Verify the AJAX URLs are correct in the network tab
|
208
|
+
|
209
|
+
### Multiple Select Not Working
|
210
|
+
|
211
|
+
Ensure you've added `multiple: true` to both the input and the corresponding association:
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
# In your model
|
215
|
+
has_many :tags
|
216
|
+
|
217
|
+
# In your ActiveAdmin resource
|
218
|
+
f.input :tags, as: :searchable_select, ajax: true, multiple: true
|
219
|
+
```
|
220
|
+
|
221
|
+
## Version Compatibility
|
222
|
+
|
223
|
+
- **Ruby**: >= 3.0
|
224
|
+
- **Rails**: >= 7.0 (optimized for Rails 8)
|
225
|
+
- **ActiveAdmin**: ~> 4.0.0.beta
|
226
|
+
- **Node.js**: >= 18.0
|
227
|
+
|
228
|
+
## Differences from Original Gem
|
229
|
+
|
230
|
+
1. **Tom Select instead of Select2**: No jQuery dependency required!
|
231
|
+
2. **NPM Scope**: Package is now `@rocket-sensei/activeadmin-tom_select`
|
232
|
+
3. **Gem Name**: Gem is now `activeadmin-tom_select`
|
233
|
+
4. **Virtual Scroll**: Automatic pagination for large datasets
|
234
|
+
5. **Rails 8 Ready**: Full support for Propshaft and modern Rails
|
235
|
+
6. **Better Performance**: Smaller bundle size without jQuery
|
236
|
+
7. **Modern JavaScript**: ES6 modules and vanilla JavaScript
|
237
|
+
|
238
|
+
## Migration Notes for Select2 Users
|
239
|
+
|
240
|
+
If you're migrating from a Select2-based version to this Tom Select version:
|
241
|
+
|
242
|
+
### CSS Class Changes
|
243
|
+
| Select2 Class | Tom Select Class |
|
244
|
+
|---------------|------------------|
|
245
|
+
| `.select2-container` | `.ts-wrapper` |
|
246
|
+
| `.select2-dropdown` | `.ts-dropdown` |
|
247
|
+
| `.select2-selection` | `.ts-control` |
|
248
|
+
| `.select2-results` | `.ts-dropdown-content` |
|
249
|
+
| `.select2-search__field` | `.ts-control input` |
|
250
|
+
|
251
|
+
### JavaScript API Changes
|
252
|
+
- No jQuery dependency required
|
253
|
+
- Tom Select instances are attached directly to the DOM element: `element.tomselect`
|
254
|
+
- Options are configured differently (see Tom Select documentation)
|
255
|
+
|
256
|
+
### Features
|
257
|
+
- Virtual scroll is automatically enabled for AJAX-loaded options
|
258
|
+
- Clear button is included by default (can be disabled with `clearable: false`)
|
259
|
+
- Pagination now uses 1-based indexing (page 1 is the first page)
|
260
|
+
|
261
|
+
## Need Help?
|
262
|
+
|
263
|
+
If you encounter issues:
|
264
|
+
1. Check that all JavaScript packages are installed
|
265
|
+
2. Verify your bundler configuration
|
266
|
+
3. Clear browser cache and Rails tmp/cache
|
267
|
+
4. Check browser console for errors
|
268
|
+
|
269
|
+
For bug reports or questions, please visit:
|
270
|
+
https://github.com/rs-pro/activeadmin-tom_select/issues
|
271
|
+
|
272
|
+
## Contributing
|
273
|
+
|
274
|
+
We welcome contributions! Please:
|
275
|
+
1. Fork the repository
|
276
|
+
2. Create a feature branch
|
277
|
+
3. Write tests for your changes
|
278
|
+
4. Ensure all tests pass with `bundle exec rspec`
|
279
|
+
5. Submit a pull request
|
280
|
+
|
281
|
+
## License
|
282
|
+
|
283
|
+
This gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/docs/normal.png
ADDED
Binary file
|
@@ -0,0 +1,320 @@
|
|
1
|
+
# Propshaft: A Modern Asset Pipeline for Rails
|
2
|
+
|
3
|
+
Propshaft is an asset pipeline library for Rails. It's built for an era where bundling assets to save on HTTP connections is no longer urgent, where JavaScript and CSS are either compiled by dedicated Node.js bundlers or served directly to the browsers, and where increases in bandwidth have made the need for minification less pressing. These factors allow for a dramatically simpler and faster asset pipeline compared to previous options, like [Sprockets](https://github.com/rails/sprockets-rails).
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
|
7
|
+
1. [Overview](#overview)
|
8
|
+
2. [Architecture](#architecture)
|
9
|
+
3. [Asset Serving in Development/Test Mode](#asset-serving-in-developmenttest-mode)
|
10
|
+
4. [Configuration and Asset Paths](#configuration-and-asset-paths)
|
11
|
+
5. [Test Environment Specifics](#test-environment-specifics)
|
12
|
+
6. [JavaScript and CSS Bundling Integration](#javascript-and-css-bundling-integration)
|
13
|
+
7. [Common Patterns and Best Practices](#common-patterns-and-best-practices)
|
14
|
+
|
15
|
+
## Overview
|
16
|
+
|
17
|
+
So that's what Propshaft doesn't do. Here's what it does provide:
|
18
|
+
|
19
|
+
1. **Configurable load path**: You can register directories from multiple places in your app and gems, and reference assets from all of these paths as though they were one.
|
20
|
+
1. **Digest stamping**: All assets in the load path will be copied (or compiled) in a precompilation step for production that also stamps all of them with a digest hash, so you can use long-expiry cache headers for better performance. The digested assets can be referred to through their logical path because the processing leaves a manifest file that provides a way to translate.
|
21
|
+
1. **Development server**: There's no need to precompile the assets in development. You can refer to them via the same asset_path helpers and they'll be served by a development server.
|
22
|
+
1. **Basic compilers**: Propshaft was explicitly not designed to provide full transpiler capabilities. You can get that better elsewhere. But it does offer a simple input->output compiler setup that by default is used to translate `url(asset)` function calls in CSS to `url(digested-asset)` instead and source mapping comments likewise.
|
23
|
+
|
24
|
+
## Architecture
|
25
|
+
|
26
|
+
### Core Components
|
27
|
+
|
28
|
+
```
|
29
|
+
Propshaft::Assembly
|
30
|
+
├── LoadPath # Asset discovery and caching
|
31
|
+
├── Resolver # Path resolution (Dynamic/Static)
|
32
|
+
│ ├── Dynamic # Development/test mode
|
33
|
+
│ └── Static # Production with manifest
|
34
|
+
├── Server # Rack middleware for asset serving
|
35
|
+
├── Processor # Precompilation and digesting
|
36
|
+
├── Compilers # Asset transformation
|
37
|
+
└── Manifest # Asset mapping and integrity hashes
|
38
|
+
```
|
39
|
+
|
40
|
+
### Key Classes
|
41
|
+
|
42
|
+
#### `Propshaft::Assembly`
|
43
|
+
Central coordinator that manages all components. Created during Rails initialization and accessible via `Rails.application.assets`.
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
# Core assembly configuration
|
47
|
+
Rails.application.configure do
|
48
|
+
app.assets = Propshaft::Assembly.new(app.config.assets)
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
#### `Propshaft::LoadPath`
|
53
|
+
Manages asset discovery across multiple directories. Automatically includes:
|
54
|
+
- `app/assets/**/*` (application assets)
|
55
|
+
- `lib/assets/**/*` (library assets)
|
56
|
+
- `vendor/assets/**/*` (third-party assets)
|
57
|
+
- Engine assets from all loaded gems
|
58
|
+
|
59
|
+
#### `Propshaft::Resolver::Dynamic`
|
60
|
+
Used in development and test environments. Resolves assets on-demand without requiring precompilation.
|
61
|
+
|
62
|
+
#### `Propshaft::Resolver::Static`
|
63
|
+
Used in production. Relies on `.manifest.json` for fast asset resolution.
|
64
|
+
|
65
|
+
## Asset Serving in Development/Test Mode
|
66
|
+
|
67
|
+
### Dynamic Resolution Process
|
68
|
+
|
69
|
+
1. **Request Interception**: `Propshaft::Server` middleware catches requests to `/assets/*`
|
70
|
+
2. **Asset Discovery**: `LoadPath#find` searches configured paths for matching assets
|
71
|
+
3. **Compilation**: Assets pass through registered compilers
|
72
|
+
4. **Cache Headers**: Aggressive caching with ETags and immutable cache-control
|
73
|
+
5. **Response**: Compiled content served with appropriate MIME type
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
# Server middleware in action (simplified)
|
77
|
+
def call(env)
|
78
|
+
if path.start_with?(@assembly.prefix) && (asset = @assembly.load_path.find(path))
|
79
|
+
[200, {
|
80
|
+
'Content-Type' => asset.content_type,
|
81
|
+
'ETag' => "\"#{asset.digest}\"",
|
82
|
+
'Cache-Control' => 'public, max-age=31536000, immutable'
|
83
|
+
}, [asset.compiled_content]]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
### Cache Sweeping
|
89
|
+
|
90
|
+
In development/test, Propshaft monitors file changes:
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
config.assets.sweep_cache = Rails.env.development?
|
94
|
+
```
|
95
|
+
|
96
|
+
When enabled, before each request:
|
97
|
+
1. File watcher checks for modifications
|
98
|
+
2. Asset cache cleared if changes detected
|
99
|
+
3. New assets discovered and cached
|
100
|
+
|
101
|
+
## Configuration and Asset Paths
|
102
|
+
|
103
|
+
### Default Configuration (from Railtie)
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
config.assets.paths = [] # Auto-populated
|
107
|
+
config.assets.excluded_paths = [] # Paths to exclude
|
108
|
+
config.assets.version = "1" # Cache invalidation
|
109
|
+
config.assets.prefix = "/assets" # URL prefix
|
110
|
+
config.assets.server = Rails.env.development? || Rails.env.test?
|
111
|
+
config.assets.sweep_cache = Rails.env.development?
|
112
|
+
```
|
113
|
+
|
114
|
+
### Path Resolution Order
|
115
|
+
|
116
|
+
1. **Application paths**: `app/assets/**/*`
|
117
|
+
2. **Library paths**: `lib/assets/**/*`
|
118
|
+
3. **Vendor paths**: `vendor/assets/**/*`
|
119
|
+
4. **Engine paths**: From all loaded Rails engines/gems
|
120
|
+
|
121
|
+
Paths are automatically prioritized:
|
122
|
+
- Application assets take precedence over engine assets
|
123
|
+
- Later additions to load path have lower priority
|
124
|
+
|
125
|
+
|
126
|
+
## Installation
|
127
|
+
|
128
|
+
With Rails 8, Propshaft is the default asset pipeline for new applications. With Rails 7, you can start a new application with propshaft using `rails new myapp -a propshaft`. For existing applications, check the [upgrade guide](https://github.com/rails/propshaft/blob/main/UPGRADING.md) which contains step-by-step instructions.
|
129
|
+
|
130
|
+
## Usage
|
131
|
+
|
132
|
+
Propshaft makes all the assets from all the paths it's been configured with through `config.assets.paths` available for serving and will copy all of them into `public/assets` when precompiling. This is unlike Sprockets, which did not copy over assets that hadn't been explicitly included in one of the bundled assets.
|
133
|
+
|
134
|
+
You can however exempt directories that have been added through the `config.assets.excluded_paths`. This is useful if you're for example using `app/assets/stylesheets` exclusively as a set of inputs to a compiler like Dart Sass for Rails, and you don't want these input files to be part of the load path. (Remember you need to add full paths, like `Rails.root.join("app/assets/stylesheets")`).
|
135
|
+
|
136
|
+
These assets can be referenced through their logical path using the normal helpers like `asset_path`, `image_tag`, `javascript_include_tag`, and all the other asset helper tags. These logical references are automatically converted into digest-aware paths in production when `assets:precompile` has been run (through a JSON mapping file found in `public/assets/.manifest.json`).
|
137
|
+
|
138
|
+
## Referencing digested assets in CSS and JavaScript
|
139
|
+
|
140
|
+
Propshaft will automatically convert asset references in CSS to use the digested file names. So `background: url("/bg/pattern.svg")` is converted to `background: url("/assets/bg/pattern-2169cbef.svg")` before the stylesheet is served.
|
141
|
+
|
142
|
+
For JavaScript, you'll have to manually trigger this transformation by using the `RAILS_ASSET_URL` pseudo-method. It's used like this:
|
143
|
+
|
144
|
+
```javascript
|
145
|
+
export default class extends Controller {
|
146
|
+
init() {
|
147
|
+
this.img = RAILS_ASSET_URL("/icons/trash.svg")
|
148
|
+
}
|
149
|
+
}
|
150
|
+
```
|
151
|
+
|
152
|
+
That'll turn into:
|
153
|
+
|
154
|
+
```javascript
|
155
|
+
export default class extends Controller {
|
156
|
+
init() {
|
157
|
+
this.img = "/assets/icons/trash-54g9cbef.svg"
|
158
|
+
}
|
159
|
+
}
|
160
|
+
```
|
161
|
+
|
162
|
+
## Bypassing the digest step
|
163
|
+
|
164
|
+
If you need to put multiple files that refer to each other through Propshaft, like a JavaScript file and its source map, you have to digest these files in advance to retain stable file names. Propshaft looks for the specific pattern of `-[digest].digested.js` as the postfix to any asset file as an indication that the file has already been digested.
|
165
|
+
|
166
|
+
## Subresource Integrity (SRI)
|
167
|
+
|
168
|
+
Propshaft supports Subresource Integrity (SRI) to help protect against malicious modifications of assets. SRI allows browsers to verify that resources fetched from CDNs or other sources haven't been tampered with by checking cryptographic hashes.
|
169
|
+
|
170
|
+
### Enabling SRI
|
171
|
+
|
172
|
+
To enable SRI support, configure the hash algorithm in your Rails application:
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
config.assets.integrity_hash_algorithm = "sha384"
|
176
|
+
```
|
177
|
+
|
178
|
+
Valid hash algorithms include:
|
179
|
+
- `"sha256"` - SHA-256 (most common)
|
180
|
+
- `"sha384"` - SHA-384 (recommended for enhanced security)
|
181
|
+
- `"sha512"` - SHA-512 (strongest)
|
182
|
+
|
183
|
+
### Using SRI in your views
|
184
|
+
|
185
|
+
Once configured, you can enable SRI by passing the `integrity: true` option to asset helpers:
|
186
|
+
|
187
|
+
```erb
|
188
|
+
<%= stylesheet_link_tag "application", integrity: true %>
|
189
|
+
<%= javascript_include_tag "application", integrity: true %>
|
190
|
+
```
|
191
|
+
|
192
|
+
This generates HTML with integrity hashes:
|
193
|
+
|
194
|
+
```html
|
195
|
+
<link rel="stylesheet" href="/assets/application-abc123.css"
|
196
|
+
integrity="sha384-xyz789...">
|
197
|
+
<script src="/assets/application-def456.js"
|
198
|
+
integrity="sha384-uvw012..."></script>
|
199
|
+
```
|
200
|
+
|
201
|
+
**Important**: SRI only works in secure contexts (HTTPS) or during local development. The integrity hashes are automatically omitted when serving over HTTP in production for security reasons.
|
202
|
+
|
203
|
+
### Bulk stylesheet inclusion with SRI
|
204
|
+
|
205
|
+
Propshaft extends `stylesheet_link_tag` with special symbols for bulk inclusion:
|
206
|
+
|
207
|
+
```erb
|
208
|
+
<%= stylesheet_link_tag :all, integrity: true %> <!-- All stylesheets -->
|
209
|
+
<%= stylesheet_link_tag :app, integrity: true %> <!-- Only app/assets stylesheets -->
|
210
|
+
```
|
211
|
+
|
212
|
+
## Improving performance in development
|
213
|
+
|
214
|
+
Before every request Propshaft checks if any asset was updated to decide if a cache sweep is needed. This verification is done using the application's configured file watcher which, by default, is `ActiveSupport::FileUpdateChecker`.
|
215
|
+
|
216
|
+
If you have a lot of assets in your project, you can improve performance by adding the `listen` gem to the development group in your Gemfile, and this line to the `development.rb` environment file:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
220
|
+
```
|
221
|
+
|
222
|
+
|
223
|
+
## Migrating from Sprockets
|
224
|
+
|
225
|
+
Propshaft does a lot less than Sprockets, by design, so it might well be a fair bit of work to migrate if it's even desirable. This is particularly true if you rely on Sprockets to provide any form of transpiling, like CoffeeScript or Sass, or if you rely on any gems that do. You'll need to either stop transpiling or use a Node-based transpiler, like those in [`jsbundling-rails`](https://github.com/rails/jsbundling-rails) and [`cssbundling-rails`](https://github.com/rails/cssbundling-rails).
|
226
|
+
|
227
|
+
On the other hand, if you're already bundling JavaScript and CSS through a Node-based setup, then Propshaft is going to slot in easily. Since you don't need another tool to bundle or transpile. Just to digest and serve.
|
228
|
+
|
229
|
+
But for greenfield apps using the default import-map approach, Propshaft can also work well, if you're able to deal with vanilla CSS.
|
230
|
+
|
231
|
+
|
232
|
+
## License
|
233
|
+
|
234
|
+
Propshaft is released under the [MIT License](https://opensource.org/licenses/MIT).
|
235
|
+
|
236
|
+
## Compilation and Digesting
|
237
|
+
|
238
|
+
### Asset Processing Pipeline
|
239
|
+
|
240
|
+
1. **Discovery**: `LoadPath` finds all assets matching patterns
|
241
|
+
2. **Compilation**: Each asset processed through registered compilers
|
242
|
+
3. **Digesting**: Content hash generated using SHA1 + version string
|
243
|
+
4. **Output**: Files written to `config.assets.output_path` (default: `public/assets/`)
|
244
|
+
|
245
|
+
### Built-in Compilers
|
246
|
+
|
247
|
+
#### CSS Asset URL Compiler
|
248
|
+
Transforms relative URLs to digested versions:
|
249
|
+
|
250
|
+
```css
|
251
|
+
/* Input */
|
252
|
+
background: url('./hero.jpg');
|
253
|
+
|
254
|
+
/* Output */
|
255
|
+
background: url('/assets/hero-abc123.jpg');
|
256
|
+
```
|
257
|
+
|
258
|
+
#### JavaScript Asset URL Compiler
|
259
|
+
Processes `RAILS_ASSET_URL()` pseudo-functions:
|
260
|
+
|
261
|
+
```javascript
|
262
|
+
// Input
|
263
|
+
const icon = RAILS_ASSET_URL('./icon.svg');
|
264
|
+
|
265
|
+
// Output
|
266
|
+
const icon = '/assets/icon-def456.svg';
|
267
|
+
```
|
268
|
+
|
269
|
+
#### Source Map Compiler
|
270
|
+
Updates source map references to match digested filenames.
|
271
|
+
|
272
|
+
### Manifest Generation
|
273
|
+
|
274
|
+
The manifest file (`.manifest.json`) maps logical paths to digested paths:
|
275
|
+
|
276
|
+
```json
|
277
|
+
{
|
278
|
+
"application.js": {
|
279
|
+
"digested_path": "application-abc123.js",
|
280
|
+
"integrity": "sha384-xyz789..."
|
281
|
+
}
|
282
|
+
}
|
283
|
+
```
|
284
|
+
|
285
|
+
## Asset Organization
|
286
|
+
|
287
|
+
```
|
288
|
+
app/assets/
|
289
|
+
├── builds/ # jsbundling-rails/cssbundling-rails output
|
290
|
+
│ ├── application.js
|
291
|
+
│ └── application.css
|
292
|
+
├── images/ # Static assets
|
293
|
+
│ └── logo.svg
|
294
|
+
├── stylesheets/ # SCSS source (often excluded)
|
295
|
+
│ └── application.scss
|
296
|
+
└── javascripts/ # JS source (often excluded)
|
297
|
+
└── application.js
|
298
|
+
```
|
299
|
+
|
300
|
+
## Error Handling
|
301
|
+
|
302
|
+
### Missing Asset Handling
|
303
|
+
```ruby
|
304
|
+
# Propshaft raises MissingAssetError for missing assets
|
305
|
+
begin
|
306
|
+
asset_path('nonexistent.js')
|
307
|
+
rescue Propshaft::MissingAssetError => e
|
308
|
+
Rails.logger.error "Missing asset: #{e.message}"
|
309
|
+
# Fallback logic
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
313
|
+
### Development Debugging
|
314
|
+
```ruby
|
315
|
+
# Show all available assets
|
316
|
+
rake assets:reveal
|
317
|
+
|
318
|
+
# Show asset full paths
|
319
|
+
rake assets:reveal:full
|
320
|
+
```
|