thecore_generators 3.2.0 → 3.8.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 +4 -4
- data/README.md +215 -2
- data/lib/generators/thecore/action_companion.rb +116 -0
- data/lib/generators/thecore/association_wiring.rb +217 -0
- data/lib/generators/thecore/atom_aware.rb +14 -3
- data/lib/generators/thecore/companion_files.rb +142 -0
- data/lib/generators/thecore/member_action/member_action_generator.rb +61 -0
- data/lib/generators/thecore/member_action/templates/action.html.erb.tt +16 -0
- data/lib/generators/thecore/member_action/templates/action.js.tt +44 -0
- data/lib/generators/thecore/member_action/templates/action.rb.tt +25 -0
- data/lib/generators/thecore/member_action/templates/action.scss.tt +38 -0
- data/lib/generators/thecore/migration/migration_generator.rb +18 -7
- data/lib/generators/thecore/model/model_generator.rb +14 -0
- data/lib/generators/thecore/root_action/root_action_generator.rb +80 -0
- data/lib/generators/thecore/root_action/templates/action.html.erb.tt +13 -0
- data/lib/generators/thecore/root_action/templates/action.js.tt +42 -0
- data/lib/generators/thecore/root_action/templates/action.rb.tt +33 -0
- data/lib/generators/thecore/root_action/templates/action.scss.tt +38 -0
- data/lib/generators/thecore/workspace_context.rb +48 -0
- data/lib/tasks/thecore_generators_tasks.rake +33 -0
- data/lib/templates/app_template.rb +220 -0
- data/lib/thecore_generators/check_practices.rb +392 -0
- data/lib/thecore_generators/railtie.rb +4 -0
- data/lib/thecore_generators/version.rb +1 -1
- metadata +17 -1
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "rails/generators"
|
|
3
|
+
require "rails/generators/named_base"
|
|
4
|
+
require "generators/thecore/workspace_context"
|
|
5
|
+
require "generators/thecore/action_companion"
|
|
6
|
+
require "generators/thecore/root_action/root_action_generator"
|
|
7
|
+
require "generators/thecore/member_action/member_action_generator"
|
|
8
|
+
|
|
9
|
+
module Thecore
|
|
10
|
+
# `rails thecore:check_practices` — a Ruby port of thecore_code_extension's
|
|
11
|
+
# checkPractices.js: the Scaffold Files check and the Model concern check
|
|
12
|
+
# (thecore_generators#13), plus the Actions check and `--fix`
|
|
13
|
+
# (thecore_generators#14, per ADR 0004 in the thecore repo).
|
|
14
|
+
#
|
|
15
|
+
# Unlike checkPractices.js, which only ever ran the Scaffold Files check in
|
|
16
|
+
# ATOM context, Runner applies every check uniformly to every context root
|
|
17
|
+
# it scans — the host app root plus every ATOM under vendor/submodules/ by
|
|
18
|
+
# default, or a single named ATOM.
|
|
19
|
+
#
|
|
20
|
+
# The Model check is rescoped per ADR 0001's consequence: since
|
|
21
|
+
# Api::ModelName/RailsAdmin::ModelName concern files are no longer
|
|
22
|
+
# generated by default, their plain absence is not a violation. Only an
|
|
23
|
+
# orphan `include Api::X`/`RailsAdmin::X` (pointing at a missing concern
|
|
24
|
+
# file) or a concern file present but missing its required marker is
|
|
25
|
+
# flagged.
|
|
26
|
+
module CheckPractices
|
|
27
|
+
# One reported issue. `to_h` matches the JSON schema this ticket's
|
|
28
|
+
# acceptance criteria specifies: file, line, message, severity, fixable,
|
|
29
|
+
# code (a stable machine-readable identifier a future consumer, e.g. the
|
|
30
|
+
# VS Code extension, can filter on without depending on `message` text).
|
|
31
|
+
# `fix` (a zero-arg Proc, or nil) is deliberately excluded from `to_h` —
|
|
32
|
+
# it exists only for `--fix` to invoke internally, mirroring
|
|
33
|
+
# checkPractices.js's own `violation.fix.apply(ctx)` pattern; it isn't
|
|
34
|
+
# part of the public JSON contract.
|
|
35
|
+
Violation = Struct.new(:file, :line, :message, :severity, :fixable, :code, :fix, keyword_init: true) do
|
|
36
|
+
def to_h
|
|
37
|
+
{
|
|
38
|
+
"file" => file,
|
|
39
|
+
"line" => line,
|
|
40
|
+
"message" => message,
|
|
41
|
+
"severity" => severity.to_s,
|
|
42
|
+
"fixable" => fixable,
|
|
43
|
+
"code" => code,
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# `fix: true` applies every fixable violation in one pass (no
|
|
49
|
+
# confirmation of its own — whoever passes `fix:` has already decided),
|
|
50
|
+
# then re-scans and returns whatever violations remain, per ADR 0004:
|
|
51
|
+
# "exits non-zero whenever violations remain unresolved after any --fix
|
|
52
|
+
# pass" — this makes a fix that turns out incomplete (or a violation
|
|
53
|
+
# this ticket doesn't know how to fix, e.g. a collection_action
|
|
54
|
+
# companion) visible in the result rather than silently assumed fixed.
|
|
55
|
+
def self.run(app_root:, atom_name: nil, fix: false)
|
|
56
|
+
violations = Runner.new(app_root: app_root, atom_name: atom_name).run
|
|
57
|
+
return violations unless fix
|
|
58
|
+
|
|
59
|
+
violations.each { |v| v.fix&.call if v.fixable }
|
|
60
|
+
Runner.new(app_root: app_root, atom_name: atom_name).run
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# A Thor::Group instance whose only purpose is running
|
|
64
|
+
# Thecore::Generators::CompanionFiles' generic (action-kind-agnostic)
|
|
65
|
+
# after_initialize.rb/locale fixes against a specific destination_root
|
|
66
|
+
# and action name — used by Runner's Actions check for all three kinds,
|
|
67
|
+
# including collection_action, which has no generator of its own to
|
|
68
|
+
# delegate companion-file (view/JS/SCSS) rendering to. Deliberately not
|
|
69
|
+
# placed under lib/generators/ (and so never discovered as a
|
|
70
|
+
# `rails generate` namespace) — it is an internal implementation detail
|
|
71
|
+
# of check_practices' --fix, not a public command.
|
|
72
|
+
class GenericFixTarget < Rails::Generators::NamedBase
|
|
73
|
+
include Thecore::Generators::AtomAware
|
|
74
|
+
include Thecore::Generators::CompanionFiles
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
class Runner
|
|
78
|
+
AFTER_INITIALIZE_MARKER = "Rails.application.configure do"
|
|
79
|
+
ASSETS_MARKER = "Rails.application.config.assets.precompile"
|
|
80
|
+
API_CONCERN_MARKERS = ["extend ActiveSupport::Concern", "cattr_accessor :json_attrs"].freeze
|
|
81
|
+
RAILS_ADMIN_CONCERN_MARKERS = ["extend ActiveSupport::Concern", "rails_admin do"].freeze
|
|
82
|
+
|
|
83
|
+
ACTION_KINDS = %w[root_action member_action collection_action].freeze
|
|
84
|
+
ACTION_FILE_MARKERS = ["RailsAdmin::Config::Actions.add_action", "http_methods"].freeze
|
|
85
|
+
VIEW_MARKERS = ["stylesheet_link_tag", "javascript_include_tag"].freeze
|
|
86
|
+
JS_MARKERS = ["document.addEventListener('turbo:load'"].freeze
|
|
87
|
+
SCSS_MARKERS = ["@keyframes sk-bounce"].freeze
|
|
88
|
+
# Only root_action/member_action have a generator whose own template
|
|
89
|
+
# rendering a companion-file fix can delegate to; collection_action
|
|
90
|
+
# has none (ADR 0004: "no generator to have gotten it right" - a
|
|
91
|
+
# tracked, deliberate gap), so its missing-companion violations are
|
|
92
|
+
# never fixable.
|
|
93
|
+
ACTION_GENERATOR_CLASSES = {
|
|
94
|
+
"root_action" => Thecore::Generators::RootActionGenerator,
|
|
95
|
+
"member_action" => Thecore::Generators::MemberActionGenerator,
|
|
96
|
+
}.freeze
|
|
97
|
+
|
|
98
|
+
def initialize(app_root:, atom_name: nil)
|
|
99
|
+
@app_root = app_root.to_s
|
|
100
|
+
@atom_name = atom_name
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def run
|
|
104
|
+
context_roots.flat_map do |root|
|
|
105
|
+
scaffold_file_violations(root) + model_violations(root) + action_violations(root)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
# Delegates entirely to WorkspaceContext.atom_dir_for for the
|
|
112
|
+
# `--atom=NAME` case, the same resolution+error-message logic
|
|
113
|
+
# AtomAware uses for the Model/Migration/Root Action generators'
|
|
114
|
+
# `--atom=NAME` - rather than re-deriving it here, which would leave
|
|
115
|
+
# two copies of the same "no ATOM named X" message to keep in sync.
|
|
116
|
+
# It raises Thor::Error (not a CheckPractices-specific class) on an
|
|
117
|
+
# unknown name; the rake task rescues that directly.
|
|
118
|
+
def context_roots
|
|
119
|
+
if @atom_name
|
|
120
|
+
[Thecore::Generators::WorkspaceContext.atom_dir_for(cwd: nil, app_root: @app_root, atom_name: @atom_name)]
|
|
121
|
+
else
|
|
122
|
+
[@app_root, *Thecore::Generators::WorkspaceContext.all_atom_dirs(@app_root)]
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def scaffold_file_violations(root)
|
|
127
|
+
check_scaffold_file(
|
|
128
|
+
File.join(root, "config", "initializers", "after_initialize.rb"),
|
|
129
|
+
AFTER_INITIALIZE_MARKER, "after_initialize.rb", "after_initialize"
|
|
130
|
+
) + check_scaffold_file(
|
|
131
|
+
File.join(root, "config", "initializers", "assets.rb"),
|
|
132
|
+
ASSETS_MARKER, "assets.rb", "assets"
|
|
133
|
+
)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def check_scaffold_file(path, marker, label, code_prefix)
|
|
137
|
+
unless File.exist?(path)
|
|
138
|
+
return [Violation.new(
|
|
139
|
+
file: path, line: 0, message: "Missing Scaffold File: #{label}",
|
|
140
|
+
severity: :error, fixable: false, code: "missing_#{code_prefix}"
|
|
141
|
+
)]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
return [] if File.read(path).include?(marker)
|
|
145
|
+
|
|
146
|
+
[Violation.new(
|
|
147
|
+
file: path, line: 0, message: "#{label} is missing the `#{marker}` marker",
|
|
148
|
+
severity: :error, fixable: false, code: "missing_#{code_prefix}_marker"
|
|
149
|
+
)]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def model_violations(root)
|
|
153
|
+
model_dir = File.join(root, "app", "models")
|
|
154
|
+
return [] unless File.directory?(model_dir)
|
|
155
|
+
|
|
156
|
+
Dir.children(model_dir).select { |f| f.end_with?(".rb") }.sort.flat_map do |file|
|
|
157
|
+
check_model(root, file)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def check_model(root, file)
|
|
162
|
+
model_name = File.basename(file, ".rb").camelize
|
|
163
|
+
model_content = File.read(File.join(root, "app", "models", file))
|
|
164
|
+
|
|
165
|
+
check_concern(root, file, model_name, model_content, type: "api", module_prefix: "Api", markers: API_CONCERN_MARKERS) +
|
|
166
|
+
check_concern(root, file, model_name, model_content, type: "rails_admin", module_prefix: "RailsAdmin", markers: RAILS_ADMIN_CONCERN_MARKERS)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def check_concern(root, file, model_name, model_content, type:, module_prefix:, markers:)
|
|
170
|
+
# A plain #include? substring check would false-positive: "include
|
|
171
|
+
# Api::Foo" is itself a substring of "include Api::FooBar" (no
|
|
172
|
+
# separator between them), so a model happening to include a
|
|
173
|
+
# differently-named, longer concern would wrongly look like it
|
|
174
|
+
# includes this one too. The negative lookahead requires whatever
|
|
175
|
+
# follows the constant name to not itself be a constant-name
|
|
176
|
+
# character.
|
|
177
|
+
return [] unless model_content.match?(/include #{Regexp.escape("#{module_prefix}::#{model_name}")}(?![A-Za-z0-9_])/)
|
|
178
|
+
|
|
179
|
+
concern_path = File.join(root, "app", "models", "concerns", type, file)
|
|
180
|
+
unless File.exist?(concern_path)
|
|
181
|
+
return [Violation.new(
|
|
182
|
+
file: concern_path, line: 0,
|
|
183
|
+
message: "#{model_name}: includes #{module_prefix}::#{model_name} but its concern file is missing",
|
|
184
|
+
severity: :error, fixable: false, code: "orphan_#{type}_include"
|
|
185
|
+
)]
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
concern_content = File.read(concern_path)
|
|
189
|
+
markers.reject { |marker| concern_content.include?(marker) }.map do |marker|
|
|
190
|
+
Violation.new(
|
|
191
|
+
file: concern_path, line: 0,
|
|
192
|
+
message: "#{model_name}: #{type} concern missing '#{marker}' marker",
|
|
193
|
+
severity: :error, fixable: false, code: "#{type}_concern_missing_marker"
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# --- Actions check (thecore_generators#14) ---
|
|
199
|
+
|
|
200
|
+
def action_violations(root)
|
|
201
|
+
base_dir = root == @app_root ? "config" : "lib"
|
|
202
|
+
|
|
203
|
+
ACTION_KINDS.flat_map { |kind| action_kind_violations(root, base_dir, kind) }
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def action_kind_violations(root, base_dir, kind)
|
|
207
|
+
dir = File.join(root, base_dir, "#{kind}s")
|
|
208
|
+
return [] unless File.directory?(dir)
|
|
209
|
+
|
|
210
|
+
Dir.children(dir).select { |f| f.end_with?(".rb") }.sort.flat_map do |file|
|
|
211
|
+
check_action(root, base_dir, kind, File.basename(file, ".rb"))
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def check_action(root, base_dir, kind, action_name)
|
|
216
|
+
action_path = File.join(root, base_dir, "#{kind}s", "#{action_name}.rb")
|
|
217
|
+
content = File.read(action_path)
|
|
218
|
+
|
|
219
|
+
ACTION_FILE_MARKERS.reject { |m| content.include?(m) }.map { |m|
|
|
220
|
+
Violation.new(
|
|
221
|
+
file: action_path, line: 0, message: "#{action_name}: missing '#{m}' marker",
|
|
222
|
+
severity: :error, fixable: false, code: "action_file_missing_marker"
|
|
223
|
+
)
|
|
224
|
+
} + check_companion(root, kind, action_name, template: "action.html.erb.tt",
|
|
225
|
+
rel_path: "app/views/rails_admin/main/#{action_name}.html.erb",
|
|
226
|
+
markers: VIEW_MARKERS, code: "companion_view") +
|
|
227
|
+
check_companion(root, kind, action_name, template: "action.js.tt",
|
|
228
|
+
rel_path: "app/assets/javascripts/rails_admin/actions/#{action_name}.js",
|
|
229
|
+
markers: JS_MARKERS, code: "companion_js") +
|
|
230
|
+
check_companion(root, kind, action_name, template: "action.scss.tt",
|
|
231
|
+
rel_path: "app/assets/stylesheets/rails_admin/actions/#{action_name}.scss",
|
|
232
|
+
markers: SCSS_MARKERS, code: "companion_scss") +
|
|
233
|
+
check_action_require_line(root, base_dir, kind, action_name) +
|
|
234
|
+
check_action_locale_entries(root, action_name)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Renders only the one missing companion file, via the actual
|
|
238
|
+
# Root/Member Action generator's own template - never the bundled
|
|
239
|
+
# "render all three companions" method, which would risk a
|
|
240
|
+
# non-interactive file-collision hang/prompt on a hand-customized
|
|
241
|
+
# sibling file that already exists with different content. A file
|
|
242
|
+
# that exists but lost a marker is never fixable here, same as the
|
|
243
|
+
# Model check above: regenerating over it could clobber real
|
|
244
|
+
# customization; a human has to look at it.
|
|
245
|
+
#
|
|
246
|
+
# The fix re-checks `File.exist?(full_path)` at call time, not just at
|
|
247
|
+
# scan time: the same companion rel_path is kind-agnostic (e.g.
|
|
248
|
+
# app/views/rails_admin/main/<name>.html.erb), so a root_action and a
|
|
249
|
+
# member_action sharing the same action name produce two independent
|
|
250
|
+
# violations against the identical file. Without the re-check, the
|
|
251
|
+
# second violation's fix would call `template` against a file the
|
|
252
|
+
# first violation's fix just created, tripping Thor's interactive
|
|
253
|
+
# file-collision prompt during a non-interactive `--fix` run - the
|
|
254
|
+
# exact hazard this design otherwise avoids by never calling the
|
|
255
|
+
# bundled render-all-three method.
|
|
256
|
+
def check_companion(root, kind, action_name, template:, rel_path:, markers:, code:)
|
|
257
|
+
full_path = File.join(root, rel_path)
|
|
258
|
+
|
|
259
|
+
unless File.exist?(full_path)
|
|
260
|
+
generator_class = ACTION_GENERATOR_CLASSES[kind]
|
|
261
|
+
fix = generator_class && -> {
|
|
262
|
+
next if File.exist?(full_path)
|
|
263
|
+
|
|
264
|
+
build_action_generator(generator_class, action_name, root).send(:template, template, rel_path)
|
|
265
|
+
}
|
|
266
|
+
return [Violation.new(
|
|
267
|
+
file: full_path, line: 0, message: "#{action_name}: missing companion #{File.basename(rel_path)}",
|
|
268
|
+
severity: :error, fixable: !fix.nil?, code: "missing_#{code}", fix: fix
|
|
269
|
+
)]
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
content = File.read(full_path)
|
|
273
|
+
markers.reject { |m| content.include?(m) }.map do |m|
|
|
274
|
+
Violation.new(
|
|
275
|
+
file: full_path, line: 0, message: "#{action_name}: #{code.tr("_", " ")} missing '#{m}' marker",
|
|
276
|
+
severity: :error, fixable: false, code: "#{code}_missing_marker"
|
|
277
|
+
)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Unlike the companion-file check above, this one applies to all
|
|
282
|
+
# three action kinds (including collection_action) - inserting a
|
|
283
|
+
# require line doesn't need a kind-specific template, just
|
|
284
|
+
# CompanionFiles' generic, already-idempotent ensure_*! helper via
|
|
285
|
+
# GenericFixTarget.
|
|
286
|
+
def check_action_require_line(root, base_dir, kind, action_name)
|
|
287
|
+
after_init_path = File.join(root, "config", "initializers", "after_initialize.rb")
|
|
288
|
+
# A missing/markerless after_initialize.rb is already reported by
|
|
289
|
+
# the Scaffold Files check - don't double-report it here, matching
|
|
290
|
+
# checkActionFile's own `if (fs.existsSync(afterInitPath))` guard.
|
|
291
|
+
return [] unless File.exist?(after_init_path)
|
|
292
|
+
|
|
293
|
+
require_line = Thecore::Generators::ActionCompanion.require_line_for(
|
|
294
|
+
kind: kind, in_atom: base_dir == "lib", name: action_name
|
|
295
|
+
)
|
|
296
|
+
return [] if File.read(after_init_path).include?(require_line)
|
|
297
|
+
|
|
298
|
+
fix = -> { fix_target(action_name, root).send(:ensure_after_initialize_require!, require_line) }
|
|
299
|
+
[Violation.new(
|
|
300
|
+
file: after_init_path, line: 0,
|
|
301
|
+
message: "#{action_name}: missing require line in after_initialize.rb",
|
|
302
|
+
severity: :error, fixable: true, code: "missing_action_require_line", fix: fix
|
|
303
|
+
)]
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def check_action_locale_entries(root, action_name)
|
|
307
|
+
locales_dir = File.join(root, "config", "locales")
|
|
308
|
+
return [] unless Dir.exist?(locales_dir)
|
|
309
|
+
|
|
310
|
+
Dir.children(locales_dir).select { |f| f.end_with?(".yml") }.sort.flat_map do |file|
|
|
311
|
+
check_single_locale_entry(root, locales_dir, file, action_name)
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def check_single_locale_entry(root, locales_dir, file, action_name)
|
|
316
|
+
path = File.join(locales_dir, file)
|
|
317
|
+
data = YAML.load_file(path) || {}
|
|
318
|
+
lang = data.size == 1 ? data.keys.first : File.basename(file, ".yml")
|
|
319
|
+
return [] if data.dig(lang, "admin", "actions", action_name)
|
|
320
|
+
|
|
321
|
+
title = action_name.split("_").map(&:capitalize).join(" ")
|
|
322
|
+
# write_action_locale_entries! writes into every *.yml file already
|
|
323
|
+
# present, not just this one - idempotent, so a second violation
|
|
324
|
+
# for the same action_name against another missing locale file
|
|
325
|
+
# simply re-applies the same already-correct end state.
|
|
326
|
+
fix = -> { fix_target(action_name, root).send(:write_action_locale_entries!, action_name, title) }
|
|
327
|
+
[Violation.new(
|
|
328
|
+
file: path, line: 0, message: "#{action_name}: missing locale entry in #{file}",
|
|
329
|
+
severity: :warning, fixable: true, code: "missing_action_locale_entry", fix: fix
|
|
330
|
+
)]
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# `--atom=NAME` is always resolved explicitly here (never left to
|
|
334
|
+
# cwd-based detection) so a fix always lands in the exact `root` this
|
|
335
|
+
# violation was found under, regardless of the check_practices
|
|
336
|
+
# process's own invocation cwd.
|
|
337
|
+
#
|
|
338
|
+
# For a host-app violation (`root == @app_root`), `atom_name` is nil -
|
|
339
|
+
# but `AtomAware#atom_dir` treats a nil/blank `--atom` as "no override,
|
|
340
|
+
# fall back to cwd-based detection"
|
|
341
|
+
# (`Thecore::Generators::WorkspaceContext.atom_dir_for`), not as "force
|
|
342
|
+
# host-app placement". If the check_practices process's own `Dir.pwd`
|
|
343
|
+
# happens to sit inside a `vendor/submodules/<atom>/` tree at the
|
|
344
|
+
# moment `--fix` runs (e.g. invoked via an explicit `bin/rails` path
|
|
345
|
+
# rather than the `rails` executable's own directory walk-up - see
|
|
346
|
+
# WorkspaceContext's `Dir.pwd`-reset gotcha in this gem's CLAUDE.md),
|
|
347
|
+
# `AtomAware#initialize` would silently redirect the freshly-built
|
|
348
|
+
# generator's `destination_root` into that unrelated ATOM instead of
|
|
349
|
+
# `@app_root`, even though this violation was found in host-app
|
|
350
|
+
# context. Explicitly re-asserting `destination_root = root` after
|
|
351
|
+
# construction closes that gap without touching `WorkspaceContext`/
|
|
352
|
+
# `AtomAware` themselves (shared by Model/Migration/Root/Member
|
|
353
|
+
# generators) - it simply overrides whatever cwd-based guess
|
|
354
|
+
# `AtomAware#initialize` made with the exact root this violation was
|
|
355
|
+
# actually found under.
|
|
356
|
+
def build_action_generator(generator_class, action_name, root)
|
|
357
|
+
atom_name = root == @app_root ? nil : File.basename(root)
|
|
358
|
+
generator = generator_class.new([action_name], { atom: atom_name }, destination_root: @app_root)
|
|
359
|
+
generator.destination_root = root
|
|
360
|
+
generator
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def fix_target(action_name, root)
|
|
364
|
+
build_action_generator(GenericFixTarget, action_name, root)
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
module Reporter
|
|
369
|
+
module_function
|
|
370
|
+
|
|
371
|
+
def text(violations)
|
|
372
|
+
return "✅ No violations found.\n" if violations.empty?
|
|
373
|
+
|
|
374
|
+
violations
|
|
375
|
+
.group_by(&:file)
|
|
376
|
+
.sort
|
|
377
|
+
.map { |file, file_violations| text_for_file(file, file_violations) }
|
|
378
|
+
.join("\n\n") + "\n"
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def text_for_file(file, violations)
|
|
382
|
+
lines = violations.map { |v| " [#{v.severity.to_s.upcase}] #{v.message}" }
|
|
383
|
+
"#{file}:\n#{lines.join("\n")}"
|
|
384
|
+
end
|
|
385
|
+
private_class_method :text_for_file
|
|
386
|
+
|
|
387
|
+
def json(violations)
|
|
388
|
+
JSON.generate({ "violations" => violations.map(&:to_h) })
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|
|
@@ -21,5 +21,9 @@ module ThecoreGenerators
|
|
|
21
21
|
# `--help`, it never blocks direct namespace invocation.
|
|
22
22
|
class Railtie < ::Rails::Railtie
|
|
23
23
|
config.app_generators.orm :thecore, migration: true, timestamps: true
|
|
24
|
+
|
|
25
|
+
rake_tasks do
|
|
26
|
+
load File.expand_path("../tasks/thecore_generators_tasks.rake", __dir__)
|
|
27
|
+
end
|
|
24
28
|
end
|
|
25
29
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: thecore_generators
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gabriele Tassoni
|
|
@@ -36,13 +36,29 @@ files:
|
|
|
36
36
|
- MIT-LICENSE
|
|
37
37
|
- README.md
|
|
38
38
|
- Rakefile
|
|
39
|
+
- lib/generators/thecore/action_companion.rb
|
|
40
|
+
- lib/generators/thecore/association_wiring.rb
|
|
39
41
|
- lib/generators/thecore/atom_aware.rb
|
|
42
|
+
- lib/generators/thecore/companion_files.rb
|
|
43
|
+
- lib/generators/thecore/member_action/member_action_generator.rb
|
|
44
|
+
- lib/generators/thecore/member_action/templates/action.html.erb.tt
|
|
45
|
+
- lib/generators/thecore/member_action/templates/action.js.tt
|
|
46
|
+
- lib/generators/thecore/member_action/templates/action.rb.tt
|
|
47
|
+
- lib/generators/thecore/member_action/templates/action.scss.tt
|
|
40
48
|
- lib/generators/thecore/migration/migration_generator.rb
|
|
41
49
|
- lib/generators/thecore/model/model_generator.rb
|
|
42
50
|
- lib/generators/thecore/model/templates/api_concern.rb.tt
|
|
43
51
|
- lib/generators/thecore/model/templates/rails_admin_concern.rb.tt
|
|
52
|
+
- lib/generators/thecore/root_action/root_action_generator.rb
|
|
53
|
+
- lib/generators/thecore/root_action/templates/action.html.erb.tt
|
|
54
|
+
- lib/generators/thecore/root_action/templates/action.js.tt
|
|
55
|
+
- lib/generators/thecore/root_action/templates/action.rb.tt
|
|
56
|
+
- lib/generators/thecore/root_action/templates/action.scss.tt
|
|
44
57
|
- lib/generators/thecore/workspace_context.rb
|
|
58
|
+
- lib/tasks/thecore_generators_tasks.rake
|
|
59
|
+
- lib/templates/app_template.rb
|
|
45
60
|
- lib/thecore_generators.rb
|
|
61
|
+
- lib/thecore_generators/check_practices.rb
|
|
46
62
|
- lib/thecore_generators/railtie.rb
|
|
47
63
|
- lib/thecore_generators/version.rb
|
|
48
64
|
homepage: https://github.com/gabrieletassoni/thecore_generators
|