outset 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 863fabe79965bc0d21477932bf97e165ad54fbcd385ba972d198f40dec78e3df
4
+ data.tar.gz: 3ea461e5243abbff2cf8ed693b919edc7d23467c7c039255f3bbf914009fb7df
5
+ SHA512:
6
+ metadata.gz: 3faf1b252d4b2b15126422ba2c83771be0a39fcb865194526d654a421a408062e3542e560bf4f5a6aec4926b1583eed8d5931915d22bb112fab8f69b1ac172b2
7
+ data.tar.gz: 3d06676a5c1a7b330b90ad703b530bd963c2d2384f902a42d492b9200e28342ce6d5bd22e2f18b00433a69e08241d2fe1665b53a101d4cc2362217a73ac96af4
data/exe/outset ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/outset"
5
+
6
+ Outset::CLI.start(ARGV)
data/lib/outset/cli.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+ require_relative "commands/new"
5
+ require_relative "commands/config_cmd"
6
+ require_relative "commands/doctor"
7
+ require_relative "recipes"
8
+
9
+ module Outset
10
+ class CLI < Thor
11
+ def self.exit_on_failure? = true
12
+
13
+ desc "new APP_NAME", "Bootstrap a new Rails application"
14
+ option :database, aliases: "-d", type: :string, default: nil, desc: "Database (postgresql, mysql, sqlite3)"
15
+ option :css, aliases: "-c", type: :string, default: nil, desc: "CSS framework (tailwind, bootstrap, sass, postcss, none)"
16
+ option :js, aliases: "-j", type: :string, default: nil, desc: "JavaScript bundler (importmap, esbuild, bun, webpack, rollup)"
17
+ option :recipe, aliases: "-r", type: :string, default: nil, desc: "Use a predefined recipe"
18
+ option :yes, aliases: "-y", type: :boolean, default: false, desc: "Accept all defaults, skip prompts"
19
+ def new(app_name)
20
+ UI.banner
21
+ Commands::New.new(app_name, options).run
22
+ end
23
+
24
+ desc "config [ACTION]", "View or edit your outset config (~/.outset/config.toml)"
25
+ def config(action = "show")
26
+ Commands::ConfigCmd.new(action).run
27
+ end
28
+
29
+ desc "doctor", "Check that your environment is ready to use outset"
30
+ def doctor
31
+ UI.banner
32
+ Commands::Doctor.new.run
33
+ end
34
+
35
+ desc "recipes", "List available recipes"
36
+ def recipes
37
+ UI.info("Available recipes:")
38
+ puts
39
+ Recipes.all.each do |name, recipe|
40
+ puts " #{UI::PASTEL.bold(name.ljust(10))} #{recipe[:description]}"
41
+ UI.muted(" db=#{recipe[:database]} css=#{recipe[:css]} js=#{recipe[:js]} gems=#{recipe[:gems].empty? ? "none" : recipe[:gems].join(", ")}")
42
+ puts
43
+ end
44
+ end
45
+
46
+ desc "version", "Print outset version"
47
+ def version
48
+ puts "outset v#{Outset::VERSION}"
49
+ end
50
+
51
+ map %w[--version -v] => :version
52
+ end
53
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Outset
4
+ module Commands
5
+ class ConfigCmd
6
+ def initialize(action)
7
+ @action = action
8
+ end
9
+
10
+ def run
11
+ case @action
12
+ when "show" then show
13
+ when "init" then Config.init!
14
+ when "edit" then edit
15
+ when "path" then puts Config::CONFIG_FILE
16
+ else
17
+ UI.error("Unknown config action: '#{@action}'")
18
+ UI.muted(" Available: show, init, edit, path")
19
+ exit(1)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def show
26
+ if File.exist?(Config::CONFIG_FILE)
27
+ UI.info("Config file: #{Config::CONFIG_FILE}")
28
+ puts
29
+ puts File.read(Config::CONFIG_FILE)
30
+ else
31
+ UI.warn("No config file found. Run `outset config init` to create one.")
32
+ puts
33
+ UI.muted("Default values:")
34
+ puts Config.default_toml
35
+ end
36
+ end
37
+
38
+ def edit
39
+ unless File.exist?(Config::CONFIG_FILE)
40
+ Config.init!
41
+ end
42
+ editor = ENV["VISUAL"] || ENV["EDITOR"] || "nano"
43
+ system("#{editor} #{Config::CONFIG_FILE}")
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Outset
4
+ module Commands
5
+ class Doctor
6
+ CHECKS = [
7
+ {
8
+ name: "Ruby >= 3.1",
9
+ check: -> { RUBY_VERSION >= "3.1.0" },
10
+ fix: "Install Ruby 3.1+ via rbenv or asdf"
11
+ },
12
+ {
13
+ name: "Rails installed",
14
+ check: -> { system("which rails > /dev/null 2>&1") },
15
+ fix: "Run: gem install rails"
16
+ },
17
+ {
18
+ name: "Git installed",
19
+ check: -> { system("which git > /dev/null 2>&1") },
20
+ fix: "Install git from https://git-scm.com"
21
+ },
22
+ {
23
+ name: "Git user.name configured",
24
+ check: -> { !`git config user.name`.strip.empty? rescue false },
25
+ fix: "Run: git config --global user.name 'Your Name'"
26
+ },
27
+ {
28
+ name: "Git user.email configured",
29
+ check: -> { !`git config user.email`.strip.empty? rescue false },
30
+ fix: "Run: git config --global user.email 'you@example.com'"
31
+ },
32
+ {
33
+ name: "Outset config file",
34
+ check: -> { File.exist?(Config::CONFIG_FILE) },
35
+ fix: "Run: outset config init"
36
+ }
37
+ ].freeze
38
+
39
+ def run
40
+ UI.info("Checking your environment...\n")
41
+ all_passed = true
42
+
43
+ CHECKS.each do |check|
44
+ if check[:check].call
45
+ UI.success(check[:name])
46
+ else
47
+ UI.warn("#{check[:name]} → #{check[:fix]}")
48
+ all_passed = false
49
+ end
50
+ end
51
+
52
+ puts
53
+ if all_passed
54
+ UI.success("All checks passed. You're ready to use outset!")
55
+ else
56
+ UI.warn("Some checks failed. Fix the issues above and run `outset doctor` again.")
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require "tmpdir"
5
+
6
+ module Outset
7
+ module Commands
8
+ class New
9
+ DATABASES = %w[postgresql mysql sqlite3].freeze
10
+ CSS_OPTIONS = %w[tailwind bootstrap sass postcss none].freeze
11
+ JS_OPTIONS = %w[importmap esbuild bun webpack rollup].freeze
12
+
13
+ OPTIONAL_GEMS = [
14
+ { name: "Devise (Authentication)", value: "devise" },
15
+ { name: "Pundit (Authorization)", value: "pundit" },
16
+ { name: "Sidekiq (Background Jobs)", value: "sidekiq" },
17
+ { name: "RSpec + FactoryBot (Tests)", value: "rspec" },
18
+ { name: "Annotate (Model Annotations)", value: "annotate" },
19
+ { name: "Letter Opener (Email preview)",value: "letter_opener" },
20
+ { name: "Pagy (Pagination)", value: "pagy" },
21
+ ].freeze
22
+
23
+ def initialize(app_name, options = {})
24
+ @app_name = app_name
25
+ @options = options
26
+ @resolved = Config.resolve(options)
27
+ @prompt = TTY::Prompt.new(interrupt: :exit)
28
+ end
29
+
30
+ def run
31
+ validate_app_name!
32
+ validate_rails_installed!
33
+
34
+ selections = if effective_recipe
35
+ recipe_selections
36
+ elsif @options[:yes]
37
+ default_selections
38
+ else
39
+ prompt_user
40
+ end
41
+
42
+ confirm_and_run(selections)
43
+ end
44
+
45
+ private
46
+
47
+ def effective_recipe
48
+ @options[:recipe] || @resolved["default_recipe"]
49
+ end
50
+
51
+ def validate_app_name!
52
+ unless @app_name.match?(/\A[a-z][a-z0-9_]*\z/)
53
+ UI.error("Invalid app name: '#{@app_name}'")
54
+ UI.muted(" App names must start with a letter and contain only lowercase letters, numbers, and underscores.")
55
+ exit(1)
56
+ end
57
+
58
+ if Dir.exist?(@app_name)
59
+ UI.error("Directory '#{@app_name}' already exists.")
60
+ exit(1)
61
+ end
62
+ end
63
+
64
+ def validate_rails_installed!
65
+ unless system("which rails > /dev/null 2>&1")
66
+ UI.error("Rails is not installed. Run: gem install rails")
67
+ exit(1)
68
+ end
69
+ end
70
+
71
+ def recipe_selections
72
+ name = effective_recipe
73
+ recipe = Recipes.find(name)
74
+ UI.info("Using recipe: #{name} — #{recipe[:description]}")
75
+ puts
76
+ {
77
+ database: @options[:database] || recipe[:database],
78
+ css: @options[:css] || recipe[:css],
79
+ js: @options[:js] || recipe[:js],
80
+ gems: (recipe[:gems] + @resolved["gems"]).uniq
81
+ }
82
+ end
83
+
84
+ def default_selections
85
+ {
86
+ database: @resolved["database"],
87
+ css: @resolved["css"],
88
+ js: @resolved["javascript"],
89
+ gems: @resolved["gems"]
90
+ }
91
+ end
92
+
93
+ def prompt_user
94
+ UI.info("Configuring: #{@app_name}")
95
+ puts
96
+
97
+ database = if @options[:database]
98
+ UI.muted(" Database: #{@options[:database]} (from flag)")
99
+ @options[:database]
100
+ else
101
+ @prompt.select("Database:", DATABASES, default: @resolved["database"])
102
+ end
103
+
104
+ css = if @options[:css]
105
+ UI.muted(" CSS: #{@options[:css]} (from flag)")
106
+ @options[:css]
107
+ else
108
+ @prompt.select("CSS framework:", CSS_OPTIONS, default: @resolved["css"])
109
+ end
110
+
111
+ js = if @options[:js]
112
+ UI.muted(" JS: #{@options[:js]} (from flag)")
113
+ @options[:js]
114
+ else
115
+ @prompt.select("JavaScript bundler:", JS_OPTIONS, default: @resolved["javascript"])
116
+ end
117
+
118
+ always_gems = @resolved["gems"]
119
+ gems = @prompt.multi_select("Optional gems: (space to select, enter to confirm)") do |menu|
120
+ OPTIONAL_GEMS.each do |gem_opt|
121
+ preselected = always_gems.include?(gem_opt[:value])
122
+ menu.choice gem_opt[:name], gem_opt[:value], disabled: (preselected ? "(always)" : false)
123
+ end
124
+ end
125
+ gems += always_gems
126
+
127
+ { database: database, css: css, js: js, gems: gems.uniq }
128
+ end
129
+
130
+ def confirm_and_run(selections)
131
+ puts
132
+ UI.info("Ready to create '#{@app_name}' with:")
133
+ UI.muted(" Recipe : #{effective_recipe}") if effective_recipe
134
+ UI.muted(" Database : #{selections[:database]}")
135
+ UI.muted(" CSS : #{selections[:css]}")
136
+ UI.muted(" JS : #{selections[:js]}")
137
+ UI.muted(" Gems : #{selections[:gems].empty? ? "none" : selections[:gems].join(", ")}")
138
+ puts
139
+
140
+ return unless @options[:yes] || @prompt.yes?("Proceed?")
141
+
142
+ generate(selections)
143
+ end
144
+
145
+ def generate(selections)
146
+ rails_flags = build_rails_flags(selections)
147
+
148
+ if selections[:gems].any?
149
+ template_path = write_template(selections[:gems])
150
+ rails_flags << "--template=#{template_path}"
151
+ end
152
+
153
+ cmd = "rails new #{@app_name} #{rails_flags.join(" ")}"
154
+ UI.info("Running: #{cmd}")
155
+ puts
156
+
157
+ success = Bundler.with_unbundled_env { system(cmd) }
158
+
159
+ File.delete(template_path) if template_path && File.exist?(template_path)
160
+
161
+ if success
162
+ puts
163
+ UI.success("App created! Next steps:")
164
+ UI.muted(" cd #{@app_name}")
165
+ UI.muted(" bin/setup")
166
+ UI.muted(" bin/dev")
167
+ else
168
+ UI.error("rails new failed. See output above for details.")
169
+ exit(1)
170
+ end
171
+ end
172
+
173
+ def build_rails_flags(selections)
174
+ flags = []
175
+ flags << "--database=#{selections[:database]}"
176
+ flags << "--css=#{selections[:css]}" unless selections[:css] == "none"
177
+ flags << "--javascript=#{selections[:js]}"
178
+ flags << "--skip-test" if selections[:gems].include?("rspec")
179
+ flags
180
+ end
181
+
182
+ # Writes a temporary Rails template.rb file and returns its path
183
+ def write_template(gems)
184
+ template = build_template(gems)
185
+ path = File.join(Dir.tmpdir, "outset_template_#{@app_name}_#{Process.pid}.rb")
186
+ File.write(path, template)
187
+ path
188
+ end
189
+
190
+ def build_template(gems)
191
+ lines = ["# Generated by outset v#{Outset::VERSION}", ""]
192
+
193
+ # Gem declarations
194
+ lines << "# ── Gems ─────────────────────────────────────────────"
195
+ gems.each { |g| lines << gem_declaration(g) }
196
+ lines << ""
197
+
198
+ # after_bundle block
199
+ lines << "after_bundle do"
200
+ gems.each do |g|
201
+ after = after_bundle_steps(g)
202
+ lines += after.map { |l| " #{l}" } if after.any?
203
+ end
204
+ lines << " git add: '.', commit: %(-m 'Initial scaffold via outset')"
205
+ lines << "end"
206
+ lines << ""
207
+
208
+ lines.join("\n")
209
+ end
210
+
211
+ def gem_declaration(gem_name)
212
+ case gem_name
213
+ when "rspec"
214
+ "gem_group :development, :test do\n gem 'rspec-rails'\n gem 'factory_bot_rails'\n gem 'faker'\nend"
215
+ when "letter_opener"
216
+ "gem_group :development do\n gem 'letter_opener'\nend"
217
+ when "sidekiq"
218
+ "gem 'sidekiq'"
219
+ else
220
+ "gem '#{gem_name}'"
221
+ end
222
+ end
223
+
224
+ def after_bundle_steps(gem_name)
225
+ case gem_name
226
+ when "devise" then ["generate 'devise:install'", "generate 'devise', 'User'"]
227
+ when "pundit" then ["generate 'pundit:install'"]
228
+ when "rspec" then ["generate 'rspec:install'", "rails_command 'db:create'"]
229
+ when "sidekiq" then ["# Add Sidekiq as ActiveJob backend in config/application.rb manually"]
230
+ else []
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Outset
6
+ class Config
7
+ CONFIG_DIR = File.expand_path("~/.outset")
8
+ CONFIG_FILE = File.join(CONFIG_DIR, "config.toml")
9
+
10
+ DEFAULTS = {
11
+ "defaults" => {
12
+ "database" => "postgresql",
13
+ "css" => "tailwind",
14
+ "javascript" => "importmap"
15
+ },
16
+ "skip" => {
17
+ "rubocop" => false,
18
+ "brakeman" => false,
19
+ "docker" => false
20
+ },
21
+ "gems" => {
22
+ "always" => []
23
+ },
24
+ "recipes" => {
25
+ "default" => nil
26
+ }
27
+ }.freeze
28
+
29
+ def self.load
30
+ new.load
31
+ end
32
+
33
+ def load
34
+ return DEFAULTS.dup unless File.exist?(CONFIG_FILE)
35
+
36
+ require "toml-rb"
37
+ user_config = TomlRB.load_file(CONFIG_FILE)
38
+ deep_merge(DEFAULTS.dup, user_config)
39
+ rescue => e
40
+ UI.warn("Could not read config file: #{e.message}. Using defaults.")
41
+ DEFAULTS.dup
42
+ end
43
+
44
+ def self.resolve(options = {})
45
+ config = load
46
+ default_recipe = config.dig("recipes", "default")
47
+ default_recipe = nil if default_recipe.nil? || default_recipe.to_s.strip.empty?
48
+ {
49
+ "database" => options[:database] || ENV["OUTSET_DATABASE"] || config.dig("defaults", "database"),
50
+ "css" => options[:css] || ENV["OUTSET_CSS"] || config.dig("defaults", "css"),
51
+ "javascript" => options[:js] || ENV["OUTSET_JS"] || config.dig("defaults", "javascript"),
52
+ "gems" => config.dig("gems", "always") || [],
53
+ "default_recipe" => default_recipe
54
+ }
55
+ end
56
+
57
+ def self.init!
58
+ return if File.exist?(CONFIG_FILE)
59
+
60
+ FileUtils.mkdir_p(CONFIG_DIR)
61
+ File.write(CONFIG_FILE, default_toml)
62
+ UI.success("Created config file at #{CONFIG_FILE}")
63
+ end
64
+
65
+ def self.default_toml
66
+ <<~TOML
67
+ # ~/.outset/config.toml
68
+ # Edit this file to set your personal defaults.
69
+
70
+ [defaults]
71
+ database = "postgresql"
72
+ css = "tailwind"
73
+ javascript = "importmap"
74
+
75
+ [skip]
76
+ rubocop = false
77
+ brakeman = false
78
+ docker = false
79
+
80
+ [gems]
81
+ always = [] # Gems added to every new app, e.g. ["annotate", "letter_opener"]
82
+
83
+ [recipes]
84
+ default = "" # Name of your default recipe, e.g. "saas"
85
+
86
+ # Define custom recipes (uncomment and edit to add your own):
87
+ # [recipes.mystartup]
88
+ # description = "My startup stack"
89
+ # database = "postgresql"
90
+ # css = "tailwind"
91
+ # js = "esbuild"
92
+ # gems = ["devise", "sidekiq", "pagy"]
93
+ TOML
94
+ end
95
+
96
+ private
97
+
98
+ def deep_merge(base, override)
99
+ base.merge(override) do |_key, base_val, override_val|
100
+ if base_val.is_a?(Hash) && override_val.is_a?(Hash)
101
+ deep_merge(base_val, override_val)
102
+ else
103
+ override_val
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Outset
4
+ module Recipes
5
+ REGISTRY = {
6
+ "saas" => {
7
+ description: "Full SaaS stack — auth, jobs, pagination",
8
+ database: "postgresql",
9
+ css: "tailwind",
10
+ js: "importmap",
11
+ gems: %w[devise pundit sidekiq pagy annotate letter_opener]
12
+ },
13
+ "api" => {
14
+ description: "API-only app — no frontend assets",
15
+ database: "postgresql",
16
+ css: "none",
17
+ js: "importmap",
18
+ gems: %w[devise rspec]
19
+ },
20
+ "minimal" => {
21
+ description: "Bare minimum — SQLite, no extras",
22
+ database: "sqlite3",
23
+ css: "none",
24
+ js: "importmap",
25
+ gems: []
26
+ }
27
+ }.freeze
28
+
29
+ def self.find(name)
30
+ REGISTRY[name] || user_recipes[name] || begin
31
+ UI.error("Unknown recipe: '#{name}'")
32
+ UI.muted(" Available recipes: #{all.keys.join(", ")}")
33
+ exit(1)
34
+ end
35
+ end
36
+
37
+ def self.all
38
+ REGISTRY.merge(user_recipes)
39
+ end
40
+
41
+ def self.user_recipes
42
+ config = Config.load
43
+ (config["recipes"] || {}).each_with_object({}) do |(name, value), hash|
44
+ next unless value.is_a?(Hash)
45
+ hash[name] = {
46
+ description: value["description"] || "Custom recipe",
47
+ database: value["database"] || "postgresql",
48
+ css: value["css"] || "tailwind",
49
+ js: value["js"] || "importmap",
50
+ gems: value["gems"] || []
51
+ }
52
+ end
53
+ end
54
+ end
55
+ end
data/lib/outset/ui.rb ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pastel"
4
+
5
+ module Outset
6
+ module UI
7
+ PASTEL = Pastel.new
8
+
9
+ def self.banner
10
+ puts PASTEL.bold.blue(<<~BANNER)
11
+ ___ _ _ _____ ____ _____ _____
12
+ / _ \\ | | | ||_ _|/ ___| | ____|_ _|
13
+ | | | || | | | | | \\___ \\ | _| | |
14
+ | |_| || |_| | | | ___) || |___ | |
15
+ \\___/ \\___/ |_| |____/ |_____| |_|
16
+ BANNER
17
+ puts PASTEL.dim(" Rails Application Bootstrapper v#{Outset::VERSION}")
18
+ puts
19
+ end
20
+
21
+ def self.success(msg) = puts PASTEL.green("✓ #{msg}")
22
+ def self.error(msg) = puts PASTEL.red("✗ #{msg}")
23
+ def self.info(msg) = puts PASTEL.cyan("→ #{msg}")
24
+ def self.warn(msg) = puts PASTEL.yellow("! #{msg}")
25
+ def self.muted(msg) = puts PASTEL.dim(msg)
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Outset
4
+ VERSION = "0.1.0"
5
+ end
data/lib/outset.rb ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "outset/version"
4
+ require_relative "outset/ui"
5
+ require_relative "outset/config"
6
+ require_relative "outset/recipes"
7
+ require_relative "outset/cli"
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: outset
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kingsley Chijioke
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: thor
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: tty-prompt
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.23'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.23'
40
+ - !ruby/object:Gem::Dependency
41
+ name: toml-rb
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: pastel
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.8'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.8'
68
+ - !ruby/object:Gem::Dependency
69
+ name: minitest
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '5.20'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '5.20'
82
+ - !ruby/object:Gem::Dependency
83
+ name: minitest-reporters
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.6'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.6'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rake
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '13.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '13.0'
110
+ description: A personal Rails application bootstrapper with interactive prompts, a
111
+ config file, and predefined recipes — callable as `outset new <app_name>`.
112
+ email:
113
+ - dev@kingsleychijioke.me
114
+ executables:
115
+ - outset
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - exe/outset
120
+ - lib/outset.rb
121
+ - lib/outset/cli.rb
122
+ - lib/outset/commands/config_cmd.rb
123
+ - lib/outset/commands/doctor.rb
124
+ - lib/outset/commands/new.rb
125
+ - lib/outset/config.rb
126
+ - lib/outset/recipes.rb
127
+ - lib/outset/ui.rb
128
+ - lib/outset/version.rb
129
+ homepage: https://github.com/kinsomicrote/outset
130
+ licenses:
131
+ - MIT
132
+ metadata: {}
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: 3.1.0
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubygems_version: 4.0.3
148
+ specification_version: 4
149
+ summary: Bootstrap new Rails applications your way
150
+ test_files: []