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 answers :textarea on Rails 8.1 and :text_area on 7.1, which is the difference between a generated _form that runs on this gem's floor and one that doesn't. 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 on purpose: Rails 8.1 renamed them to #textarea_tag / #checkbox_tag but kept these as aliases, while 7.1 and 7.2 have only these. One spelling valid across the whole supported matrix beats a version branch in a template.

{
  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.



63
64
65
66
67
68
69
70
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 63

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.



61
62
63
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 61

def attr
  @attr
end

#reflectionObject (readonly)

Returns the value of attribute reflection.



61
62
63
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 61

def reflection
  @reflection
end

#validatorsObject (readonly)

Returns the value of attribute validators.



61
62
63
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 61

def validators
  @validators
end

Instance Method Details

#association_class_nameObject



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

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

#association_nameObject



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

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

#belongs_to?Boolean

Returns:

  • (Boolean)


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

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

#display_readerObject

What the row partial prints. A belongs_to contributes the association's label rather than its id, which is why the model gets a delegate :name, to: :author, prefix: true โ€” the record and the row projection then answer the same reader, so one partial serves both.



101
102
103
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 101

def display_reader
  belongs_to? ? :"#{association_name}_#{label_column}" : name
end

#filterable?Boolean

Returns:

  • (Boolean)


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

def filterable? = type == :boolean

#form_field_helperObject



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

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.



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

def html_bounds
  return {} unless numeric?

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

#human_nameObject



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

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(:).



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

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.



133
134
135
136
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 133

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.



80
81
82
# File 'lib/generators/hibiki/rails/scaffold_schema.rb', line 80

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

#numeric?Boolean

Returns:

  • (Boolean)


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

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

#options_localObject



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

def options_local = :"#{association_name}_options"

#required?Boolean

Returns:

  • (Boolean)


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

def required? = @required

#searchable?Boolean

Returns:

  • (Boolean)


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

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

#sortable?Boolean

Returns:

  • (Boolean)


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

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

#tag_eventObject



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

def tag_event
  return :change if belongs_to?

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

#tag_field_helperObject



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

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

#typeObject



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

def type = attr.type