Class: Hibiki::Rails::Generators::ScaffoldSchema
- Inherits:
-
Object
- Object
- Hibiki::Rails::Generators::ScaffoldSchema
- Defined in:
- lib/generators/hibiki/rails/scaffold_schema.rb
Overview
The column list a scaffold generator hands its templates, plus every list derived from it.
Two builders, because a scaffold has two genuinely different sources of truth and neither covers both commands:
from_attributes `hibiki:rails:scaffold Book title:string` — the
migration has NOT run when the controller generator
is invoked, so the schema does not exist yet.
from_model `hibiki:rails:scaffold_controller Book` with no
field list — the only source is the real schema.
Explicit attributes always win, matching Rails' own precedence.
But ORDER and FACTS are separate questions, and only order has to come
from the arguments. from_attributes takes a model: for the case
where both sources exist — scaffold_controller Book title:string ...
against a migrated app, which is exactly what the field-order notice
recommends — and reads every fact from the schema while the argument
list decides the sequence. Without it, taking that advice silently cost
the validators behind live_errors, a number field's min/max, and the
label column a belongs_to's select displays.
Constant Summary collapse
- TIMESTAMPS =
%w[created_at updated_at].freeze
- REFUSALS =
Attribute shapes that cannot be a ReactiveForm signal, a Data member or a collection_select. They are refused out loud rather than emitted half-working: a silently dropped column is the §10 failure class, where everything looks generated and one field is quietly missing.
{ rich_text?: "rich text needs its own editor and cannot be a form signal", attachment?: "Active Storage attachments are not form signals", attachments?: "Active Storage attachments are not form signals", password_digest?: "a password digest must not travel over a channel", token?: "a token must not travel over a channel", polymorphic?: "a polymorphic belongs_to has no single collection to select from" }.freeze
Instance Attribute Summary collapse
-
#columns ⇒ Object
readonly
Returns the value of attribute columns.
-
#skipped ⇒ Object
readonly
Returns the value of attribute skipped.
-
#unmatched ⇒ Object
readonly
Returns the value of attribute unmatched.
Class Method Summary collapse
-
.from_attributes(attributes, timestamps: true, model: nil) ⇒ Object
modelis nil whenever there is nothing to read: no constant, or no table yet — which is EVERYhibiki:rails:scaffoldrun, since it invokes the controller generator before its own migration. - .from_model(model) ⇒ Object
- .refusal_for(attr) ⇒ Object
Instance Method Summary collapse
- #attribute_names ⇒ Object
- #belongs_tos ⇒ Object
- #booleans ⇒ Object
- #empty? ⇒ Boolean
- #filterable ⇒ Object
-
#initialize(columns:, skipped: [], unmatched: [], introspection: nil, timestamps: true, introspected: false, schema_ordered: false) ⇒ ScaffoldSchema
constructor
A new instance of ScaffoldSchema.
-
#introspected? ⇒ Boolean
Two independent facts, and the merge is the case that separates them:
introspected?says the FACTS came from a live model, whileschema_ordered?says the schema also decided the ORDER — which is the only thing the field-order notice has anything to say about. - #live_errors ⇒ Object
-
#primary_boolean ⇒ Object
One grouped COUNT covers both numbers, so the counts sentence gets a boolean breakdown for free.
- #schema_ordered? ⇒ Boolean
- #searchable ⇒ Object
- #searchable? ⇒ Boolean
-
#sortable ⇒ Object
:id first because it is the default sort and always exists; :created_at last because "newest" is the sort a reader reaches for that no declared column provides.
- #timestamps? ⇒ Boolean
-
#unmirrored_uniqueness ⇒ Object
§5.15: unique indexes with no validator mirroring them, as column-name lists.
Constructor Details
#initialize(columns:, skipped: [], unmatched: [], introspection: nil, timestamps: true, introspected: false, schema_ordered: false) ⇒ ScaffoldSchema
Returns a new instance of ScaffoldSchema.
479 480 481 482 483 484 485 486 487 488 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 479 def initialize(columns:, skipped: [], unmatched: [], introspection: nil, timestamps: true, introspected: false, schema_ordered: false) @columns = columns @skipped = skipped @unmatched = unmatched @introspection = introspection @timestamps = @introspected = introspected @schema_ordered = schema_ordered end |
Instance Attribute Details
#columns ⇒ Object (readonly)
Returns the value of attribute columns.
406 407 408 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 406 def columns @columns end |
#skipped ⇒ Object (readonly)
Returns the value of attribute skipped.
406 407 408 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 406 def skipped @skipped end |
#unmatched ⇒ Object (readonly)
Returns the value of attribute unmatched.
406 407 408 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 406 def unmatched @unmatched end |
Class Method Details
.from_attributes(attributes, timestamps: true, model: nil) ⇒ Object
model is nil whenever there is nothing to read: no constant, or
no table yet — which is EVERY hibiki:rails:scaffold run, since it
invokes the controller generator before its own migration. That
case is this method's original behaviour, unchanged.
415 416 417 418 419 420 421 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 415 def from_attributes(attributes, timestamps: true, model: nil) introspection = ModelIntrospection.new(model) if model supported, refused = attributes.partition { refusal_for(it).nil? } return from_arguments_only(supported, refused, ) unless introspection merge(supported, refused, introspection) end |
.from_model(model) ⇒ Object
423 424 425 426 427 428 429 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 423 def from_model(model) introspection = ModelIntrospection.new(model) new(columns: introspection.columns, skipped: introspection.skipped, introspection: introspection, timestamps: introspection., introspected: true, schema_ordered: true) end |
.refusal_for(attr) ⇒ Object
409 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 409 def refusal_for(attr) = REFUSALS.find { |query, _| attr.public_send(query) }&.last |
Instance Method Details
#attribute_names ⇒ Object
503 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 503 def attribute_names = columns.map(&:name) |
#belongs_tos ⇒ Object
501 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 501 def belongs_tos = columns.select(&:belongs_to?) |
#booleans ⇒ Object
502 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 502 def booleans = columns.select(&:filterable?) |
#empty? ⇒ Boolean
497 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 497 def empty? = columns.empty? |
#filterable ⇒ Object
500 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 500 def filterable = columns.select(&:filterable?).map(&:name) |
#introspected? ⇒ Boolean
Two independent facts, and the merge is the case that separates them:
introspected? says the FACTS came from a live model, while
schema_ordered? says the schema also decided the ORDER — which is
the only thing the field-order notice has anything to say about.
494 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 494 def introspected? = @introspected |
#live_errors ⇒ Object
520 521 522 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 520 def live_errors columns.filter_map { |column| [column.name, column.live_error_clause] if column.live_error_clause } end |
#primary_boolean ⇒ Object
One grouped COUNT covers both numbers, so the counts sentence gets a boolean breakdown for free. With no boolean column there is nothing to group by and the sentence loses its trailing clause.
518 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 518 def primary_boolean = booleans.first |
#schema_ordered? ⇒ Boolean
495 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 495 def schema_ordered? = @schema_ordered |
#searchable ⇒ Object
499 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 499 def searchable = columns.select(&:searchable?).map(&:name) |
#searchable? ⇒ Boolean
504 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 504 def searchable? = searchable.any? |
#sortable ⇒ Object
:id first because it is the default sort and always exists; :created_at last because "newest" is the sort a reader reaches for that no declared column provides. :updated_at is deliberately absent — it moves on every write, so ordering by it makes the list reshuffle under the reader.
511 512 513 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 511 def sortable [:id, *columns.select(&:sortable?).map(&:name), *(:created_at if )] end |
#timestamps? ⇒ Boolean
496 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 496 def = @timestamps |
#unmirrored_uniqueness ⇒ Object
§5.15: unique indexes with no validator mirroring them, as column-name
lists. The model is authoritative wherever there is one — it knows
composite indexes, which no argument list can express — and
title:string:uniq is the fallback for the path with no model to ask.
Read off the attribute rather than the column for the same reason
refusal_for is: it is a question about what was TYPED.
530 531 532 533 |
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 530 def unmirrored_uniqueness @unmirrored_uniqueness ||= @introspection&.unmirrored_unique_indexes(columns.map(&:name)) || columns.select { it.attr.has_uniq_index? }.map { [it.name] } end |