rolemodel-rails 2.5.0 → 2.6.1
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 +4 -4
- data/README.md +14 -0
- data/lib/generators/rolemodel/README.md +12 -1
- data/lib/generators/rolemodel/all_generator.rb +2 -0
- data/lib/generators/rolemodel/core_setup/README.md +3 -1
- data/lib/generators/rolemodel/core_setup/core_setup_generator.rb +6 -1
- data/lib/generators/rolemodel/github/README.md +0 -4
- data/lib/generators/rolemodel/github/USAGE +0 -1
- data/lib/generators/rolemodel/github/github_generator.rb +0 -5
- data/lib/generators/rolemodel/heroku/templates/deploy-app/SKILL.md +10 -8
- data/lib/generators/rolemodel/linters/rubocop/templates/.rubocop/cops/form_error_response.rb +54 -0
- data/lib/generators/rolemodel/linters/rubocop/templates/.rubocop/cops/no_chrome_tag.rb +45 -0
- data/lib/generators/rolemodel/linters/rubocop/templates/.rubocop.yml +74 -0
- data/lib/generators/rolemodel/optics/README.md +3 -0
- data/lib/generators/rolemodel/optics/all_generator.rb +5 -1
- data/lib/generators/rolemodel/optics/icons/icon_library_options.rb +23 -0
- data/lib/generators/rolemodel/optics/icons/icons_generator.rb +1 -1
- data/lib/generators/rolemodel/readme/readme_generator.rb +1 -1
- data/lib/generators/rolemodel/sentry/templates/config/initializers/sentry.rb +0 -1
- data/lib/generators/rolemodel/simple_form/simple_form_generator.rb +1 -3
- data/lib/generators/rolemodel/testing/parallel_tests/templates/.rspec_parallel +3 -0
- data/lib/generators/rolemodel/testing/rspec/rspec_generator.rb +1 -0
- data/lib/generators/rolemodel/testing/rspec/templates/.rspec +1 -0
- data/lib/generators/rolemodel/tom_select/README.md +15 -0
- data/lib/generators/rolemodel/tom_select/USAGE +7 -0
- data/lib/generators/rolemodel/tom_select/templates/app/inputs/grouped_tom_select_input.rb +15 -0
- data/lib/generators/rolemodel/tom_select/templates/app/inputs/tom_select_input.rb +17 -0
- data/lib/generators/rolemodel/tom_select/templates/app/javascript/controllers/tom_select_controller.js +36 -0
- data/lib/generators/rolemodel/tom_select/tom_select_generator.rb +43 -0
- data/lib/generators/rolemodel/turbo/all/README.md +1 -1
- data/lib/generators/rolemodel/turbo/form/README.md +4 -3
- data/lib/generators/rolemodel/turbo/form/form_generator.rb +4 -19
- data/lib/generators/rolemodel/webpack/webpack_generator.rb +1 -0
- data/lib/rolemodel/generator_base.rb +8 -2
- data/lib/rolemodel/version.rb +1 -1
- data/lib/rolemodel/yarn.rb +5 -1
- metadata +27 -4
- data/lib/generators/rolemodel/github/templates/instructions +0 -1
- data/lib/generators/rolemodel/turbo/form/templates/app/javascript/controllers/turbo_form_controller.js +0 -15
- data/lib/generators/rolemodel/turbo/form/templates/spec/support/helpers/turbo_form_helper.rb +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 54ba8996a84dfe7f2f989442444f039ebd5868a9a3f4800f800223d035f6a9d2
|
|
4
|
+
data.tar.gz: f79e30570b359cd0dc120b514127d207348589e85eb2f06414b0938aa3cd08d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8b08a3d1b935ca08181f45fb948112194888325c6e60971c3c18b279b4cd8e9073dec7f01d8c26373bc9276e54b071c238fbece40ced0bb60da864f2c20b61c2
|
|
7
|
+
data.tar.gz: 96a5ea26590b5074da86fd3059ca6bb04e49bc3bd82fd4e2b56a3f18415dd80f2a807ef31ab396f6313034394d2e90e591eb75711a50f795744d00636ca2b298
|
data/README.md
CHANGED
|
@@ -107,6 +107,7 @@ bin/rails g
|
|
|
107
107
|
* [GoodJob](./lib/generators/rolemodel/good_job)
|
|
108
108
|
* [Editors](./lib/generators/rolemodel/editors)
|
|
109
109
|
* [Tailored Select](./lib/generators/rolemodel/tailored_select)
|
|
110
|
+
* [Tom Select](./lib/generators/rolemodel/tom_select)
|
|
110
111
|
* [Lograge](./lib/generators/rolemodel/lograge)
|
|
111
112
|
|
|
112
113
|
## Utilities
|
|
@@ -331,6 +332,19 @@ We use the embeded Rails apps (`example_rails_current` & `example_rails_legacy`)
|
|
|
331
332
|
|
|
332
333
|
## Testing
|
|
333
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
|
+
|
|
334
348
|
Generator specs should be added to the [spec](./spec) directory.
|
|
335
349
|
|
|
336
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'
|
|
@@ -17,10 +17,12 @@ app you can push straight to Heroku right after generation.
|
|
|
17
17
|
- [Webpack](../webpack) — Webpack v5 for JS and CSS
|
|
18
18
|
- [Sentry](../sentry) — error monitoring for Ruby and JavaScript
|
|
19
19
|
- [Slim](../slim) — Slim templates
|
|
20
|
-
- [Optics](../optics) — Optics design system
|
|
20
|
+
- [Optics](../optics) — Optics design system (pass an icon library flag such as
|
|
21
|
+
`--material` to choose it up front; otherwise the icons generator asks)
|
|
21
22
|
- [Testing](../testing) — RSpec, FactoryBot, parallel_tests, TestProf
|
|
22
23
|
(pass `--js-runner` to include jasmine-playwright-runner)
|
|
23
24
|
- [SimpleForm](../simple_form) — SimpleForm with our configuration
|
|
25
|
+
- [Tom Select](../tom_select) — Tom Select with a Stimulus controller and SimpleForm inputs
|
|
24
26
|
- [Linters](../linters) — Rubocop and ESLint
|
|
25
27
|
- [UI Components](../ui_components) — flash, the modal pattern, & Turbo 8 support
|
|
26
28
|
- [Editors](../editors) — EditorConfig and recommended VSCode extensions
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
require_relative '../optics/icons/icon_library_options'
|
|
2
|
+
|
|
1
3
|
module Rolemodel
|
|
2
4
|
class CoreSetupGenerator < ::Rolemodel::GeneratorBase
|
|
5
|
+
include Optics::IconLibraryOptions
|
|
6
|
+
|
|
3
7
|
class_option :skip_deployable, type: :boolean, default: false, desc: 'skip generators for app deployment (Heroku, Sentry, Lograge)'
|
|
4
8
|
|
|
5
9
|
def run_the_core_generators
|
|
6
10
|
generate 'rolemodel:slim'
|
|
7
11
|
generate 'rolemodel:webpack'
|
|
8
|
-
generate 'rolemodel:optics:all'
|
|
12
|
+
generate 'rolemodel:optics:all', *icon_library_flags
|
|
9
13
|
generate 'rolemodel:simple_form'
|
|
14
|
+
generate 'rolemodel:tom_select'
|
|
10
15
|
generate 'rolemodel:testing:all'
|
|
11
16
|
generate 'rolemodel:turbo:all'
|
|
12
17
|
generate 'rolemodel:ui_components:flash'
|
|
@@ -16,9 +16,5 @@ It doesn't need to be run first, but the parallel_tests generator must be run in
|
|
|
16
16
|
- Pull Request Template
|
|
17
17
|
- When you open a Pull Request in Github it will use the Markdown file as a [template](./templates/pull_request_template.md) for the content of the PR.
|
|
18
18
|
- Helpful for reminding collaborators to add specific details to the PR.
|
|
19
|
-
- Copilot Instructions
|
|
20
|
-
- Installed into `.github/instructions`, these are context-specific instructions for Copilot to help it give more accurate
|
|
21
|
-
and relevant results. These are a good starting point but they should be tweaked for your project's frameworks and
|
|
22
|
-
standards.
|
|
23
19
|
- Dependabot Configuration
|
|
24
20
|
- defines a set of rules that dependabot will use to keep your applications dependencies up-to-date on a weekly basis.
|
|
@@ -3,10 +3,6 @@
|
|
|
3
3
|
module Rolemodel
|
|
4
4
|
class GithubGenerator < GeneratorBase
|
|
5
5
|
GITHUB_ACTIONS_REPO = 'https://github.com/RoleModel/actions.git'
|
|
6
|
-
# Files which are both used by the gem source and copied to the target app without modification
|
|
7
|
-
# are placed in the `.github` folder at the top level of this repository. This folder is then
|
|
8
|
-
# symlinked to the `templates` folder relative to this generator so they can still be copied over.
|
|
9
|
-
# Any files which are significantly different or not used by the gem source are just in `templates`.
|
|
10
6
|
source_root File.expand_path('templates', __dir__)
|
|
11
7
|
|
|
12
8
|
class_option :playwright, type: :boolean, default: true,
|
|
@@ -22,7 +18,6 @@ module Rolemodel
|
|
|
22
18
|
end
|
|
23
19
|
|
|
24
20
|
def install_github_config
|
|
25
|
-
directory 'instructions', '.github/instructions'
|
|
26
21
|
directory 'workflows', '.github/workflows', force: true
|
|
27
22
|
template 'pull_request_template.md', '.github/pull_request_template.md'
|
|
28
23
|
end
|
|
@@ -24,12 +24,16 @@ go. Ask the user questions **one at a time**.
|
|
|
24
24
|
|
|
25
25
|
## Phase 0: Preflight
|
|
26
26
|
|
|
27
|
-
Run these checks before touching anything
|
|
28
|
-
fail:
|
|
29
|
-
|
|
30
|
-
1. `
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
Run these checks before touching anything (creating a missing GitHub repo is the one
|
|
28
|
+
exception). Stop and tell the user how to fix any that fail:
|
|
29
|
+
|
|
30
|
+
1. `gh auth status` — GitHub CLI authenticated.
|
|
31
|
+
2. `git remote get-url origin` — must be a GitHub repo. A brand-new app has no `origin` yet:
|
|
32
|
+
offer to create one, confirming the name (default: the app directory's name) and that it
|
|
33
|
+
should be private in the RoleModel org, then
|
|
34
|
+
`gh repo create RoleModel/<name> --private --source . --remote origin --push`.
|
|
35
|
+
Derive `REPO_NAME` from `gh repo view --json name -q .name` (fallback: basename of the
|
|
36
|
+
origin URL).
|
|
33
37
|
3. `heroku auth:whoami` — Heroku CLI installed and logged in.
|
|
34
38
|
4. Sentry MCP tools available (try listing organizations). If no Sentry MCP server is
|
|
35
39
|
connected, ask the user to add/authorize the Sentry MCP server
|
|
@@ -41,10 +45,8 @@ fail:
|
|
|
41
45
|
- reads `Rails.application.credentials...` → **credentials mode**
|
|
42
46
|
7. Deploy-readiness (recent `rolemodel-rails` bakes these in; older apps need the manual
|
|
43
47
|
fixes noted in each phase). Confirm the generated app already has:
|
|
44
|
-
- `terser-webpack-plugin` in `package.json` (webpack config imports it)
|
|
45
48
|
- custom cops under `.rubocop/cops/`, not `lib/cops/` (the latter crashes prod boot)
|
|
46
49
|
- `config/initializers/lograge.rb` uses `controller.try(:current_user)`
|
|
47
|
-
- `config/database.yml` `production:` block uses `url: <%= ENV["DATABASE_URL"] %>`
|
|
48
50
|
- a `ruby` directive in the `Gemfile` (so the version reaches `Gemfile.lock`)
|
|
49
51
|
- `stackprof` in the `Gemfile` (Sentry profiling)
|
|
50
52
|
If any are missing, apply the fix inline and note the app predates the generator update.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cops
|
|
4
|
+
# Custom Cop to Ensure Form Error Response Statuses
|
|
5
|
+
#
|
|
6
|
+
# There are three levels of matching here:
|
|
7
|
+
# Methods (def on_if)
|
|
8
|
+
# This scans code and only stops on the method specified.
|
|
9
|
+
# E.G. `on_if` will only move into the below checks when it encounters an if statement.
|
|
10
|
+
#
|
|
11
|
+
# Node matcher (def_node_matcher :form_error)
|
|
12
|
+
# This scans the block of code (in this case, the else branch of the if block)
|
|
13
|
+
# using AST (Abstract Tree Syntax) to match certain patterns
|
|
14
|
+
#
|
|
15
|
+
# Regex
|
|
16
|
+
# This checks the specific matching line to ensure it meets our expectations
|
|
17
|
+
class FormErrorResponse < RuboCop::Cop::Base
|
|
18
|
+
extend RuboCop::Cop::AutoCorrector
|
|
19
|
+
|
|
20
|
+
def self.rack_version
|
|
21
|
+
Gem.loaded_specs['rack'].version
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.http_status
|
|
25
|
+
if rack_version >= '3.1'
|
|
26
|
+
:unprocessable_content
|
|
27
|
+
else
|
|
28
|
+
:unprocessable_entity
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Match code in the else branch that either: is the only line and looks like `render :something, ...`
|
|
33
|
+
# or is not the first line and looks like the same.
|
|
34
|
+
def_node_matcher :form_error, <<~PATTERN
|
|
35
|
+
{(... $(send nil? :render (:sym _) ...)) | $(send nil? :render (:sym _) ...)}
|
|
36
|
+
PATTERN
|
|
37
|
+
|
|
38
|
+
MSG = "Use status: :#{self.http_status} for invalid form requests."
|
|
39
|
+
|
|
40
|
+
# Ensure the render is returning the correct status, otherwise add the offense
|
|
41
|
+
STATUS_PAIR = /\(pair\s+\(sym :status\)\s+\(sym :#{self.http_status}\)\)/m
|
|
42
|
+
|
|
43
|
+
# Only check within if blocks
|
|
44
|
+
def on_if(node)
|
|
45
|
+
form_error(node.else_branch) do |name|
|
|
46
|
+
next if name.to_s.match?(STATUS_PAIR)
|
|
47
|
+
|
|
48
|
+
add_offense(name) do |corrector|
|
|
49
|
+
corrector.insert_after(name, ", status: :#{self.class.http_status}")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rubocop'
|
|
4
|
+
|
|
5
|
+
module Cops
|
|
6
|
+
# Detect occurences of the chrome tag in specs, and warn and remove them.
|
|
7
|
+
class NoChromeTag < RuboCop::Cop::Base
|
|
8
|
+
include RuboCop::Cop::RangeHelp
|
|
9
|
+
extend RuboCop::Cop::AutoCorrector
|
|
10
|
+
|
|
11
|
+
MSG = 'The :chrome tag is only for testing, and should not be checked into the repository.'
|
|
12
|
+
RESTRICT_ON_SEND = %i[describe context it feature scenario].freeze
|
|
13
|
+
|
|
14
|
+
def on_send(node) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
15
|
+
node.arguments.each do |a|
|
|
16
|
+
if a.sym_type? && a.value == :chrome
|
|
17
|
+
range = range_with_surrounding_comma(a.source_range)
|
|
18
|
+
add_offense(range) do |corrector|
|
|
19
|
+
corrector.replace(range, '')
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
next unless a.hash_type?
|
|
24
|
+
|
|
25
|
+
a.pairs.each do |pair|
|
|
26
|
+
next unless pair.key.value == :chrome
|
|
27
|
+
|
|
28
|
+
range = range_with_surrounding_comma(pair.source_range)
|
|
29
|
+
add_offense(range) do |corrector|
|
|
30
|
+
corrector.replace(range, '')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def range_with_surrounding_comma(source_range)
|
|
37
|
+
buffer = source_range.source_buffer
|
|
38
|
+
src = buffer.source
|
|
39
|
+
end_pos = source_range.end_pos
|
|
40
|
+
comma_index = src[...end_pos].rindex(/\s*,\s*/)
|
|
41
|
+
|
|
42
|
+
Parser::Source::Range.new(buffer, comma_index, end_pos)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require:
|
|
2
|
+
- ./.rubocop/cops/form_error_response.rb
|
|
3
|
+
- ./.rubocop/cops/no_chrome_tag.rb
|
|
4
|
+
plugins:
|
|
5
|
+
- rubocop-rails
|
|
6
|
+
|
|
7
|
+
AllCops:
|
|
8
|
+
Exclude:
|
|
9
|
+
- bin/**/*
|
|
10
|
+
- db/**/*
|
|
11
|
+
- vendor/**/*
|
|
12
|
+
- config/**/*
|
|
13
|
+
- node_modules/**/*
|
|
14
|
+
- tmp/**/*
|
|
15
|
+
NewCops: enable
|
|
16
|
+
|
|
17
|
+
Metrics:
|
|
18
|
+
Severity: refactor
|
|
19
|
+
|
|
20
|
+
# RSpec tests and rake tasks are expected to have
|
|
21
|
+
# large blocks, so exclude them from this cop.
|
|
22
|
+
Metrics/BlockLength:
|
|
23
|
+
Exclude:
|
|
24
|
+
- 'Rakefile'
|
|
25
|
+
- '**/*.rake'
|
|
26
|
+
- 'spec/**/*.rb'
|
|
27
|
+
|
|
28
|
+
# Hard to make this pass always and want to keep flexibility
|
|
29
|
+
Layout/ArrayAlignment:
|
|
30
|
+
Enabled: false
|
|
31
|
+
|
|
32
|
+
# Best Practices for readability
|
|
33
|
+
Layout:
|
|
34
|
+
Severity: refactor
|
|
35
|
+
|
|
36
|
+
Layout/FirstArrayElementIndentation:
|
|
37
|
+
EnforcedStyle: consistent
|
|
38
|
+
|
|
39
|
+
Layout/FirstHashElementIndentation:
|
|
40
|
+
EnforcedStyle: consistent
|
|
41
|
+
|
|
42
|
+
Lint/UnusedMethodArgument:
|
|
43
|
+
Severity: refactor
|
|
44
|
+
AutoCorrect: false
|
|
45
|
+
|
|
46
|
+
Lint/UnusedBlockArgument:
|
|
47
|
+
Severity: refactor
|
|
48
|
+
AutoCorrect: false
|
|
49
|
+
|
|
50
|
+
Lint/UselessAssignment:
|
|
51
|
+
Severity: refactor
|
|
52
|
+
AutoCorrect: false
|
|
53
|
+
|
|
54
|
+
Lint/UnderscorePrefixedVariableName:
|
|
55
|
+
Enabled: false
|
|
56
|
+
|
|
57
|
+
Lint/Loop:
|
|
58
|
+
Enabled: false
|
|
59
|
+
|
|
60
|
+
Lint/SuppressedException:
|
|
61
|
+
Enabled: false
|
|
62
|
+
|
|
63
|
+
Style:
|
|
64
|
+
Severity: refactor
|
|
65
|
+
|
|
66
|
+
# It is ok to use inject at times
|
|
67
|
+
Style/EachWithObject:
|
|
68
|
+
Enabled: false
|
|
69
|
+
|
|
70
|
+
Style/Documentation:
|
|
71
|
+
Enabled: false
|
|
72
|
+
|
|
73
|
+
Cops/NoChromeTag:
|
|
74
|
+
AutoCorrect: contextual
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
require_relative 'icons/icon_library_options'
|
|
2
|
+
|
|
1
3
|
module Rolemodel
|
|
2
4
|
module Optics
|
|
3
5
|
class AllGenerator < Rolemodel::GeneratorBase
|
|
6
|
+
include IconLibraryOptions
|
|
7
|
+
|
|
4
8
|
source_root File.expand_path('templates', __dir__)
|
|
5
9
|
|
|
6
10
|
def run_all_the_generators
|
|
7
11
|
generate 'rolemodel:optics:base'
|
|
8
|
-
generate 'rolemodel:optics:icons'
|
|
12
|
+
generate 'rolemodel:optics:icons', *icon_library_flags
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'icons_generator'
|
|
4
|
+
|
|
5
|
+
module Rolemodel
|
|
6
|
+
module Optics
|
|
7
|
+
# For generators that run optics:icons: accept its icon library flag (e.g. --material) and pass
|
|
8
|
+
# it along, so the icons generator doesn't stop to ask.
|
|
9
|
+
module IconLibraryOptions
|
|
10
|
+
LIBRARIES = IconsGenerator::SUPPORTED_LIBRARIES.keys.freeze
|
|
11
|
+
|
|
12
|
+
def self.included(base)
|
|
13
|
+
base.class_exclusive do
|
|
14
|
+
LIBRARIES.each { base.class_option it, type: :boolean, lazy_default: false, desc: "Use #{it} icon library" }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def icon_library_flags = LIBRARIES.select { options[it] }.map { "--#{it}" }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -42,7 +42,7 @@ module Rolemodel
|
|
|
42
42
|
private
|
|
43
43
|
|
|
44
44
|
def capture_user_selection
|
|
45
|
-
options.slice(*SUPPORTED_LIBRARIES.keys).invert[true] ||
|
|
45
|
+
options.slice(*SUPPORTED_LIBRARIES.keys).invert[true] || ask_or_default(
|
|
46
46
|
'What icon library would you like to add?',
|
|
47
47
|
default: SUPPORTED_LIBRARIES.keys.first.to_s,
|
|
48
48
|
limited_to: SUPPORTED_LIBRARIES.keys.map(&:to_s)
|
|
@@ -5,7 +5,7 @@ module Rolemodel
|
|
|
5
5
|
def install_readme
|
|
6
6
|
@project_name = Rails.application.class.try(:parent_name) || Rails.application.class.module_parent_name
|
|
7
7
|
@project = @project_name.underscore
|
|
8
|
-
template 'README.md.erb', 'README.md'
|
|
8
|
+
template 'README.md.erb', 'README.md', force: true
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
|
@@ -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
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Form Generator
|
|
2
2
|
|
|
3
|
-
Enable
|
|
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
|
-
-
|
|
8
|
-
|
|
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
|
-
|
|
4
|
+
def install_turbo_form
|
|
5
|
+
say 'Installing turbo_form', :green
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
say 'Installing @rails/request.js package', :green
|
|
7
|
+
bundle_command 'add turbo_form'
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
@@ -10,6 +10,12 @@ module Rolemodel
|
|
|
10
10
|
include ::Rails::Generators::BundleHelper, ReplaceContentHelper
|
|
11
11
|
|
|
12
12
|
private
|
|
13
|
+
|
|
14
|
+
# Thor's ask loops forever on EOF when limited_to is set, so only prompt when someone can answer.
|
|
15
|
+
def ask_or_default(statement, default:, **)
|
|
16
|
+
$stdin.tty? ? ask(statement, default:, **) : default
|
|
17
|
+
end
|
|
18
|
+
|
|
13
19
|
# based on https://github.com/rails/rails/blob/main/railties/lib/rails/generators/app_base.rb#L713
|
|
14
20
|
def run_bundle
|
|
15
21
|
bundle_command("install --quiet", "BUNDLE_IGNORE_MESSAGES" => "1")
|
|
@@ -22,13 +28,13 @@ module Rolemodel
|
|
|
22
28
|
end
|
|
23
29
|
|
|
24
30
|
# Enable Corepack and pin the project to Yarn 4+ before running any `yarn`
|
|
25
|
-
# command.
|
|
31
|
+
# command. Args/opts are passed explicitly: otherwise Thor forwards this
|
|
26
32
|
# generator's own CLI args (e.g. the test harness's --skip-bundle
|
|
27
33
|
# --skip-bootsnap) into Rolemodel::Yarn#setup, which takes none and raises
|
|
28
34
|
# Thor::InvocationError.
|
|
29
35
|
def ensure_yarn
|
|
30
36
|
return if Rails.root.join(destination_root, '.yarnrc.yml').exist?
|
|
31
|
-
invoke 'rolemodel:yarn:setup', [],
|
|
37
|
+
invoke 'rolemodel:yarn:setup', [], options.slice(:pretend)
|
|
32
38
|
end
|
|
33
39
|
|
|
34
40
|
def application_stylesheet_path
|
data/lib/rolemodel/version.rb
CHANGED
data/lib/rolemodel/yarn.rb
CHANGED
|
@@ -16,7 +16,8 @@ module Rolemodel
|
|
|
16
16
|
|
|
17
17
|
yes! 'yarn set version stable'
|
|
18
18
|
yes! 'corepack enable'
|
|
19
|
-
|
|
19
|
+
# `yarn init` replaces an existing package.json, dropping the scripts and packages Rails added.
|
|
20
|
+
yes! 'yarn init' unless File.exist?(File.expand_path('package.json', destination_root))
|
|
20
21
|
|
|
21
22
|
ignore_yarn_install_state
|
|
22
23
|
end
|
|
@@ -32,6 +33,9 @@ module Rolemodel
|
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
def yes!(command)
|
|
36
|
+
say_status :run, command
|
|
37
|
+
return if options[:pretend]
|
|
38
|
+
|
|
35
39
|
Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
|
|
36
40
|
stdin.puts 'y'
|
|
37
41
|
stdin.close
|
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
|
+
version: 2.6.1
|
|
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
|
|
@@ -189,7 +203,6 @@ files:
|
|
|
189
203
|
- lib/generators/rolemodel/github/github_generator.rb
|
|
190
204
|
- lib/generators/rolemodel/github/templates/CODEOWNERS
|
|
191
205
|
- lib/generators/rolemodel/github/templates/dependabot.yml
|
|
192
|
-
- lib/generators/rolemodel/github/templates/instructions
|
|
193
206
|
- lib/generators/rolemodel/github/templates/pull_request_template.md
|
|
194
207
|
- lib/generators/rolemodel/github/templates/workflows/ci.yml.tt
|
|
195
208
|
- lib/generators/rolemodel/github/templates/workflows/deploy-production.yml
|
|
@@ -225,6 +238,9 @@ files:
|
|
|
225
238
|
- lib/generators/rolemodel/linters/rubocop/README.md
|
|
226
239
|
- lib/generators/rolemodel/linters/rubocop/USAGE
|
|
227
240
|
- lib/generators/rolemodel/linters/rubocop/rubocop_generator.rb
|
|
241
|
+
- lib/generators/rolemodel/linters/rubocop/templates/.rubocop.yml
|
|
242
|
+
- lib/generators/rolemodel/linters/rubocop/templates/.rubocop/cops/form_error_response.rb
|
|
243
|
+
- lib/generators/rolemodel/linters/rubocop/templates/.rubocop/cops/no_chrome_tag.rb
|
|
228
244
|
- lib/generators/rolemodel/lograge/README.md
|
|
229
245
|
- lib/generators/rolemodel/lograge/USAGE
|
|
230
246
|
- lib/generators/rolemodel/lograge/lograge_generator.rb
|
|
@@ -248,6 +264,7 @@ files:
|
|
|
248
264
|
- lib/generators/rolemodel/optics/base/base_generator.rb
|
|
249
265
|
- lib/generators/rolemodel/optics/icons/README.md
|
|
250
266
|
- lib/generators/rolemodel/optics/icons/USAGE
|
|
267
|
+
- lib/generators/rolemodel/optics/icons/icon_library_options.rb
|
|
251
268
|
- lib/generators/rolemodel/optics/icons/icons_generator.rb
|
|
252
269
|
- lib/generators/rolemodel/optics/icons/templates/app/helpers/icon_helper.rb.tt
|
|
253
270
|
- lib/generators/rolemodel/react/README.md
|
|
@@ -357,9 +374,11 @@ files:
|
|
|
357
374
|
- lib/generators/rolemodel/testing/parallel_tests/README.md
|
|
358
375
|
- lib/generators/rolemodel/testing/parallel_tests/USAGE
|
|
359
376
|
- lib/generators/rolemodel/testing/parallel_tests/parallel_tests_generator.rb
|
|
377
|
+
- lib/generators/rolemodel/testing/parallel_tests/templates/.rspec_parallel
|
|
360
378
|
- lib/generators/rolemodel/testing/rspec/README.md
|
|
361
379
|
- lib/generators/rolemodel/testing/rspec/USAGE
|
|
362
380
|
- lib/generators/rolemodel/testing/rspec/rspec_generator.rb
|
|
381
|
+
- lib/generators/rolemodel/testing/rspec/templates/.rspec
|
|
363
382
|
- lib/generators/rolemodel/testing/rspec/templates/spec/rails_helper.rb.tt
|
|
364
383
|
- lib/generators/rolemodel/testing/rspec/templates/spec/spec_helper.rb.tt
|
|
365
384
|
- lib/generators/rolemodel/testing/rspec/templates/spec/support/capybara_config.rb.tt
|
|
@@ -379,6 +398,12 @@ files:
|
|
|
379
398
|
- lib/generators/rolemodel/testing/vitest/templates/spec/javascript/test-setup.js
|
|
380
399
|
- lib/generators/rolemodel/testing/vitest/templates/vitest.config.js
|
|
381
400
|
- lib/generators/rolemodel/testing/vitest/vitest_generator.rb
|
|
401
|
+
- lib/generators/rolemodel/tom_select/README.md
|
|
402
|
+
- lib/generators/rolemodel/tom_select/USAGE
|
|
403
|
+
- lib/generators/rolemodel/tom_select/templates/app/inputs/grouped_tom_select_input.rb
|
|
404
|
+
- lib/generators/rolemodel/tom_select/templates/app/inputs/tom_select_input.rb
|
|
405
|
+
- lib/generators/rolemodel/tom_select/templates/app/javascript/controllers/tom_select_controller.js
|
|
406
|
+
- lib/generators/rolemodel/tom_select/tom_select_generator.rb
|
|
382
407
|
- lib/generators/rolemodel/turbo/all/README.md
|
|
383
408
|
- lib/generators/rolemodel/turbo/all/USAGE
|
|
384
409
|
- lib/generators/rolemodel/turbo/all/all_generator.rb
|
|
@@ -390,8 +415,6 @@ files:
|
|
|
390
415
|
- lib/generators/rolemodel/turbo/form/README.md
|
|
391
416
|
- lib/generators/rolemodel/turbo/form/USAGE
|
|
392
417
|
- 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
418
|
- lib/generators/rolemodel/turbo/modals/README.md
|
|
396
419
|
- lib/generators/rolemodel/turbo/modals/USAGE
|
|
397
420
|
- lib/generators/rolemodel/turbo/modals/modals_generator.rb
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
../../../../../.github/instructions
|
|
@@ -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
|
-
}
|
data/lib/generators/rolemodel/turbo/form/templates/spec/support/helpers/turbo_form_helper.rb
DELETED
|
@@ -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
|