rolemodel-rails 2.4.1 → 2.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab7ce13bbc4227eedf045d0ce216b341049445daab3856f6b1d435f9799ffb53
4
- data.tar.gz: 5e5ce9316bd729ad8dd3bacf21d849aa274d9c870768fd6d7866f5cb2ae093bf
3
+ metadata.gz: 9dec1145653dd0763c5df4ac4f21c6f2f2961ee3776fb30cf100529aacdb061e
4
+ data.tar.gz: be40ee27b145627cb7945eb8a7f478ef2520c7b0b6f8ab247466cfa45fdd57b4
5
5
  SHA512:
6
- metadata.gz: 74101aeb24869c8e84b1bb645573df9b95e97dfaf79d8c0d4d5980efafe46c64f414b9afded5511a9f4ae8bf0ee2339074e841710f08ba8314d996bd27a847db
7
- data.tar.gz: 6438c65a802314259eaa3c0ebe8cdd94301e95f3dc5a1e82c1809f2285c219f731953da1b1bfe45ac5c877273a958b7ba5cd17f6052d4500dd12302590f194c4
6
+ metadata.gz: 8be97f77d771928e00785f143c43424d5557b395d2115dcd08c2243814037a830f081e51722a3a4f2318efc6467d7ca9fa4e643c68d31a2ac3af12ddc45e240c
7
+ data.tar.gz: ad3e144e4d3400e60f443cbe577484dafcb5640af32c803c92cdb9dedb18076b31628b4c801c91615d37e70fb69c99e0665b3de48487a1db2287459186157d9e
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Executable Best Practices for Rails apps, based on RoleModel's best approaches
4
4
 
5
+ ![title image](title_image.png)
6
+
5
7
  Attempts to solve the pain of:
6
8
 
7
9
  * Setup of a new Rails app is harder than it needs to be
@@ -105,6 +107,7 @@ bin/rails g
105
107
  * [GoodJob](./lib/generators/rolemodel/good_job)
106
108
  * [Editors](./lib/generators/rolemodel/editors)
107
109
  * [Tailored Select](./lib/generators/rolemodel/tailored_select)
110
+ * [Tom Select](./lib/generators/rolemodel/tom_select)
108
111
  * [Lograge](./lib/generators/rolemodel/lograge)
109
112
 
110
113
  ## Utilities
@@ -324,11 +327,24 @@ e.g.
324
327
  bin/new_generator testing/fantasitic_specs 'A Fantastic Testing Framework'
325
328
  ```
326
329
 
327
- We use the embeded Rails apps (`example_rails_current` & `example_rails_legacy`) to test generators against. They reference the rolemodel-rails gem by local path,
328
- so you can navigate into one of them and run your generator for immediate feedback while developing.
330
+ We use the embeded Rails apps (`example_rails_current` & `example_rails_legacy`) to test generators against. The specs run generators against a copy of `example_rails_current`.
331
+ `example_rails_legacy` references the rolemodel-rails gem by local path, so you can navigate into it and run your generator for immediate feedback while developing.
329
332
 
330
333
  ## Testing
331
334
 
335
+ Run the full suite with `bin/ci`. It splits the spec files across parallel processes, so it is much faster than a plain `bundle exec rspec`. CI uses the same command.
336
+
337
+ ```shell
338
+ bin/ci
339
+ ```
340
+
341
+ During development, you can still run a single file or example with RSpec directly:
342
+
343
+ ```shell
344
+ bundle exec rspec spec/generators/rolemodel/turbo/form_generator_spec.rb
345
+ bundle exec rspec spec/generators/rolemodel/turbo/form_generator_spec.rb:11
346
+ ```
347
+
332
348
  Generator specs should be added to the [spec](./spec) directory.
333
349
 
334
350
  Setup & Teardown of the test-dummy app is handled for you. All you need to do is run the provided helper method:
@@ -4,20 +4,31 @@
4
4
 
5
5
  ## What you get
6
6
 
7
+ * [Editors](./editors)
7
8
  * [Github](./github)
8
9
  * [GoodJob](./good_job)
9
10
  * [Heroku](./heroku)
11
+ * [Kaminari](./kaminari)
10
12
  * [Linters](./linters)
13
+ * [Lograge](./lograge)
11
14
  * [Mailers](./mailers)
12
- * [Modals](./modals)
13
15
  * [Optics](./optics)
16
+ * [React](./react)
14
17
  * [README](./readme)
15
18
  * [SaaS](./saas)
19
+ * [Sentry](./sentry)
16
20
  * [SimpleForm](./simple_form)
17
21
  * [Slim](./slim)
18
22
  * [SoftDestroyable](./soft_destroyable)
19
23
  * [Source Map](./source_map)
20
24
  * [Testing](./testing)
25
+ * [Tom Select](./tom_select)
26
+ * [Turbo](./turbo/all)
27
+ * [Ready](./turbo/ready)
28
+ * [Confirm](./turbo/confirm)
29
+ * [Modals](./turbo/modals)
30
+ * [Form](./turbo/form)
31
+ * [UI Components](./ui_components)
21
32
  * [Webpack](./webpack)
22
33
 
23
34
  ## Helpful documentation
@@ -12,7 +12,9 @@ module Rolemodel
12
12
  generate 'rolemodel:slim'
13
13
  generate 'rolemodel:optics:all'
14
14
  generate 'rolemodel:testing:all'
15
+ generate 'rolemodel:turbo:all'
15
16
  generate 'rolemodel:simple_form'
17
+ generate 'rolemodel:tom_select'
16
18
  generate 'rolemodel:soft_destroyable'
17
19
  generate 'rolemodel:saas:all'
18
20
  generate 'rolemodel:mailers'
@@ -21,6 +21,7 @@ app you can push straight to Heroku right after generation.
21
21
  - [Testing](../testing) — RSpec, FactoryBot, parallel_tests, TestProf
22
22
  (pass `--js-runner` to include jasmine-playwright-runner)
23
23
  - [SimpleForm](../simple_form) — SimpleForm with our configuration
24
+ - [Tom Select](../tom_select) — Tom Select with a Stimulus controller and SimpleForm inputs
24
25
  - [Linters](../linters) — Rubocop and ESLint
25
26
  - [UI Components](../ui_components) — flash, the modal pattern, & Turbo 8 support
26
27
  - [Editors](../editors) — EditorConfig and recommended VSCode extensions
@@ -7,6 +7,7 @@ module Rolemodel
7
7
  generate 'rolemodel:webpack'
8
8
  generate 'rolemodel:optics:all'
9
9
  generate 'rolemodel:simple_form'
10
+ generate 'rolemodel:tom_select'
10
11
  generate 'rolemodel:testing:all'
11
12
  generate 'rolemodel:turbo:all'
12
13
  generate 'rolemodel:ui_components:flash'
@@ -10,3 +10,10 @@ Adds icon helper for handling icon generation using the provider of your choice.
10
10
 
11
11
  Run the generator with the `-i` option to install the IconBuilders themselves in your
12
12
  application's `lib/rolemodel/optics` directory.
13
+
14
+ ## SVG icons (for example, Hugeicons)
15
+
16
+ Use `--custom`. Put each SVG in `app/assets/images/icons/`, and `icon('name')` inlines it with
17
+ Optics' `.icon--svg` modifier (Optics 2.5+), so size, weight and emphasis modifiers apply. A subfolder
18
+ works as part of the name: `icon('duotone-rounded/home-01')`. Draw the SVGs with `currentColor`
19
+ so they follow the text color.
@@ -8,9 +8,7 @@ module Rolemodel
8
8
  desc: 'Install the tailored_select experimental component input'
9
9
 
10
10
  def add_gem
11
- Bundler.with_unbundled_env do
12
- bundle_command 'add simple_form'
13
- end
11
+ bundle_command 'add simple_form'
14
12
  end
15
13
 
16
14
  def add_files
@@ -0,0 +1,15 @@
1
+ # Tom Select Generator
2
+
3
+ `rails g rolemodel:tom_select`
4
+
5
+ ## What you get
6
+
7
+ * The [Tom Select](https://tom-select.js.org/) package and its stylesheet
8
+ * A `tom-select` Stimulus controller that survives Turbo morphs
9
+ * `tom_select` and `grouped_tom_select` SimpleForm inputs, if the app uses SimpleForm
10
+
11
+ ```slim
12
+ = f.input :project_manager, as: :tom_select, collection: User.all
13
+ = f.input :substance, as: :tom_select, collection: Substance.all, allow_create: true
14
+ = f.input :material, as: :grouped_tom_select, collection: Category.all, group_method: :materials
15
+ ```
@@ -0,0 +1,7 @@
1
+ Description:
2
+ Installs the tom-select package, its stylesheet, and a tom-select Stimulus
3
+ controller. If the app is using SimpleForm, it also installs the matching
4
+ tom_select and grouped_tom_select SimpleForm inputs.
5
+
6
+ Example:
7
+ rails generate rolemodel:tom_select
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class GroupedTomSelectInput < SimpleForm::Inputs::GroupedCollectionSelectInput
4
+ def input(wrapper_options = nil)
5
+ super(merge_wrapper_options(tom_select_options, wrapper_options))
6
+ end
7
+
8
+ def input_html_classes = []
9
+
10
+ private
11
+
12
+ def tom_select_options
13
+ { data: { controller: 'tom-select', action: TomSelectInput::MORPH_ACTIONS } }
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TomSelectInput < SimpleForm::Inputs::CollectionSelectInput
4
+ MORPH_ACTIONS = 'turbo:before-morph-element->tom-select#disconnect:self turbo:morph-element->tom-select#afterMorph:self'
5
+
6
+ def input(wrapper_options = nil)
7
+ super(merge_wrapper_options(tom_select_options, wrapper_options))
8
+ end
9
+
10
+ def input_html_classes = []
11
+
12
+ private
13
+
14
+ def tom_select_options
15
+ { data: { controller: 'tom-select', tom_select_create_value: options.delete(:allow_create) || false, action: MORPH_ACTIONS } }
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import TomSelect from 'tom-select'
3
+
4
+ export default class extends Controller {
5
+ static values = { create: Boolean }
6
+
7
+ connect() {
8
+ this.tomSelect = new TomSelect(this.element, this.settings)
9
+ this.element.tomSelect = this.tomSelect
10
+ }
11
+
12
+ disconnect() {
13
+ // In case a Turbo morph is happening, we need to update TomSelect's revertSettings,
14
+ // in order to preserve the current value(s) when reconnecting.
15
+ this.tomSelect.revertSettings.innerHTML = this.element.innerHTML
16
+
17
+ this.tomSelect.destroy()
18
+ delete this.element.tomSelect
19
+ }
20
+
21
+ afterMorph() {
22
+ queueMicrotask(() => this.connect())
23
+ }
24
+
25
+ get settings() {
26
+ return {
27
+ create: this.createValue,
28
+ persist: false,
29
+ maxOptions: null,
30
+ delimiter: null,
31
+ maxItems: this.element.multiple ? null : 1,
32
+ plugins: this.element.multiple ? ['remove_button'] : [],
33
+ onItemAdd: () => this.tomSelect.setTextboxValue(''), // clear the input text box when a selection is made
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolemodel
4
+ class TomSelectGenerator < GeneratorBase
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ def add_tom_select_package
8
+ say 'Installing Tom Select package', :green
9
+
10
+ yarn_command 'add tom-select'
11
+ end
12
+
13
+ def import_stylesheet
14
+ say 'Importing Tom Select stylesheet', :green
15
+
16
+ prepend_to_file application_stylesheet_path, <<~CSS
17
+ @import 'tom-select/dist/css/tom-select.css';
18
+ CSS
19
+ end
20
+
21
+ def add_stimulus_controller
22
+ say 'Adding Tom Select Stimulus controller', :green
23
+
24
+ directory 'app/javascript/controllers'
25
+
26
+ rails_command 'stimulus:manifest:update'
27
+ end
28
+
29
+ def add_simple_form_inputs
30
+ return unless simple_form?
31
+
32
+ say 'Installing the Tom Select SimpleForm inputs', :green
33
+
34
+ directory 'app/inputs'
35
+ end
36
+
37
+ private
38
+
39
+ def simple_form?
40
+ File.exist?(File.expand_path('config/initializers/simple_form.rb', destination_root))
41
+ end
42
+ end
43
+ end
@@ -13,4 +13,4 @@ Runs all Turbo Generators
13
13
  - Turbo meta tags in HEAD
14
14
  - TurboConfirm setup
15
15
  - Turbo Frame Modals setup
16
- - Turbo-Form stimulus controller
16
+ - turbo_form gem for dynamic forms
@@ -1,8 +1,9 @@
1
1
  # Form Generator
2
2
 
3
- Enable the "turbo-fetch" (now Turbo-Form) pattern in new/existing rails apps.
3
+ Enable dynamic form interactions in new/existing rails apps with the [turbo_form](https://github.com/OutlawAndy/turbo_form) gem.
4
4
 
5
5
  ## What you get
6
6
 
7
- - turbo-form controller
8
- - system spec helper
7
+ - the `turbo_form` gem, set up by its `turbo_form:install` generator
8
+
9
+ Draw the `:turbo_form` route concern on each resource with a dynamic form. See the gem's README for usage.
@@ -1,28 +1,13 @@
1
1
  module Rolemodel
2
2
  module Turbo
3
3
  class FormGenerator < GeneratorBase
4
- source_root File.expand_path('templates', __dir__)
4
+ def install_turbo_form
5
+ say 'Installing turbo_form', :green
5
6
 
6
- def add_rails_request_package
7
- say 'Installing @rails/request.js package', :green
7
+ bundle_command 'add turbo_form'
8
8
 
9
- yarn_command 'add @rails/request.js'
10
- end
11
-
12
- def add_stimulus_controller
13
- say 'Adding Turbo Form Stimulus Controller', :green
14
-
15
- directory 'app/javascript/controllers'
16
-
17
- rails_command 'stimulus:manifest:update'
18
- end
19
-
20
- def add_system_spec_helper
21
- say 'Adding Turbo Form System Spec Helper', :green
22
-
23
- directory 'spec/support/helpers'
9
+ generate 'turbo_form:install'
24
10
  end
25
11
  end
26
12
  end
27
13
  end
28
-
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # CustomIconBuilder is an IconBuilder that allows for custom SVG icons to be used in the application.
4
+ # It inlines app/assets/images/icons/<name>.svg, so any SVG set works, including Hugeicons:
5
+ #
6
+ # icon('home-01') # icons/home-01.svg
7
+ # icon('duotone-rounded/home-01') # icons/duotone-rounded/home-01.svg
4
8
  class Rolemodel::Optics::CustomIconBuilder < Rolemodel::Optics::IconBuilder
5
9
  def self.flash_icons
6
10
  {
@@ -15,28 +19,28 @@ class Rolemodel::Optics::CustomIconBuilder < Rolemodel::Optics::IconBuilder
15
19
  :span
16
20
  end
17
21
 
18
- # NOTE: This does not work with propshaft and the latest asset pipeline
19
- # We will need to find a way to make this work in Rails 8 with propshaft.
20
- def tag_contents # rubocop:disable Metrics/AbcSize
22
+ def tag_contents
21
23
  # Inspired by https://blog.cloud66.com/using-svgs-in-a-rails-stack
22
- asset = Rails.application.assets_manifest.find_sources(svg_path).first
24
+ source = svg_source
25
+ return "<!-- SVG #{svg_path} not found -->".html_safe if source.nil?
23
26
 
24
- if asset
25
- file = asset.source.force_encoding('UTF-8')
26
- doc = Nokogiri::HTML::DocumentFragment.parse file
27
- svg = doc.at_css 'svg'
28
- svg['class'] = options[:class] if options[:class].present?
27
+ # These SVG files can safely be marked html_safe since we created them and they are part of this app's code.
28
+ source.dup.force_encoding('UTF-8').html_safe
29
+ end
30
+
31
+ # Propshaft and Sprockets find assets differently.
32
+ def svg_source
33
+ assets = Rails.application.assets
34
+ if assets.respond_to?(:load_path)
35
+ assets.load_path.find(svg_path)&.content
29
36
  else
30
- doc = "<!-- SVG #{svg_path} not found -->"
37
+ Rails.application.assets_manifest.find_sources(svg_path).first&.source
31
38
  end
32
-
33
- # These SVG files can safely be marked html_safe since we created them and they are part of this app's code.
34
- raw doc
35
39
  end
36
40
 
37
41
  def tag_classes
38
42
  [
39
- 'custom-icons',
43
+ 'icon--svg',
40
44
  filled ? 'icon--filled' : '',
41
45
  weight == DEFAULT_WEIGHT ? '' : "icon--weight-#{weight}",
42
46
  emphasis == DEFAULT_EMPHASIS ? '' : "icon--#{emphasis}-emphasis"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rolemodel
4
- VERSION = '2.4.1'
4
+ VERSION = '2.6.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rolemodel-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.1
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - RoleModel Software Inc
@@ -65,6 +65,20 @@ dependencies:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0.10'
68
+ - !ruby/object:Gem::Dependency
69
+ name: parallel_tests
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
68
82
  - !ruby/object:Gem::Dependency
69
83
  name: rake
70
84
  requirement: !ruby/object:Gem::Requirement
@@ -379,6 +393,12 @@ files:
379
393
  - lib/generators/rolemodel/testing/vitest/templates/spec/javascript/test-setup.js
380
394
  - lib/generators/rolemodel/testing/vitest/templates/vitest.config.js
381
395
  - lib/generators/rolemodel/testing/vitest/vitest_generator.rb
396
+ - lib/generators/rolemodel/tom_select/README.md
397
+ - lib/generators/rolemodel/tom_select/USAGE
398
+ - lib/generators/rolemodel/tom_select/templates/app/inputs/grouped_tom_select_input.rb
399
+ - lib/generators/rolemodel/tom_select/templates/app/inputs/tom_select_input.rb
400
+ - lib/generators/rolemodel/tom_select/templates/app/javascript/controllers/tom_select_controller.js
401
+ - lib/generators/rolemodel/tom_select/tom_select_generator.rb
382
402
  - lib/generators/rolemodel/turbo/all/README.md
383
403
  - lib/generators/rolemodel/turbo/all/USAGE
384
404
  - lib/generators/rolemodel/turbo/all/all_generator.rb
@@ -390,8 +410,6 @@ files:
390
410
  - lib/generators/rolemodel/turbo/form/README.md
391
411
  - lib/generators/rolemodel/turbo/form/USAGE
392
412
  - lib/generators/rolemodel/turbo/form/form_generator.rb
393
- - lib/generators/rolemodel/turbo/form/templates/app/javascript/controllers/turbo_form_controller.js
394
- - lib/generators/rolemodel/turbo/form/templates/spec/support/helpers/turbo_form_helper.rb
395
413
  - lib/generators/rolemodel/turbo/modals/README.md
396
414
  - lib/generators/rolemodel/turbo/modals/USAGE
397
415
  - lib/generators/rolemodel/turbo/modals/modals_generator.rb
@@ -459,7 +477,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
459
477
  - !ruby/object:Gem::Version
460
478
  version: '0'
461
479
  requirements: []
462
- rubygems_version: 4.0.3
480
+ rubygems_version: 4.0.20
463
481
  specification_version: 4
464
482
  summary: The RoleModel Way of building Rails apps.
465
483
  test_files: []
@@ -1,15 +0,0 @@
1
- import { Controller } from '@hotwired/stimulus'
2
- import { patch } from '@rails/request.js'
3
-
4
- export default class extends Controller {
5
- static values = { url: String, count: Number }
6
-
7
- async perform({ params: { url: urlParam, query: queryParams } }) {
8
- const body = new FormData(this.element)
9
-
10
- if (queryParams) Object.keys(queryParams).forEach(key => body.append(key, queryParams[key]))
11
-
12
- const response = await patch(urlParam || this.urlValue, { body, responseKind: 'turbo-stream' })
13
- if (response.ok) this.countValue += 1
14
- }
15
- }
@@ -1,8 +0,0 @@
1
- module TurboFormHelper
2
- def expect_turbo_form_request
3
- count_value = find("[data-controller='turbo-form']")['data-turbo-form-count-value'] || 0
4
- yield
5
- expect(page).to have_selector("[data-turbo-form-count-value='#{count_value.to_i + 1}']"),
6
- 'Expected a successful Turbo-Form request, but none was detected'
7
- end
8
- end