Class: Hibiki::Rails::Generators::ScaffoldColumn

Inherits:
Object
  • Object
show all
Defined in:
lib/generators/hibiki/rails/scaffold_schema.rb

Overview

One column of a scaffolded resource.

Both sources wrap a ::Rails::Generators::GeneratedAttribute, even when the facts came from a real schema, because railties' own per-version knowledge rides on it — #field_type, requiredness, index handling — and tracking that by hand is how a generated _form stops matching the Rails it runs on. Synthesizing the attribute on the introspection path buys that for both paths at once.

Constant Summary collapse

TEXTUAL =

Types that can carry a LIKE term.

%i[string text].freeze
COMPARABLE =

Types a sort control can order by. Text is excluded deliberately: an unbounded blob is a meaningless sort key, and §11.6 wants an index per sortable column. Boolean is excluded because two values is a filter, not an order.

%i[string integer decimal float date datetime time].freeze
TAG_HELPERS =

Which *_tag helper renders the control in the channel-backed form. These are the OLD names: Rails 8.1 renamed them to #textarea_tag / #checkbox_tag and kept these as aliases. Originally that was the only spelling valid across a matrix that included 7.1/7.2; with the floor now at 8.0 both spellings work, and these stay because changing them would rewrite generated output — a byte-diff against every reference and test app — to buy nothing. Revisit if Rails ever drops the aliases; that is the one thing that would make it worth the churn.

{
  string: :text_field_tag, text: :text_area_tag,
  integer: :number_field_tag, decimal: :text_field_tag,
  float: :text_field_tag, date: :date_field_tag,
  datetime: :datetime_field_tag, time: :time_field_tag,
  boolean: :check_box_tag
}.freeze
KEYSTROKE_TYPES =

Which DOM event a control reports on. Free text and numbers report per keystroke (Helpers#on debounces :input at 250ms) so live validation and dirty? follow the typing; selects, dates and checkboxes have no intermediate states worth reporting.

%i[string text integer decimal float].freeze
COMPARISONS =

AR's own comparison vocabulary, mapped to the negation that makes an error message true. Using AR's wording means the message a field shows BEFORE a round trip is the message the record produces after one.

{
  greater_than: "<=", greater_than_or_equal_to: "<",
  less_than: ">=", less_than_or_equal_to: ">"
}.freeze
CONDITIONAL =

Options that make a validator run only sometimes. Whatever they depend on is a fact about the RECORD, which a form signal does not have — so a validator carrying one of these contributes nothing before a round trip and is dropped rather than guessed at. It still runs at #commit and still lands in #errors.

%i[if unless on].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attr:, required: false, reflection: nil, label_column: nil, validators: []) ⇒ ScaffoldColumn

Returns a new instance of ScaffoldColumn.



66
67
68
69
70
71
72
73
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 66

def initialize(attr:, required: false, reflection: nil,
               label_column: nil, validators: [])
  @attr = attr
  @required = required
  @reflection = reflection
  @label_column = label_column
  @validators = validators
end

Instance Attribute Details

#attrObject (readonly)

Returns the value of attribute attr.



64
65
66
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 64

def attr
  @attr
end

#reflectionObject (readonly)

Returns the value of attribute reflection.



64
65
66
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 64

def reflection
  @reflection
end

#validatorsObject (readonly)

Returns the value of attribute validators.



64
65
66
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 64

def validators
  @validators
end

Instance Method Details

#association_class_nameObject



89
90
91
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 89

def association_class_name
  @association_class_name ||= reflection&.class_name || attr.name.camelize
end

#association_nameObject



87
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 87

def association_name = @association_name ||= (reflection&.name || attr.name).to_sym

#belongs_to?Boolean

Returns:

  • (Boolean)


78
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 78

def belongs_to? = !reflection.nil? || attr.reference?

#display_readerObject

What the row template prints, as source text: a belongs_to prints the association's label (&. mirrors a missing parent, like the old allow_nil delegate did), a scalar its own reader.



103
104
105
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 103

def display_reader
  belongs_to? ? "#{association_name}&.#{label_column}" : name.to_s
end

#filterable?Boolean

Returns:

  • (Boolean)


118
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 118

def filterable? = type == :boolean

#form_field_helperObject



108
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 108

def form_field_helper = belongs_to? ? :collection_select : attr.field_type

#html_boundsObject

min: / max: for a number field, from the same numericality validator that produces the live error. Nothing is invented: a column with no validator gets no bounds.



125
126
127
128
129
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 125

def html_bounds
  return {} unless numeric?

  { min: lower_bound, max: upper_bound }.compact
end

#human_nameObject



77
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 77

def human_name = attr.human_name

#label_columnObject

Which column of the ASSOCIATED table a select displays. Only the introspection path can know it; the attribute path has no second schema to look at, so it guesses :name and the generator's post-install output says so out loud. Defaulted here rather than at each call site — a nil leaking into a template emits order(:).



98
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 98

def label_column = @label_column || :name

#live_error_clauseObject

The right-hand side of one entry in the form's live_errors derived, or nil when nothing about this column is knowable before a round trip. Clauses are OR-ed so a column can be both required and bounded; with a single clause this is byte-identical to a hand-written entry.



135
136
137
138
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 135

def live_error_clause
  clauses = [presence_clause, *numericality_clauses, length_clause].compact
  clauses.join(" || ") unless clauses.empty?
end

#nameObject

From the reflection when there is one: belongs_to :author, foreign_key: :writer_id is the case where the association's name and its column genuinely disagree, and GeneratedAttribute can only guess.



83
84
85
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 83

def name
  @name ||= (reflection ? reflection.foreign_key : attr.column_name).to_sym
end

#numeric?Boolean

Returns:

  • (Boolean)


120
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 120

def numeric? = %i[integer decimal float].include?(type)

#options_localObject



107
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 107

def options_local = :"#{association_name}_options"

#required?Boolean

Returns:

  • (Boolean)


76
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 76

def required? = @required

#searchable?Boolean

Returns:

  • (Boolean)


117
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 117

def searchable? = !belongs_to? && TEXTUAL.include?(type)

#sortable?Boolean

Returns:

  • (Boolean)


119
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 119

def sortable? = !belongs_to? && COMPARABLE.include?(type)

#tag_eventObject



111
112
113
114
115
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 111

def tag_event
  return :change if belongs_to?

  KEYSTROKE_TYPES.include?(type) ? :input : :change
end

#tag_field_helperObject



109
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 109

def tag_field_helper = belongs_to? ? :select_tag : TAG_HELPERS.fetch(type, :text_field_tag)

#typeObject



75
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 75

def type = attr.type