Skip to content

Commit

Permalink
Impl style option
Browse files Browse the repository at this point in the history
  • Loading branch information
zhandao committed Mar 15, 2024
1 parent 4275a01 commit dc567e6
Show file tree
Hide file tree
Showing 14 changed files with 66 additions and 57 deletions.
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ gem exec nextgen create myapp

This will download the latest version of the `nextgen` gem and use it to create an app in the `myapp` directory. You'll be asked to configure the tech stack through several interactive prompts. If you have a `~/.railsrc` file, it will be ignored.

Options:
- `style`: control the **optional enhancements** you can choose in the generator.
- defaults to `default`, [enhancements list](config)
- presets:
- `full` (`--style=full`), [enhancements list](config/styles/full)
- your local configs: `--style=path/to/your/style_dir`

> [!TIP]
> If you get an "Unknown command exec" error, fix it by upgrading rubygems: `gem update --system`.
Expand All @@ -59,7 +66,7 @@ Check out the [examples directory](./examples) to see some Rails apps that were
On top of that foundation, Nextgen offers dozens of useful enhancements to the vanilla Rails experience. You are free to pick and choose which (if any) of these to apply to your new project. Behind the scenes, **each enhancement is applied in a separate git commit,** so that you can later see what was applied and why, and revert the suggestions if necessary.

> [!TIP]
> For the full list of what Nextgen provides, check out [config/generators.yml](https://github.com/mattbrictson/nextgen/tree/main/config/generators.yml). The source code of each generator can be found in [lib/nextgen/generators](https://github.com/mattbrictson/nextgen/tree/main/lib/nextgen/generators).
> For the full list of what Nextgen provides, check out [config/*.yml](https://github.com/mattbrictson/nextgen/tree/main/config). The source code of each generator can be found in [lib/nextgen/generators](https://github.com/mattbrictson/nextgen/tree/main/lib/nextgen/generators).
Here are some highlights of what Nextgen brings to the table:

Expand All @@ -71,16 +78,14 @@ Nextgen can optionally set up a GitHub Actions CI workflow for your app that aut

Prefer RSpec? Nextgen can set you up with RSpec, plus the gems and configuration you need for system specs (browser testing). Or stick with the Rails Minitest defaults. In either case, Nextgen will set up a good default Rake task and appropriate CI job.

### Gems

Nextgen can install and configure your choice of these recommended gems:
### Job Backends

#### Job Backends

- [sidekiq](https://github.com/sidekiq/sidekiq)
- [sidekiq](https://github.com/sidekiq/sidekiq) (`--style=full`)
- [solid_queue](https://github.com/basecamp/solid_queue)

#### Other
### Gems

Nextgen can install and configure your choice of these recommended gems:

- [annotate](https://github.com/ctran/annotate_models)
- [brakeman](https://github.com/presidentbeef/brakeman)
Expand Down
5 changes: 5 additions & 0 deletions config/job_backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

sidekiq:
prompt: "Sidekiq (Redis-backed)"
description: "Install sidekiq gem to use in production"
requires: active_job
Empty file added config/styles/full/checkers.yml
Empty file.
Empty file.
Empty file added config/styles/full/gems.yml
Empty file.
5 changes: 0 additions & 5 deletions config/job.yml → config/styles/full/job_backend.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@

sidekiq:
prompt: "Sidekiq (Redis-backed)"
description: "Install sidekiq gem to use in production"
requires: active_job

solid_queue:
prompt: "SolidQueue (Database-backed)"
description: "Install solid_queue as ActiveJob's backend"
Expand Down
Empty file.
24 changes: 24 additions & 0 deletions lib/nextgen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,28 @@ def self.generators_path(scope = "")
def self.template_path
Pathname.new(__dir__).join("../template")
end

def self.config_path(style: nil)
if style
if style.match?("/")
Pathname.new(style)
else
Pathname.new(__dir__).join("../config/styles", style)
end
else
Pathname.new(__dir__).join("../config")
end
end

def self.config_for(scope:, style: nil)
base = YAML.load_file("#{Nextgen.config_path}/#{scope}.yml")
if style
base.merge!(YAML.load_file("#{Nextgen.config_path(style: style)}/#{scope}.yml") || {})
end
base
end

def self.scopes_for(style: nil)
Dir[Nextgen.config_path(style: style) + "*.yml"].map { _1.match(/([_a-z]*)\.yml/)[1] }
end
end
1 change: 1 addition & 0 deletions lib/nextgen/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class CLI < Thor

map %w[-v --version] => "version"

option :style, type: :string, default: nil
desc "create APP_PATH", "Generate a Rails app interactively in APP_PATH"
def create(app_path)
Commands::Create.run(app_path, options)
Expand Down
51 changes: 16 additions & 35 deletions lib/nextgen/commands/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ def self.run(app_path, options)
new(app_path, options).run
end

def initialize(app_path, _options)
def initialize(app_path, options)
@app_path = File.expand_path(app_path)
@app_name = File.basename(@app_path).gsub(/\W/, "_").squeeze("_").camelize
@rails_opts = RailsOptions.new
@style = options[:style]
end

def run # rubocop:disable Metrics/MethodLength Metrics/PerceivedComplexity
def run # rubocop:disable Metrics/MethodLength, Metrics/PerceivedComplexity
say_banner
continue_if "Ready to start?"

Expand All @@ -39,12 +40,8 @@ def run # rubocop:disable Metrics/MethodLength Metrics/PerceivedComplexity
ask_system_testing if rails_opts.frontend? && rails_opts.test_framework?
say

if prompt.yes?("More detailed configuration? [ job, code snippets, gems ... ] ↵")
ask_job_backend if rails_opts.active_job?
ask_workflows
ask_checkers
ask_code_snippets
ask_optional_enhancements
if prompt.yes?("More enhancements? [ job, code snippets, gems ... ] ↵")
ask_styled_enhancements
end

say_summary
Expand Down Expand Up @@ -98,7 +95,7 @@ def ask_full_stack_or_api
"API only" => true
)
rails_opts.api! if api
@generators = {basic: Generators.compatible_with(rails_opts: rails_opts, scope: "basic")}
@generators = {basic: Generators.compatible_with(rails_opts: rails_opts, style: nil, scope: "basic")}
end

def ask_frontend_management
Expand Down Expand Up @@ -189,33 +186,17 @@ def ask_system_testing
rails_opts.skip_system_test! unless system_testing
end

def ask_job_backend
generators[:job] = Generators.compatible_with(rails_opts: rails_opts, scope: "job").tap do |it|
it.ask_select("Which #{underline("job backend")} would you like to use?", prompt: prompt)
end
end

def ask_workflows
generators[:workflows] = Generators.compatible_with(rails_opts: rails_opts, scope: "workflows").tap do |it|
it.ask_select("Which #{underline("workflows")} would you like to add?", multi: true, prompt: prompt)
end
end

def ask_checkers
generators[:checkers] = Generators.compatible_with(rails_opts: rails_opts, scope: "checkers").tap do |it|
it.ask_select("Which #{underline("checkers")} would you like to add?", multi: true, prompt: prompt)
end
end

def ask_code_snippets
generators[:code_snippets] = Generators.compatible_with(rails_opts: rails_opts, scope: "code_snippets").tap do |it|
it.ask_select("Which #{underline("code snippets")} would you like to add?", multi: true, prompt: prompt)
end
end
def ask_styled_enhancements
say " ↪ style: #{cyan(@style || "default")}"
Nextgen.scopes_for(style: @style).each do |scope|
gen = Generators.compatible_with(rails_opts: rails_opts, style: @style, scope: scope)
next if gen.empty? || scope == "basic"

def ask_optional_enhancements
generators[:gems] = Generators.compatible_with(rails_opts: rails_opts, scope: "gems").tap do |it|
it.ask_select("Which optional enhancements would you like to add?", multi: true, sort: true, prompt: prompt)
key_word = underline(scope.tr("_", " "))
multi = scope == scope.pluralize
sort = gen.optional.size > 10
gen.ask_select("Which #{key_word} would you like to add?", prompt: prompt, multi: multi, sort: sort)
generators[scope.to_sym] = gen
end
end
end
Expand Down
4 changes: 1 addition & 3 deletions lib/nextgen/commands/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ def capture_version(command)
end

def activated_generators
activated = generators[:gems].all_active_names
activated.prepend(generators[:job].all_active_names.first) unless generators[:job].nil?

activated = generators.values.flat_map(&:all_active_names)
activated.any? ? activated.sort_by(&:downcase) : ["<None>"]
end

Expand Down
12 changes: 6 additions & 6 deletions lib/nextgen/generators.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

module Nextgen
class Generators
def self.compatible_with(rails_opts:, scope:)
yaml_path = File.expand_path("../../config/#{scope}.yml", __dir__)
def self.compatible_with(rails_opts:, style:, scope:)
new(scope, api: rails_opts.api?).tap do |generators|
YAML.load_file(yaml_path).each do |name, options|
Nextgen.config_for(style: style, scope: scope).each do |name, options|
options ||= {}
requirements = Array(options["requires"])
next unless requirements.all? { |req| rails_opts.public_send(:"#{req}?") }
Expand All @@ -18,7 +17,6 @@ def self.compatible_with(rails_opts:, scope:)
questions: options["questions"]
)
end

generators.deactivate_node unless rails_opts.requires_node?
end
end
Expand All @@ -29,9 +27,11 @@ def initialize(scope, **vars)
@scope = scope
end

def empty? = @generators.empty?

def ask_select(question, multi: false, sort: false, prompt: TTY::Prompt.new)
opt = sort ? optional.sort_by { |label, _| label.downcase }.to_h : optional
args = [question, opt, {cycle: true, filter: true}]
opts = sort ? optional.sort_by { |label, _| label.downcase }.to_h : optional
args = [question, opts, {cycle: true, filter: true}]
answers = multi ? prompt.multi_select(*args) : [prompt.select(*args)]

answers.each do |answer|
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit dc567e6

Please sign in to comment.