Class: LcpRuby::Generators::EntityGenerator
- Inherits:
-
Rails::Generators::NamedBase
- Object
- Rails::Generators::NamedBase
- LcpRuby::Generators::EntityGenerator
- Includes:
- FormatSupport, Prerequisites
- Defined in:
- lib/generators/lcp_ruby/entity_generator.rb
Constant Summary collapse
- SYSTEM_FIELDS =
%i[id created_at updated_at created_by_id updated_by_id].freeze
- SYNTHETIC_FIELDS =
Feature flags that require a backing column declared in ‘fields:` for ConfigurationValidator to accept the configuration. Each entry: [flag, field]. See `inject_synthetic_fields!` for the rationale.
[ [ :positioning, :position ], [ :tree, :parent_id ] ].freeze
Constants included from FormatSupport
Instance Method Summary collapse
-
#auto_add_reverse_associations ⇒ Object
Must run before post_validate so the validator does not see the “no corresponding has_many” warnings that auto-add just resolved.
- #create_model ⇒ Object
- #create_permissions ⇒ Object
- #create_presenter ⇒ Object
- #create_view_group ⇒ Object
-
#inject_synthetic_fields! ⇒ Object
Some feature flags require a backing column declared in ‘fields:` for ConfigurationValidator to accept the configuration: –positioning → `position` (acts_as_list expects an explicit field; without it: “positioning field ’position’ is not defined”).
-
#lcp_dynamic_required_features ⇒ Object
Hook consumed by the ‘Prerequisites` concern’s ‘check_lcp_prerequisites!` Thor task.
- #parse_fields ⇒ Object
-
#plan_menu_entry ⇒ Object
Preflight for –menu.
- #post_validate ⇒ Object
- #preflight ⇒ Object
- #show_post_install_message ⇒ Object
-
#validate_menu_icon_flag ⇒ Object
Standalone check: –menu-icon without –menu is an error even when –menu is not provided (avoids silent no-op).
-
#write_menu_entry ⇒ Object
Writes the planned ‘- view_group: <name>` entry into menu.yml.
Methods included from FormatSupport
Methods included from Prerequisites
included, #lcp_format_missing_prereqs
Instance Method Details
#auto_add_reverse_associations ⇒ Object
Must run before post_validate so the validator does not see the “no corresponding has_many” warnings that auto-add just resolved.
236 237 238 239 240 241 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 236 def auto_add_reverse_associations @inverse_suggestions = compute_inverse_suggestions return if @inverse_suggestions.empty? return unless [:auto_add_inverses] process_inverse_suggestions! end |
#create_model ⇒ Object
187 188 189 190 191 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 187 def create_model copy_dsl_or_yaml "model.rb", dsl_target: "config/lcp_ruby/models/#{singular_name}.rb", yaml_target: "config/lcp_ruby/models/#{singular_name}.yml" end |
#create_permissions ⇒ Object
199 200 201 202 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 199 def @roles = Entity::RoleDiscovery.call(destination_root) { |w| @warnings << w } template "permissions.yml", "config/lcp_ruby/permissions/#{singular_name}.yml" end |
#create_presenter ⇒ Object
193 194 195 196 197 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 193 def create_presenter copy_dsl_or_yaml "presenter.rb", dsl_target: "config/lcp_ruby/presenters/#{plural_name}.rb", yaml_target: "config/lcp_ruby/presenters/#{plural_name}.yml" end |
#create_view_group ⇒ Object
204 205 206 207 208 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 204 def create_view_group copy_dsl_or_yaml "view_group.rb", dsl_target: "config/lcp_ruby/views/#{singular_name}.rb", yaml_target: "config/lcp_ruby/views/#{singular_name}.yml" end |
#inject_synthetic_fields! ⇒ Object
Some feature flags require a backing column declared in ‘fields:` for ConfigurationValidator to accept the configuration:
--positioning → `position` (acts_as_list expects an explicit field;
without it: "positioning field 'position' is not defined").
--tree → `parent_id` (validator
`configuration_validator.rb:1228` requires
"tree parent_field 'parent_id' must be declared in fields";
the showcase Category model declares it for the same reason).
Synthetic descriptors are marked ‘:hidden` so they do not appear in the generated index/show/form sections — these are platform-managed columns, not editable user content. Skipped if the user already declared the field.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 96 def inject_synthetic_fields! SYNTHETIC_FIELDS.each do |flag, field_name| next unless [flag] next if @descriptors.any? { |d| d.name == field_name } # `synthetic: true` marks the descriptor as generator-injected (not # user-declared) so check_reserved_names! skips macro-clash checks # on it — the synthetic positioning :position field IS the macro's # column, not a separate user field that conflicts with it. @descriptors << Entity::FieldDescriptor.new( name: field_name, type: :integer, enum_values: nil, modifiers: { hidden: true, synthetic: true }, options: {} ) end end |
#lcp_dynamic_required_features ⇒ Object
Hook consumed by the ‘Prerequisites` concern’s ‘check_lcp_prerequisites!` Thor task. Returns features required by option-driven flags (–auditing → audit_log, –custom-fields → custom_field_definition). Static prereqs would go via `requires_features` at class level; entity has none.
132 133 134 135 136 137 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 132 def lcp_dynamic_required_features out = [] out << "auditing" if [:auditing] out << "custom_fields" if [:custom_fields] out end |
#parse_fields ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 71 def parse_fields @warnings = [] # Phase 2 of type-system defaults: the generator now consults the # type registry for null_false_validation, default_index_visible, # renderer, and reserved_clashes. The registry is normally populated # at engine boot (engine.rb:246), but the generator runs from the # `rails generate` command without a full engine boot — so we # register built-ins lazily here. Idempotent (register_all! overwrites # by name). Types::BuiltInTypes.register_all! @descriptors = Entity::FieldTokenParser.parse(fields) inject_synthetic_fields! end |
#plan_menu_entry ⇒ Object
Preflight for –menu. Runs BEFORE any file write so a missing marker / malformed flag / position-on-dropdown error aborts without leaving half-generated model/presenter/permissions on disk (D7 §“Open Question 2: –menu errors in middle of multi-step generation”). Stashes the plan in @menu_write_plan for write_menu_entry to consume.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 144 def @menu_write_plan = nil return unless [:menu] = File.join(destination_root, "config/lcp_ruby/menu.yml") unless File.exist?() raise Thor::Error, "menu.yml at config/lcp_ruby/menu.yml is missing. " \ "Run `rails generate lcp_ruby:install` first or add the file manually." end @menu_yml_lines = File.readlines() scan_result = LcpRuby::Generators::EntityMenuWriter.scan(@menu_yml_lines) if scan_result[:markers].empty? raise Thor::Error, "menu.yml has no `# lcp:menu <section>` insertion markers. " \ "Re-run `rails generate lcp_ruby:install` (which seeds markers via " \ "--menu-layout=top|sidebar|both) or add a marker manually." end flag = LcpRuby::Generators::EntityMenuWriter.parse_flag( [:menu], available_sections: scan_result[:sections_present] ) @menu_write_plan = LcpRuby::Generators::EntityMenuWriter.plan_insert( scan_result, section: flag[:section], parent: flag[:parent], view_group: singular_name, icon: [:menu_icon], position: flag[:position] ) end |
#post_validate ⇒ Object
243 244 245 246 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 243 def post_validate return unless [:validate] run_configuration_validator! end |
#preflight ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 111 def preflight validate_format raise Thor::Error, "At least one field token is required." if @descriptors.empty? check_reserved_names! check_field_types! check_label_method! check_belongs_to_targets! check_polymorphic_conflicts! check_default_values! check_renderer_overrides! validate_searchable_fields! validate_index_fields! validate_display_template! validate_default_sort! end |
#show_post_install_message ⇒ Object
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 248 def say "" say "LCP entity '#{singular_name}' generated.", :green say "" say "Generated files:" say " - config/lcp_ruby/models/#{singular_name}.#{format_extension}" say " - config/lcp_ruby/presenters/#{plural_name}.#{format_extension}" say " - config/lcp_ruby/permissions/#{singular_name}.yml" say " - config/lcp_ruby/views/#{singular_name}.#{format_extension}" if @warnings.any? say "" @warnings.each { |w| say_status :warn, w, :yellow } end say "" show_reverse_association_hints show_polymorphic_hint say "Next steps:" next_steps.each_with_index do |step, i| say " #{i + 1}. #{step}" end say "" say "Tip: in bash, single-quote any field token whose braces contain a comma " \ "(e.g. `'customer:belongs_to{required,link}'`) — bash brace-expands `{a,b}` patterns." say "" end |
#validate_menu_icon_flag ⇒ Object
Standalone check: –menu-icon without –menu is an error even when –menu is not provided (avoids silent no-op).
182 183 184 185 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 182 def return unless [:menu_icon] && ![:menu] raise Thor::Error, "--menu-icon requires --menu; icons are only meaningful with explicit placement." end |
#write_menu_entry ⇒ Object
Writes the planned ‘- view_group: <name>` entry into menu.yml. Plan was computed in plan_menu_entry (preflight); we only need to splice and re-validate here. Backup is overwriteable single-shot so configurator can roll back the menu mutation with a single `mv` without rolling back the other generator outputs (which stay valid independently).
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/generators/lcp_ruby/entity_generator.rb', line 215 def return if @menu_write_plan.nil? if @menu_write_plan[:action] == :skip say_status :skip, @menu_write_plan[:message] return end = File.join(destination_root, "config/lcp_ruby/menu.yml") backup_path = File.join(destination_root, "config/lcp_ruby/.menu.yml.bak") FileUtils.cp(, backup_path) LcpRuby::Generators::EntityMenuWriter.apply!(, @menu_write_plan, cached_lines: @menu_yml_lines) say_status :update, "config/lcp_ruby/menu.yml" (, backup_path) end |