railstart 0.1.0 → 0.2.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/CHANGELOG.md +41 -0
- data/README.md +93 -8
- data/config/presets/api-only.yaml +46 -0
- data/config/presets/default.yaml +45 -0
- data/config/rails8_defaults.yaml +1 -1
- data/lib/railstart/cli.rb +287 -3
- data/lib/railstart/config.rb +8 -2
- data/lib/railstart/generator.rb +76 -30
- data/lib/railstart/ui.rb +143 -0
- data/lib/railstart/version.rb +1 -1
- metadata +19 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 291b9eb1edd2bd473da3d2da1ca08921b4320e7892926dfb6f4099e6c3c9b51a
|
|
4
|
+
data.tar.gz: bd917d5cc282fb789d57fe88eaa484cd86a83c079801f0ca32f701be70c9986a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 001e22b7e5f50d84058280cee64a759da54fb60a77f2e895c6ed9d01f5b53a767dbe7e1ce3d67e6ac888cec3cb3daa66c237f1118e0e0680fa7603e02d0f02ca
|
|
7
|
+
data.tar.gz: 5fe74710ececff1600e82ceceff405f9bd5a5d068b3ea26ebb47558e98f530bd72c03f74b0872e63d9f5c0b25268aca731753b425571594c65fc4cba83a2361b
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,47 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
## [0.2.1] - 2025-11-22
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- Thor::UndefinedCommandError raised with incorrect number of arguments (now passes command, nil, and all_commands.keys)
|
|
13
|
+
|
|
14
|
+
## [0.2.0] - 2025-11-22
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Three-layer preset system (builtin → user → preset config merging)
|
|
18
|
+
- CLI flags: `--preset NAME` and `--default` for preset selection
|
|
19
|
+
- Preset resolution: user presets (`~/.config/railstart/presets/`) override built-in gem presets
|
|
20
|
+
- Built-in presets: `default.yaml` (PostgreSQL + Tailwind + Importmap) and `api-only.yaml`
|
|
21
|
+
- Config overlay schema with id-based merging for questions and post_actions
|
|
22
|
+
- New `Railstart::UI` module for enhanced CLI presentation
|
|
23
|
+
- ASCII art logo displayed at startup
|
|
24
|
+
- Styled welcome box with dynamic Rails version detection
|
|
25
|
+
- Boxed configuration summary with syntax highlighting
|
|
26
|
+
- Icon-based status messages (success ✓, info ℹ, warning ⚠, error ✗)
|
|
27
|
+
- Section headers with visual separators
|
|
28
|
+
- `tty-box` dependency for frame rendering
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- `Config.load` now accepts optional `preset_path` parameter
|
|
32
|
+
- Generator modes respect preset overlays for both interactive and non-interactive flows
|
|
33
|
+
- Generator always confirms before generation, even in `--default` mode
|
|
34
|
+
- Improved TTY::Prompt integration to use hash format for select/multi_select choices
|
|
35
|
+
- Welcome message now displays detected Rails version instead of hardcoded "Rails 8"
|
|
36
|
+
- Summary display redesigned with bordered box and colored output
|
|
37
|
+
- Status messages now use consistent icons and colors throughout
|
|
38
|
+
- Generator runs Rails commands outside bundler context using `Bundler.with_unbundled_env`
|
|
39
|
+
- Bundle install post-action now disabled by default
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
- Proper deep merging with id-based array merging for questions and post_actions
|
|
43
|
+
- TTY::Prompt select/multi_select displaying duplicate options (switched from array pairs to hash format)
|
|
44
|
+
- Default value selection not working correctly (now uses 1-based index as expected by TTY::Prompt)
|
|
45
|
+
- Bundle install post-action incorrectly prompting when user explicitly skips bundle install
|
|
46
|
+
|
|
47
|
+
### Removed
|
|
48
|
+
- Plain text summary formatting replaced with styled box display
|
|
49
|
+
|
|
9
50
|
## [0.1.0] - 2025-11-21
|
|
10
51
|
|
|
11
52
|
### Added
|
data/README.md
CHANGED
|
@@ -16,9 +16,13 @@ gem install railstart
|
|
|
16
16
|
|
|
17
17
|
## Usage
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### Quick Start
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
# Generate config files (optional, for customization)
|
|
23
|
+
railstart init
|
|
24
|
+
|
|
25
|
+
# Generate a new Rails app
|
|
22
26
|
railstart new my_app
|
|
23
27
|
|
|
24
28
|
# Or run without arguments for help
|
|
@@ -79,25 +83,88 @@ Creating Rails app...
|
|
|
79
83
|
✨ Rails app created successfully at ./my_app
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
###
|
|
86
|
+
### Use Presets
|
|
87
|
+
|
|
88
|
+
Presets are configuration overlays that let you define different defaults and even different questions/post-actions for specific use cases.
|
|
89
|
+
|
|
90
|
+
**Important:** When you use `--default` without `--preset`, Railstart automatically applies the `default` preset (from `~/.config/railstart/presets/default.yaml` or the gem's built-in version). This means defaults may differ from the base `rails8_defaults.yaml` config.
|
|
83
91
|
|
|
84
|
-
|
|
92
|
+
**Modes:**
|
|
93
|
+
- **Interactive** (default): prompts for each question from the config schema
|
|
94
|
+
- **With --default**: skips questions, loads "default" preset, shows summary and confirms
|
|
95
|
+
- **With --preset**: loads specified preset as config overlay (can be interactive or with --default)
|
|
85
96
|
|
|
86
97
|
```bash
|
|
98
|
+
# Interactive mode (builtin defaults)
|
|
99
|
+
railstart new my_app
|
|
100
|
+
|
|
101
|
+
# Non-interactive with "default" preset (asks no questions, shows summary + confirms)
|
|
102
|
+
# Note: --default automatically loads the "default" preset (user or gem)
|
|
87
103
|
railstart new my_app --default
|
|
104
|
+
|
|
105
|
+
# Interactive with custom preset
|
|
106
|
+
railstart new my_app --preset api-only
|
|
107
|
+
|
|
108
|
+
# Non-interactive with custom preset
|
|
109
|
+
railstart new my_app --preset api-only --default
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Create custom presets** at `~/.config/railstart/presets/my-preset.yaml`:
|
|
113
|
+
|
|
114
|
+
Presets use the same YAML schema as config files - they can override question defaults, change choices, add new questions, or modify post-actions:
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
# ~/.config/railstart/presets/api-only.yaml
|
|
118
|
+
# Presets merge on top of user config (and built-in config)
|
|
119
|
+
questions:
|
|
120
|
+
- id: database
|
|
121
|
+
choices:
|
|
122
|
+
- name: PostgreSQL
|
|
123
|
+
value: postgresql
|
|
124
|
+
default: true # Different default for this preset
|
|
125
|
+
|
|
126
|
+
- id: api_only
|
|
127
|
+
default: true # Override default to true for API preset
|
|
128
|
+
|
|
129
|
+
post_actions:
|
|
130
|
+
- id: init_git
|
|
131
|
+
enabled: false # Disable git init for this preset
|
|
88
132
|
```
|
|
89
133
|
|
|
90
|
-
|
|
134
|
+
Then use it:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Interactive with api-only config
|
|
138
|
+
railstart new my_app --preset api-only
|
|
139
|
+
|
|
140
|
+
# Non-interactive with api-only config
|
|
141
|
+
railstart new my_app --preset api-only --default
|
|
142
|
+
```
|
|
91
143
|
|
|
92
144
|
## Configuration
|
|
93
145
|
|
|
146
|
+
### Initialize Configuration Files
|
|
147
|
+
|
|
148
|
+
The easiest way to get started with custom configuration is to generate example files:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
railstart init
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
This creates:
|
|
155
|
+
- `~/.config/railstart/config.yaml` - Example user config with common customizations
|
|
156
|
+
- `~/.config/railstart/presets/` - Directory for your presets
|
|
157
|
+
- `~/.config/railstart/presets/example.yaml` - Example preset to get started
|
|
158
|
+
|
|
159
|
+
You can then edit these files to match your preferences.
|
|
160
|
+
|
|
94
161
|
### Built-in Defaults
|
|
95
162
|
|
|
96
163
|
Railstart ships with sensible Rails 8 defaults defined in `config/rails8_defaults.yaml`. These drive the interactive questions and their defaults.
|
|
97
164
|
|
|
98
165
|
### Customize for Your Team
|
|
99
166
|
|
|
100
|
-
|
|
167
|
+
You can create `~/.config/railstart/config.yaml` manually or use `railstart init` to generate an example file:
|
|
101
168
|
|
|
102
169
|
```yaml
|
|
103
170
|
questions:
|
|
@@ -204,7 +271,7 @@ bin/console
|
|
|
204
271
|
|
|
205
272
|
# Install locally to test as a real gem
|
|
206
273
|
gem build railstart.gemspec
|
|
207
|
-
gem install railstart-
|
|
274
|
+
gem install railstart-[version].gem
|
|
208
275
|
railstart new my_app
|
|
209
276
|
```
|
|
210
277
|
|
|
@@ -226,10 +293,28 @@ bundle exec rake test && bundle exec rubocop
|
|
|
226
293
|
|
|
227
294
|
## Architecture
|
|
228
295
|
|
|
229
|
-
-
|
|
296
|
+
### Three-Layer Configuration System
|
|
297
|
+
|
|
298
|
+
Railstart merges configuration from three sources (in order):
|
|
299
|
+
|
|
300
|
+
1. **Built-in config**: `config/rails8_defaults.yaml` (shipped with gem)
|
|
301
|
+
2. **User config**: `~/.config/railstart/config.yaml` (optional global overrides)
|
|
302
|
+
3. **Preset** (optional): `~/.config/railstart/presets/NAME.yaml` (per-run overlay)
|
|
303
|
+
|
|
304
|
+
Each layer can:
|
|
305
|
+
- Override question defaults
|
|
306
|
+
- Replace choice lists entirely (by question ID)
|
|
307
|
+
- Add new questions
|
|
308
|
+
- Add/modify post-actions
|
|
309
|
+
- Enable/disable post-actions
|
|
310
|
+
|
|
311
|
+
Merging is by `id` for both `questions` and `post_actions`, allowing surgical overrides without duplicating entire configs.
|
|
312
|
+
|
|
313
|
+
### Core Components
|
|
314
|
+
|
|
230
315
|
- **Generator** (`lib/railstart/generator.rb`) - Orchestrates interactive flow
|
|
231
316
|
- **Command Builder** (`lib/railstart/command_builder.rb`) - Translates answers to `rails new` flags
|
|
232
|
-
- **CLI** (`lib/railstart/cli.rb`) - Thor command interface
|
|
317
|
+
- **CLI** (`lib/railstart/cli.rb`) - Thor command interface with `--preset` option
|
|
233
318
|
|
|
234
319
|
## Contributing
|
|
235
320
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
# API-Only Preset - Minimal Rails app for JSON APIs
|
|
3
|
+
|
|
4
|
+
questions:
|
|
5
|
+
- id: database
|
|
6
|
+
choices:
|
|
7
|
+
- name: PostgreSQL
|
|
8
|
+
value: postgresql
|
|
9
|
+
default: true
|
|
10
|
+
|
|
11
|
+
- id: css
|
|
12
|
+
choices:
|
|
13
|
+
- name: None
|
|
14
|
+
value: none
|
|
15
|
+
default: true
|
|
16
|
+
|
|
17
|
+
- id: javascript
|
|
18
|
+
choices:
|
|
19
|
+
- name: Importmap (default)
|
|
20
|
+
value: importmap
|
|
21
|
+
default: true
|
|
22
|
+
|
|
23
|
+
- id: skip_features
|
|
24
|
+
default:
|
|
25
|
+
- action_mailer
|
|
26
|
+
- action_text
|
|
27
|
+
- hotwire
|
|
28
|
+
|
|
29
|
+
- id: api_only
|
|
30
|
+
default: true
|
|
31
|
+
|
|
32
|
+
- id: skip_git
|
|
33
|
+
default: false
|
|
34
|
+
|
|
35
|
+
- id: skip_docker
|
|
36
|
+
default: false
|
|
37
|
+
|
|
38
|
+
- id: skip_bundle
|
|
39
|
+
default: false
|
|
40
|
+
|
|
41
|
+
post_actions:
|
|
42
|
+
- id: init_git
|
|
43
|
+
enabled: true
|
|
44
|
+
|
|
45
|
+
- id: bundle_install
|
|
46
|
+
enabled: true
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Default Preset - Sensible defaults for quick start
|
|
3
|
+
# This preset is automatically loaded when using --default flag without --preset
|
|
4
|
+
# Can be overridden by user preset at ~/.config/railstart/presets/default.yaml
|
|
5
|
+
|
|
6
|
+
questions:
|
|
7
|
+
- id: database
|
|
8
|
+
choices:
|
|
9
|
+
- name: PostgreSQL
|
|
10
|
+
value: postgresql
|
|
11
|
+
default: true
|
|
12
|
+
|
|
13
|
+
- id: css
|
|
14
|
+
choices:
|
|
15
|
+
- name: Tailwind
|
|
16
|
+
value: tailwind
|
|
17
|
+
default: true
|
|
18
|
+
|
|
19
|
+
- id: javascript
|
|
20
|
+
choices:
|
|
21
|
+
- name: Importmap (default)
|
|
22
|
+
value: importmap
|
|
23
|
+
default: true
|
|
24
|
+
|
|
25
|
+
- id: skip_features
|
|
26
|
+
default: []
|
|
27
|
+
|
|
28
|
+
- id: api_only
|
|
29
|
+
default: false
|
|
30
|
+
|
|
31
|
+
- id: skip_git
|
|
32
|
+
default: false
|
|
33
|
+
|
|
34
|
+
- id: skip_docker
|
|
35
|
+
default: false
|
|
36
|
+
|
|
37
|
+
- id: skip_bundle
|
|
38
|
+
default: false
|
|
39
|
+
|
|
40
|
+
post_actions:
|
|
41
|
+
- id: init_git
|
|
42
|
+
enabled: true
|
|
43
|
+
|
|
44
|
+
- id: bundle_install
|
|
45
|
+
enabled: true
|
data/config/rails8_defaults.yaml
CHANGED
data/lib/railstart/cli.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "thor"
|
|
4
|
+
require "fileutils"
|
|
4
5
|
require_relative "generator"
|
|
5
6
|
|
|
6
7
|
module Railstart
|
|
@@ -11,22 +12,206 @@ module Railstart
|
|
|
11
12
|
# @example Print version
|
|
12
13
|
# Railstart::CLI.start(%w[version])
|
|
13
14
|
class CLI < Thor
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
def self.exit_on_failure?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Show help by default when no command is given
|
|
20
|
+
def self.start(given_args = ARGV, config = {})
|
|
21
|
+
if given_args.empty?
|
|
22
|
+
# Show command list instead of requiring a command argument
|
|
23
|
+
puts "Railstart - Interactive Rails 8 application generator"
|
|
24
|
+
puts ""
|
|
25
|
+
puts "Usage:"
|
|
26
|
+
puts " railstart init # Generate config files"
|
|
27
|
+
puts " railstart new [APP_NAME] [OPTIONS] # Generate a new Rails app"
|
|
28
|
+
puts " railstart version # Show version"
|
|
29
|
+
puts " railstart help [COMMAND] # Show help for a command"
|
|
30
|
+
puts ""
|
|
31
|
+
puts "Quick Start:"
|
|
32
|
+
puts " railstart init # Create config files (optional)"
|
|
33
|
+
puts " railstart new my_app # Interactive mode"
|
|
34
|
+
puts " railstart new my_app --default # Use defaults"
|
|
35
|
+
puts " railstart new my_app --preset api-only # Use preset"
|
|
36
|
+
puts ""
|
|
37
|
+
puts "Run 'railstart help init' or 'railstart help new' for details"
|
|
38
|
+
return
|
|
39
|
+
end
|
|
40
|
+
super
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Custom banner to show available options
|
|
44
|
+
class << self
|
|
45
|
+
# rubocop:disable Style/OptionalBooleanParameter
|
|
46
|
+
def banner(command, _namespace = nil, _subcommand = false)
|
|
47
|
+
"#{basename} #{command.usage}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Override to show only positive form of boolean options
|
|
51
|
+
def help(shell, subcommand = false)
|
|
52
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
|
53
|
+
list = printable_commands(true, subcommand)
|
|
54
|
+
Thor::Util.thor_classes_in(self).each do |klass|
|
|
55
|
+
list += klass.printable_commands(false)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
shell.say "Commands:"
|
|
59
|
+
shell.print_table(list, indent: 2, truncate: true)
|
|
60
|
+
shell.say
|
|
61
|
+
class_options_help(shell)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Override to customize option display
|
|
66
|
+
# rubocop:disable Style/OptionalBooleanParameter
|
|
67
|
+
def help(command = nil, subcommand = false)
|
|
68
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
|
69
|
+
if command
|
|
70
|
+
if self.class.subcommands.include?(command)
|
|
71
|
+
self.class.subcommand_classes[command].help(shell, subcommand)
|
|
72
|
+
else
|
|
73
|
+
cmd = self.class.all_commands[command]
|
|
74
|
+
raise Thor::UndefinedCommandError.new(command, nil, self.class.all_commands.keys) unless cmd
|
|
75
|
+
|
|
76
|
+
shell.say "Usage:"
|
|
77
|
+
shell.say " #{self.class.banner(cmd)}"
|
|
78
|
+
shell.say
|
|
79
|
+
if cmd.long_description
|
|
80
|
+
shell.say "Description:"
|
|
81
|
+
shell.print_wrapped(cmd.long_description, indent: 2)
|
|
82
|
+
else
|
|
83
|
+
shell.say cmd.description
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
print_custom_options(cmd)
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
super
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
no_commands do
|
|
94
|
+
def print_custom_options(cmd)
|
|
95
|
+
return unless cmd.options.any?
|
|
96
|
+
|
|
97
|
+
shell.say
|
|
98
|
+
shell.say "Options:"
|
|
99
|
+
cmd.options.each do |name, option|
|
|
100
|
+
print_option(name, option)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def print_option(name, option)
|
|
105
|
+
# For boolean options, only show the positive form
|
|
106
|
+
if option.type == :boolean
|
|
107
|
+
shell.say " [--#{name}]#{" " * [20 - name.length, 0].max}# #{option.description}"
|
|
108
|
+
else
|
|
109
|
+
print_non_boolean_option(name, option)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def print_non_boolean_option(name, option)
|
|
114
|
+
banner_text = option.banner || name.to_s.upcase
|
|
115
|
+
padding = [20 - (name.length + banner_text.length + 3), 0].max
|
|
116
|
+
shell.say " [--#{name}=#{banner_text}]#{" " * padding}# #{option.description}"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
desc "new [APP_NAME]", "Generate a new Rails 8 application"
|
|
120
|
+
long_desc <<~DESC
|
|
121
|
+
Generate a new Rails 8 application with an interactive wizard.
|
|
122
|
+
|
|
123
|
+
Modes:
|
|
124
|
+
- Interactive (default): prompts for each question
|
|
125
|
+
- With preset: uses preset config (different questions/defaults), interactive or non-interactive
|
|
126
|
+
|
|
127
|
+
Examples:
|
|
128
|
+
railstart new my_app # Interactive mode
|
|
129
|
+
railstart new my_app --default # Non-interactive with default preset (if exists)
|
|
130
|
+
railstart new my_app --preset api-only # Interactive with api-only preset config
|
|
131
|
+
railstart new my_app --preset api-only --default # Non-interactive with api-only preset
|
|
132
|
+
|
|
133
|
+
Presets are stored in: ~/.config/railstart/presets/*.yaml
|
|
134
|
+
DESC
|
|
135
|
+
option :default, type: :boolean, default: false, desc: "Use defaults non-interactively"
|
|
136
|
+
option :preset, type: :string, desc: "Preset name from ~/.config/railstart/presets/", banner: "NAME"
|
|
16
137
|
#
|
|
17
138
|
# @param app_name [String, nil] desired Rails app name, prompted if omitted
|
|
18
139
|
# @return [void]
|
|
19
140
|
# @raise [Railstart::Error] when generation fails due to configuration or runtime errors
|
|
20
141
|
# @example Start wizard with prompts
|
|
21
142
|
# Railstart::CLI.start(%w[new my_app])
|
|
143
|
+
# @example Use preset
|
|
144
|
+
# Railstart::CLI.start(%w[new my_app --preset api-only])
|
|
145
|
+
# @example Use default preset non-interactively
|
|
146
|
+
# Railstart::CLI.start(%w[new my_app --default])
|
|
22
147
|
def new(app_name = nil)
|
|
23
|
-
|
|
148
|
+
preset_name = determine_preset_name
|
|
149
|
+
preset_path = preset_name ? preset_file_for(preset_name) : nil
|
|
150
|
+
|
|
151
|
+
config = Config.load(preset_path: preset_path)
|
|
152
|
+
|
|
153
|
+
generator = Generator.new(
|
|
154
|
+
app_name,
|
|
155
|
+
config: config,
|
|
156
|
+
use_defaults: options[:default]
|
|
157
|
+
)
|
|
158
|
+
|
|
24
159
|
generator.run
|
|
25
160
|
rescue Railstart::Error => e
|
|
26
161
|
puts "Error: #{e.message}"
|
|
27
162
|
exit 1
|
|
28
163
|
end
|
|
29
164
|
|
|
165
|
+
desc "init", "Generate config directory and starter files"
|
|
166
|
+
long_desc <<~DESC
|
|
167
|
+
Creates ~/.config/railstart directory structure with example configuration files.
|
|
168
|
+
|
|
169
|
+
This generates:
|
|
170
|
+
- ~/.config/railstart/config.yaml (example user config)
|
|
171
|
+
- ~/.config/railstart/presets/ directory
|
|
172
|
+
- ~/.config/railstart/presets/example.yaml (example preset)
|
|
173
|
+
|
|
174
|
+
You can then customize these files for your preferences.
|
|
175
|
+
DESC
|
|
176
|
+
option :force, type: :boolean, default: false, desc: "Overwrite existing files"
|
|
177
|
+
#
|
|
178
|
+
# @return [void]
|
|
179
|
+
# @example Generate config files
|
|
180
|
+
# Railstart::CLI.start(%w[init])
|
|
181
|
+
def init
|
|
182
|
+
config_dir = File.expand_path("~/.config/railstart")
|
|
183
|
+
presets_dir = File.join(config_dir, "presets")
|
|
184
|
+
|
|
185
|
+
# Create directories
|
|
186
|
+
FileUtils.mkdir_p(presets_dir)
|
|
187
|
+
puts "✓ Created #{config_dir}"
|
|
188
|
+
puts "✓ Created #{presets_dir}"
|
|
189
|
+
|
|
190
|
+
# Generate example user config
|
|
191
|
+
user_config_path = File.join(config_dir, "config.yaml")
|
|
192
|
+
if File.exist?(user_config_path) && !options[:force]
|
|
193
|
+
puts "⊗ Skipped #{user_config_path} (already exists, use --force to overwrite)"
|
|
194
|
+
else
|
|
195
|
+
File.write(user_config_path, example_user_config)
|
|
196
|
+
puts "✓ Created #{user_config_path}"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Generate example preset
|
|
200
|
+
example_preset_path = File.join(presets_dir, "example.yaml")
|
|
201
|
+
if File.exist?(example_preset_path) && !options[:force]
|
|
202
|
+
puts "⊗ Skipped #{example_preset_path} (already exists, use --force to overwrite)"
|
|
203
|
+
else
|
|
204
|
+
File.write(example_preset_path, example_preset_config)
|
|
205
|
+
puts "✓ Created #{example_preset_path}"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
puts "\n✨ Configuration files initialized!"
|
|
209
|
+
puts "\nNext steps:"
|
|
210
|
+
puts " 1. Edit ~/.config/railstart/config.yaml to customize defaults"
|
|
211
|
+
puts " 2. Create custom presets in ~/.config/railstart/presets/"
|
|
212
|
+
puts " 3. Use with: railstart new my_app --preset example"
|
|
213
|
+
end
|
|
214
|
+
|
|
30
215
|
desc "version", "Print Railstart version"
|
|
31
216
|
#
|
|
32
217
|
# @return [void]
|
|
@@ -35,5 +220,104 @@ module Railstart
|
|
|
35
220
|
def version
|
|
36
221
|
puts "Railstart v#{Railstart::VERSION}"
|
|
37
222
|
end
|
|
223
|
+
|
|
224
|
+
PRESET_DIR = File.expand_path("~/.config/railstart/presets")
|
|
225
|
+
GEM_PRESET_DIR = File.expand_path("../../config/presets", __dir__)
|
|
226
|
+
|
|
227
|
+
private
|
|
228
|
+
|
|
229
|
+
def determine_preset_name
|
|
230
|
+
# Explicit --preset flag takes priority
|
|
231
|
+
return options[:preset] if options[:preset]
|
|
232
|
+
|
|
233
|
+
# --default maps to "default" preset
|
|
234
|
+
return "default" if options[:default]
|
|
235
|
+
|
|
236
|
+
nil
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def preset_file_for(name)
|
|
240
|
+
# Check user presets first
|
|
241
|
+
user_path = File.join(PRESET_DIR, "#{name}.yaml")
|
|
242
|
+
return user_path if File.exist?(user_path)
|
|
243
|
+
|
|
244
|
+
# Fall back to built-in gem presets
|
|
245
|
+
gem_path = File.join(GEM_PRESET_DIR, "#{name}.yaml")
|
|
246
|
+
return gem_path if File.exist?(gem_path)
|
|
247
|
+
|
|
248
|
+
# If explicit --preset was used, raise error
|
|
249
|
+
raise Railstart::ConfigLoadError, "Preset '#{name}' not found in #{PRESET_DIR} or gem presets" if options[:preset]
|
|
250
|
+
|
|
251
|
+
# For --default with missing preset, return nil (fall back to builtin config)
|
|
252
|
+
nil
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def example_user_config
|
|
256
|
+
<<~YAML
|
|
257
|
+
---
|
|
258
|
+
# Railstart User Configuration
|
|
259
|
+
# This file overrides built-in defaults for all your Rails projects.
|
|
260
|
+
#
|
|
261
|
+
# Merge behavior: questions and post_actions are merged by 'id'.
|
|
262
|
+
# Override individual fields or add new entries.
|
|
263
|
+
|
|
264
|
+
questions:
|
|
265
|
+
# Example: Change database default to PostgreSQL
|
|
266
|
+
- id: database
|
|
267
|
+
choices:
|
|
268
|
+
- name: PostgreSQL
|
|
269
|
+
value: postgresql
|
|
270
|
+
default: true
|
|
271
|
+
|
|
272
|
+
# Example: Change CSS default to Tailwind
|
|
273
|
+
- id: css
|
|
274
|
+
choices:
|
|
275
|
+
- name: Tailwind
|
|
276
|
+
value: tailwind
|
|
277
|
+
default: true
|
|
278
|
+
|
|
279
|
+
post_actions:
|
|
280
|
+
# Example: Disable bundle install (manage gems manually)
|
|
281
|
+
# - id: bundle_install
|
|
282
|
+
# enabled: false
|
|
283
|
+
|
|
284
|
+
# Example: Add custom post-action
|
|
285
|
+
# - id: setup_linting
|
|
286
|
+
# name: "Setup RuboCop and StandardRB"
|
|
287
|
+
# enabled: true
|
|
288
|
+
# command: "bundle add rubocop rubocop-rails standard --group development"
|
|
289
|
+
YAML
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def example_preset_config
|
|
293
|
+
<<~YAML
|
|
294
|
+
---
|
|
295
|
+
# Example Preset - Customize this for your use case
|
|
296
|
+
# Use with: railstart new my_app --preset example
|
|
297
|
+
|
|
298
|
+
questions:
|
|
299
|
+
- id: database
|
|
300
|
+
choices:
|
|
301
|
+
- name: PostgreSQL
|
|
302
|
+
value: postgresql
|
|
303
|
+
default: true
|
|
304
|
+
|
|
305
|
+
- id: css
|
|
306
|
+
choices:
|
|
307
|
+
- name: Tailwind
|
|
308
|
+
value: tailwind
|
|
309
|
+
default: true
|
|
310
|
+
|
|
311
|
+
- id: api_only
|
|
312
|
+
default: false
|
|
313
|
+
|
|
314
|
+
post_actions:
|
|
315
|
+
- id: init_git
|
|
316
|
+
enabled: true
|
|
317
|
+
|
|
318
|
+
- id: bundle_install
|
|
319
|
+
enabled: true
|
|
320
|
+
YAML
|
|
321
|
+
end
|
|
38
322
|
end
|
|
39
323
|
end
|
data/lib/railstart/config.rb
CHANGED
|
@@ -17,19 +17,25 @@ module Railstart
|
|
|
17
17
|
|
|
18
18
|
class << self
|
|
19
19
|
#
|
|
20
|
-
# Load, merge, and validate configuration from built-in and
|
|
20
|
+
# Load, merge, and validate configuration from built-in, user, and preset sources.
|
|
21
21
|
#
|
|
22
22
|
# @param builtin_path [String] path to default config YAML shipped with the gem
|
|
23
23
|
# @param user_path [String] optional user override YAML path
|
|
24
|
+
# @param preset_path [String] optional preset YAML path (third overlay)
|
|
24
25
|
# @return [Hash] deep-copied, merged, validated configuration hash
|
|
25
26
|
# @raise [Railstart::ConfigLoadError] when YAML files are missing or unreadable
|
|
26
27
|
# @raise [Railstart::ConfigValidationError] when validation fails
|
|
27
28
|
# @example
|
|
28
29
|
# config = Railstart::Config.load
|
|
29
|
-
|
|
30
|
+
# @example With preset
|
|
31
|
+
# config = Railstart::Config.load(preset_path: "~/.config/railstart/presets/api-only.yaml")
|
|
32
|
+
def load(builtin_path: BUILTIN_CONFIG_PATH, user_path: USER_CONFIG_PATH, preset_path: nil)
|
|
30
33
|
builtin = read_yaml(builtin_path, required: true)
|
|
31
34
|
user = read_yaml(user_path, required: false)
|
|
35
|
+
preset = preset_path ? read_yaml(preset_path, required: false) : {}
|
|
36
|
+
|
|
32
37
|
merged = merge_config(builtin, user)
|
|
38
|
+
merged = merge_config(merged, preset) unless preset.empty?
|
|
33
39
|
validate!(merged)
|
|
34
40
|
merged
|
|
35
41
|
end
|
data/lib/railstart/generator.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "tty-prompt"
|
|
4
|
+
require_relative "ui"
|
|
4
5
|
|
|
5
6
|
module Railstart
|
|
6
7
|
# Orchestrates the interactive Rails app generation flow.
|
|
@@ -11,11 +12,13 @@ module Railstart
|
|
|
11
12
|
# @example Run generator with provided config
|
|
12
13
|
# config = Railstart::Config.load
|
|
13
14
|
# Railstart::Generator.new("blog", config: config).run
|
|
15
|
+
# @example Run generator non-interactively
|
|
16
|
+
# Railstart::Generator.new("blog", use_defaults: true).run
|
|
14
17
|
class Generator
|
|
15
18
|
#
|
|
16
19
|
# @param app_name [String, nil] preset app name, prompted if nil
|
|
17
20
|
# @param config [Hash, nil] injected config for testing, defaults to Config.load
|
|
18
|
-
# @param use_defaults [Boolean] skip interactive
|
|
21
|
+
# @param use_defaults [Boolean] skip interactive questions, use config defaults
|
|
19
22
|
# @param prompt [TTY::Prompt] injectable prompt for testing
|
|
20
23
|
def initialize(app_name = nil, config: nil, use_defaults: false, prompt: nil)
|
|
21
24
|
@app_name = app_name
|
|
@@ -28,13 +31,27 @@ module Railstart
|
|
|
28
31
|
#
|
|
29
32
|
# Run the complete generation flow, prompting the user and invoking Rails.
|
|
30
33
|
#
|
|
34
|
+
# Mode selection:
|
|
35
|
+
# - use_defaults: false (default) → interactive wizard
|
|
36
|
+
# - use_defaults: true → collect config defaults, show summary, confirm, run
|
|
37
|
+
#
|
|
31
38
|
# @return [void]
|
|
32
39
|
# @raise [Railstart::ConfigError, Railstart::ConfigValidationError] when configuration is invalid
|
|
33
|
-
# @example Run interactively
|
|
34
|
-
# Railstart::Generator.new.run
|
|
40
|
+
# @example Run interactively
|
|
41
|
+
# Railstart::Generator.new("blog").run
|
|
42
|
+
# @example Run with defaults (noninteractive questions)
|
|
43
|
+
# Railstart::Generator.new("blog", use_defaults: true).run
|
|
35
44
|
def run
|
|
45
|
+
show_welcome_screen unless @use_defaults
|
|
46
|
+
|
|
36
47
|
ask_app_name unless @app_name
|
|
37
|
-
|
|
48
|
+
|
|
49
|
+
if @use_defaults
|
|
50
|
+
collect_defaults
|
|
51
|
+
else
|
|
52
|
+
ask_interactive_questions
|
|
53
|
+
end
|
|
54
|
+
|
|
38
55
|
show_summary
|
|
39
56
|
return unless confirm_proceed?
|
|
40
57
|
|
|
@@ -44,24 +61,24 @@ module Railstart
|
|
|
44
61
|
|
|
45
62
|
private
|
|
46
63
|
|
|
64
|
+
def show_welcome_screen
|
|
65
|
+
UI.show_logo
|
|
66
|
+
UI.show_welcome
|
|
67
|
+
end
|
|
68
|
+
|
|
47
69
|
def ask_app_name
|
|
48
70
|
@app_name = @prompt.ask("App name?", default: "my_app") do |q|
|
|
49
71
|
q.validate(/\A[a-z0-9_-]+\z/, "Must be lowercase letters, numbers, underscores, or hyphens")
|
|
50
72
|
end
|
|
51
73
|
end
|
|
52
74
|
|
|
53
|
-
def ask_questions
|
|
54
|
-
if @use_defaults
|
|
55
|
-
collect_defaults
|
|
56
|
-
else
|
|
57
|
-
ask_interactive_questions
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
75
|
def collect_defaults
|
|
62
76
|
Array(@config["questions"]).each do |question|
|
|
77
|
+
next if should_skip_question?(question)
|
|
78
|
+
|
|
79
|
+
question_id = question["id"]
|
|
63
80
|
default_value = find_default(question)
|
|
64
|
-
@answers[
|
|
81
|
+
@answers[question_id] = default_value unless default_value.nil?
|
|
65
82
|
end
|
|
66
83
|
end
|
|
67
84
|
|
|
@@ -102,14 +119,23 @@ module Railstart
|
|
|
102
119
|
end
|
|
103
120
|
|
|
104
121
|
def ask_select(question)
|
|
105
|
-
|
|
122
|
+
# Convert to hash format: { 'Display Name' => 'value' }
|
|
123
|
+
choices = question["choices"].each_with_object({}) do |choice, hash|
|
|
124
|
+
hash[choice["name"]] = choice["value"]
|
|
125
|
+
end
|
|
106
126
|
default_val = find_default(question)
|
|
107
127
|
|
|
108
|
-
|
|
128
|
+
# TTY::Prompt expects 1-based index for default
|
|
129
|
+
default_index = (question["choices"].index { |c| c["value"] == default_val }&.+(1) if default_val)
|
|
130
|
+
|
|
131
|
+
@prompt.select(question["prompt"], choices, default: default_index)
|
|
109
132
|
end
|
|
110
133
|
|
|
111
134
|
def ask_multi_select(question)
|
|
112
|
-
|
|
135
|
+
# Convert to hash format: { 'Display Name' => 'value' }
|
|
136
|
+
choices = question["choices"].each_with_object({}) do |choice, hash|
|
|
137
|
+
hash[choice["name"]] = choice["value"]
|
|
138
|
+
end
|
|
113
139
|
defaults = question["default"] || []
|
|
114
140
|
|
|
115
141
|
@prompt.multi_select(question["prompt"], choices, default: defaults)
|
|
@@ -131,10 +157,11 @@ module Railstart
|
|
|
131
157
|
end
|
|
132
158
|
|
|
133
159
|
def show_summary
|
|
134
|
-
puts
|
|
135
|
-
|
|
136
|
-
puts
|
|
137
|
-
|
|
160
|
+
puts
|
|
161
|
+
UI.section("Configuration Summary")
|
|
162
|
+
puts
|
|
163
|
+
|
|
164
|
+
summary_lines = ["App name: #{UI.pastel.bright_cyan(@app_name)}"]
|
|
138
165
|
|
|
139
166
|
Array(@config["questions"]).each do |question|
|
|
140
167
|
question_id = question["id"]
|
|
@@ -154,34 +181,53 @@ module Railstart
|
|
|
154
181
|
answer.to_s
|
|
155
182
|
end
|
|
156
183
|
|
|
157
|
-
|
|
184
|
+
summary_lines << "#{label}: #{UI.pastel.bright_white(value_str)}"
|
|
158
185
|
end
|
|
159
|
-
|
|
186
|
+
|
|
187
|
+
box = TTY::Box.frame(
|
|
188
|
+
width: 60,
|
|
189
|
+
padding: [0, 2],
|
|
190
|
+
border: :light,
|
|
191
|
+
style: {
|
|
192
|
+
border: { fg: :bright_black }
|
|
193
|
+
}
|
|
194
|
+
) { summary_lines.join("\n") }
|
|
195
|
+
|
|
196
|
+
puts box
|
|
197
|
+
puts
|
|
160
198
|
end
|
|
161
199
|
|
|
162
200
|
def confirm_proceed?
|
|
163
|
-
return true if @use_defaults
|
|
164
|
-
|
|
165
201
|
@prompt.yes?("Proceed with app generation?")
|
|
166
202
|
end
|
|
167
203
|
|
|
168
204
|
def generate_app
|
|
169
205
|
command = CommandBuilder.build(@app_name, @config, @answers)
|
|
170
206
|
|
|
171
|
-
|
|
172
|
-
|
|
207
|
+
UI.info("Running: #{command}")
|
|
208
|
+
puts
|
|
209
|
+
|
|
210
|
+
# Run rails command outside of bundler context to use system Rails
|
|
211
|
+
success = if defined?(Bundler)
|
|
212
|
+
Bundler.with_unbundled_env { system(command) }
|
|
213
|
+
else
|
|
214
|
+
system(command)
|
|
215
|
+
end
|
|
173
216
|
|
|
174
217
|
return if success
|
|
175
218
|
|
|
219
|
+
UI.error("Failed to generate Rails app. Check the output above for details.")
|
|
176
220
|
raise Error, "Failed to generate Rails app. Check the output above for details."
|
|
177
221
|
end
|
|
178
222
|
|
|
179
223
|
def run_post_actions
|
|
180
224
|
Dir.chdir(@app_name)
|
|
225
|
+
|
|
181
226
|
Array(@config["post_actions"]).each { |action| process_post_action(action) }
|
|
182
|
-
puts
|
|
227
|
+
puts
|
|
228
|
+
UI.success("Rails app created successfully at ./#{@app_name}")
|
|
183
229
|
rescue Errno::ENOENT
|
|
184
|
-
|
|
230
|
+
UI.warning("Could not change to app directory. Post-actions skipped.")
|
|
185
231
|
end
|
|
186
232
|
|
|
187
233
|
def process_post_action(action)
|
|
@@ -198,9 +244,9 @@ module Railstart
|
|
|
198
244
|
end
|
|
199
245
|
|
|
200
246
|
def execute_action(action)
|
|
201
|
-
|
|
247
|
+
UI.info(action["name"].to_s)
|
|
202
248
|
success = system(action["command"])
|
|
203
|
-
|
|
249
|
+
UI.warning("Post-action '#{action["name"]}' failed. Continuing anyway.") unless success
|
|
204
250
|
end
|
|
205
251
|
|
|
206
252
|
def should_run_action?(action)
|
data/lib/railstart/ui.rb
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tty-box"
|
|
4
|
+
|
|
5
|
+
module Railstart
|
|
6
|
+
# Provides UI enhancement utilities for the Railstart CLI.
|
|
7
|
+
#
|
|
8
|
+
# Handles ASCII art headers, styled boxes, and visual polish
|
|
9
|
+
# to create a Laravel-like installer experience.
|
|
10
|
+
module UI
|
|
11
|
+
# ASCII art logo for Railstart
|
|
12
|
+
LOGO = <<~LOGO
|
|
13
|
+
╔═╗┌─┐┬┬ ┌─┐┌┬┐┌─┐┬─┐┌┬┐
|
|
14
|
+
╠╦╝├─┤││ └─┐ │ ├─┤├┬┘ │
|
|
15
|
+
╩╚═┴ ┴┴┴─┘└─┘ ┴ ┴ ┴┴└─ ┴
|
|
16
|
+
LOGO
|
|
17
|
+
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# Display the Railstart ASCII art logo with version and optional color.
|
|
22
|
+
#
|
|
23
|
+
# @param color [Symbol] color name (e.g., :cyan, :green, :magenta)
|
|
24
|
+
# @return [void]
|
|
25
|
+
def show_logo(color: :cyan)
|
|
26
|
+
require_relative "version"
|
|
27
|
+
puts pastel.send(color, LOGO)
|
|
28
|
+
puts pastel.dim(" v#{Railstart::VERSION}")
|
|
29
|
+
puts
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Display a styled welcome message in a box.
|
|
34
|
+
#
|
|
35
|
+
# @param message [String] the welcome text to display (defaults to Rails version message)
|
|
36
|
+
# @return [void]
|
|
37
|
+
def show_welcome(message = nil)
|
|
38
|
+
message ||= "Interactive Rails #{rails_version} Application Generator"
|
|
39
|
+
|
|
40
|
+
box = TTY::Box.frame(
|
|
41
|
+
width: 60,
|
|
42
|
+
height: 3,
|
|
43
|
+
align: :center,
|
|
44
|
+
padding: [0, 1],
|
|
45
|
+
border: :thick,
|
|
46
|
+
style: {
|
|
47
|
+
fg: :bright_white,
|
|
48
|
+
border: { fg: :cyan }
|
|
49
|
+
}
|
|
50
|
+
) { message }
|
|
51
|
+
|
|
52
|
+
puts box
|
|
53
|
+
puts # blank line
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
#
|
|
57
|
+
# Detect the installed Rails version.
|
|
58
|
+
#
|
|
59
|
+
# @return [String] Rails version or "Unknown" if not found
|
|
60
|
+
def rails_version
|
|
61
|
+
require "bundler"
|
|
62
|
+
Bundler.with_unbundled_env do
|
|
63
|
+
version_output = `rails --version 2>/dev/null`.strip
|
|
64
|
+
version_output[/Rails (\d+\.\d+\.\d+)/, 1] || "Unknown"
|
|
65
|
+
end
|
|
66
|
+
rescue StandardError
|
|
67
|
+
"Unknown"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
#
|
|
71
|
+
# Display a section header with optional separator line.
|
|
72
|
+
#
|
|
73
|
+
# @param title [String] section title
|
|
74
|
+
# @param separator [Boolean] whether to show a line underneath
|
|
75
|
+
# @return [void]
|
|
76
|
+
def section(title, separator: true)
|
|
77
|
+
puts pastel.cyan.bold(title.to_s)
|
|
78
|
+
puts pastel.dim("─" * 60) if separator
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
#
|
|
82
|
+
# Display a success message with checkmark.
|
|
83
|
+
#
|
|
84
|
+
# @param message [String] the success message
|
|
85
|
+
# @return [void]
|
|
86
|
+
def success(message)
|
|
87
|
+
puts pastel.green("✓ #{message}")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
# Display a warning message with icon.
|
|
92
|
+
#
|
|
93
|
+
# @param message [String] the warning message
|
|
94
|
+
# @return [void]
|
|
95
|
+
def warning(message)
|
|
96
|
+
puts pastel.yellow("⚠ #{message}")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
#
|
|
100
|
+
# Display an error message with icon.
|
|
101
|
+
#
|
|
102
|
+
# @param message [String] the error message
|
|
103
|
+
# @return [void]
|
|
104
|
+
def error(message)
|
|
105
|
+
puts pastel.red("✗ #{message}")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
#
|
|
109
|
+
# Display an info message with icon.
|
|
110
|
+
#
|
|
111
|
+
# @param message [String] the info message
|
|
112
|
+
# @return [void]
|
|
113
|
+
def info(message)
|
|
114
|
+
puts pastel.blue("ℹ #{message}")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
#
|
|
118
|
+
# Lazy-load Pastel for color formatting.
|
|
119
|
+
#
|
|
120
|
+
# @return [Pastel] pastel instance
|
|
121
|
+
def pastel
|
|
122
|
+
@pastel ||= begin
|
|
123
|
+
require "pastel"
|
|
124
|
+
Pastel.new
|
|
125
|
+
rescue LoadError
|
|
126
|
+
# Fallback to no-op if pastel is not available
|
|
127
|
+
# (tty-prompt depends on pastel, so this should never happen)
|
|
128
|
+
NullPastel.new
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Null object pattern for when Pastel is unavailable
|
|
133
|
+
class NullPastel
|
|
134
|
+
def method_missing(_method, *args)
|
|
135
|
+
args.first.to_s
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def respond_to_missing?(*)
|
|
139
|
+
true
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
data/lib/railstart/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: railstart
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dpaluy
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: tty-box
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
41
|
name: tty-prompt
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,6 +67,8 @@ files:
|
|
|
53
67
|
- LICENSE.txt
|
|
54
68
|
- README.md
|
|
55
69
|
- Rakefile
|
|
70
|
+
- config/presets/api-only.yaml
|
|
71
|
+
- config/presets/default.yaml
|
|
56
72
|
- config/rails8_defaults.yaml
|
|
57
73
|
- exe/railstart
|
|
58
74
|
- lib/railstart.rb
|
|
@@ -61,6 +77,7 @@ files:
|
|
|
61
77
|
- lib/railstart/config.rb
|
|
62
78
|
- lib/railstart/errors.rb
|
|
63
79
|
- lib/railstart/generator.rb
|
|
80
|
+
- lib/railstart/ui.rb
|
|
64
81
|
- lib/railstart/version.rb
|
|
65
82
|
homepage: https://github.com/dpaluy/railstart
|
|
66
83
|
licenses:
|
|
@@ -86,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
86
103
|
- !ruby/object:Gem::Version
|
|
87
104
|
version: '0'
|
|
88
105
|
requirements: []
|
|
89
|
-
rubygems_version: 3.
|
|
106
|
+
rubygems_version: 3.6.9
|
|
90
107
|
specification_version: 4
|
|
91
108
|
summary: Rails application starter and development utilities
|
|
92
109
|
test_files: []
|