Class: TsykvasRailsTemplate::Generators::InstallGenerator

Inherits:
Rails::Generators::Base
  • Object
show all
Includes:
BootstrapInstaller
Defined in:
lib/generators/tsykvas_rails_template/install/install_generator.rb

Constant Summary collapse

SHIPPED_DOCS =
%w[
  architecture
  authentication
  background-jobs
  code-style
  commands
  companions
  concepts-refactoring
  database
  deployment
  design-system
  documentation
  forms
  i18n
  routing-and-namespaces
  security
  stimulus-controllers
  testing
  testing-examples
  tsykvas_rails_template
  ui-components
].freeze

Constants included from BootstrapInstaller

BootstrapInstaller::APPLICATION_JS_BOOTSTRAP_BLOCK, BootstrapInstaller::APPLICATION_JS_PATH, BootstrapInstaller::BOOTSTRAP_SCSS_ENTRY_BODY, BootstrapInstaller::BOOTSTRAP_SCSS_ENTRY_PATH, BootstrapInstaller::DARTSASS_INITIALIZER_BODY, BootstrapInstaller::DARTSASS_INITIALIZER_PATH, BootstrapInstaller::DARTSASS_MANAGED_HEADER, BootstrapInstaller::IMPORTMAP_PINS, BootstrapInstaller::PROCFILE_DEV_BODY, BootstrapInstaller::PROCFILE_DEV_PATH

Instance Method Summary collapse

Instance Method Details

#add_concepts_to_autoload_pathsObject



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 71

def add_concepts_to_autoload_paths
  return if options[:skip_autoload_paths]

  target = destination_path("config/application.rb")
  return unless File.exist?(target)

  marker = "app/concepts"
  contents = File.read(target)
  if contents.include?("#{marker}]") || contents.include?("#{marker}\"]")
    say_status :exist, "config.autoload_paths already includes #{marker}", :blue
    return
  end

  application "config.autoload_paths += %W[\#{config.root}/app/concepts]\n"
end

#add_root_routeObject



127
128
129
130
131
132
133
134
135
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 127

def add_root_route
  return if options[:skip_home_example]

  routes_path = destination_path("config/routes.rb")
  return unless File.exist?(routes_path)
  return if File.read(routes_path).match?(/^\s*root\s/)

  route 'root "home#index"'
end

#announce_completionObject



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 228

def announce_completion
  say ""
  say "  tsykvas_rails_template installed.", :green
  say "    A working Home example landed at /app/concepts/home/ with"
  say "    `root \"home#index\"` already in routes — start `bin/rails server`"
  say "    and visit http://localhost:3000 to see it (unless you passed"
  say "    --skip-home-example)."
  say ""
  say "    Next steps:"
  say "      1. rails g tsykvas_rails_template:companions"
  say "         (adds devise / simple_form / rspec stack / mini_magick / etc."
  say "          and runs their :install sub-generators)"
  say "      2. rails g tsykvas_rails_template:concept <Name> [--controller]"
  say "         scaffolds your domain concepts."
  say "      3. Open Claude Code and run /tsykvas-claude to refresh"
  say "         probe-driven sections in CLAUDE.md + .claude/docs/"
  say "         (concept folders, gem versions, branch names) when"
  say "         your stack changes."
  say ""
end

#clean_propshaft_cacheObject

Propshaft serves from ‘public/assets/` whenever a `.manifest.json` is there, completely bypassing live `app/assets/builds/` and `app/javascript/`. A stale dir from a prior `rails assets:precompile` silently freezes the dev environment. Clean it here — but only if `.gitignore` lists it (so we know it’s a transient cache, not checked-in content). ‘rails new` adds `/public/assets` by default.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 155

def clean_propshaft_cache
  require "fileutils"

  cache_dir = destination_path("public/assets")
  return unless File.directory?(cache_dir)

  gitignore = destination_path(".gitignore")
  listed = File.exist?(gitignore) && File.read(gitignore).match?(%r{^/?public/assets\b})
  unless listed
    say_status :skip,
               "public/assets/ exists but is not in .gitignore — leaving alone " \
               "(it might be checked-in content rather than a Propshaft cache).",
               :yellow
    return
  end

  FileUtils.rm_rf(cache_dir)
  say_status :clean,
             "Removed stale public/assets/ (Propshaft would otherwise shadow live " \
             "app/assets/builds/ and app/javascript/ in dev).",
             :green
end

#copy_claude_payloadObject



201
202
203
204
205
206
207
208
209
210
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 201

def copy_claude_payload
  return if options[:skip_claude]

  directory ".claude/agents", ".claude/agents"
  directory ".claude/commands", ".claude/commands"
  empty_directory ".claude/docs" unless File.directory?(destination_path(".claude/docs"))
  SHIPPED_DOCS.each do |name|
    copy_file ".claude/docs/#{name}.md", ".claude/docs/#{name}.md"
  end
end

#copy_concepts_baseObject



62
63
64
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 62

def copy_concepts_base
  directory "app/concepts/base", "app/concepts/base"
end

#copy_operations_methods_concernObject



66
67
68
69
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 66

def copy_operations_methods_concern
  copy_file "app/controllers/concerns/operations_methods.rb",
            "app/controllers/concerns/operations_methods.rb"
end

#create_application_policyObject



103
104
105
106
107
108
109
110
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 103

def create_application_policy
  return if options[:skip_application_policy]
  return if File.exist?(destination_path("app/policies/application_policy.rb"))

  empty_directory "app/policies" unless File.directory?(destination_path("app/policies"))
  copy_file "app/policies/application_policy.rb",
            "app/policies/application_policy.rb"
end

#generate_home_exampleObject



112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 112

def generate_home_example
  return if options[:skip_home_example]
  return if File.exist?(destination_path("app/controllers/home_controller.rb"))
  return if File.directory?(destination_path("app/concepts/home"))

  copy_file "app/controllers/home_controller.rb",
            "app/controllers/home_controller.rb"
  directory "app/concepts/home", "app/concepts/home"

  empty_directory "app/policies" unless File.directory?(destination_path("app/policies"))
  return if File.exist?(destination_path("app/policies/home_policy.rb"))

  copy_file "app/policies/home_policy.rb", "app/policies/home_policy.rb"
end

#install_bootstrapObject



137
138
139
140
141
142
143
144
145
146
147
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 137

def install_bootstrap
  return if options[:skip_bootstrap]

  install_bootstrap_steps

  say_status :bootstrap,
             "Bootstrap 5.3 + dartsass-rails wired. Run `bin/dev` for the live SCSS watcher, " \
             "or `bin/rails dartsass:build` once before `bin/rails server`. " \
             "Pass --skip-bootstrap to opt out.",
             :green
end

#swap_database_to_postgresqlObject



55
56
57
58
59
60
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 55

def swap_database_to_postgresql
  return if options[:keep_sqlite]

  swap_gemfile_to_pg
  swap_database_yml_to_pg
end

#wire_application_controllerObject



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 87

def wire_application_controller
  target_rel = "app/controllers/application_controller.rb"
  target_abs = destination_path(target_rel)
  return unless File.exist?(target_abs)

  contents = File.read(target_abs)

  unless contents.include?("Pundit::Authorization") || contents.include?("include Pundit\n")
    inject_into_class target_rel, "ApplicationController", "  include Pundit::Authorization\n"
  end

  return if contents.include?("OperationsMethods")

  inject_into_class target_rel, "ApplicationController", "  include OperationsMethods\n"
end

#write_claude_mdObject



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/generators/tsykvas_rails_template/install/install_generator.rb', line 212

def write_claude_md
  return if options[:skip_claude]

  if File.exist?(destination_path("CLAUDE.md"))
    say_status :skip,
               "CLAUDE.md already exists; not overwriting. " \
               "Run `/tsykvas-claude` in Claude Code to integrate the gem's " \
               "must-know-rules and routing table inside fence markers without " \
               "touching your existing content.",
               :yellow
    return
  end

  template "CLAUDE.md.tt", "CLAUDE.md"
end