Class: CafeCar::FieldInfo

Inherits:
Object
  • Object
show all
Defined in:
lib/cafe_car/field_info.rb

Constant Summary collapse

@@reflections_by_attribute =
{}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model:, method:) ⇒ FieldInfo

Returns a new instance of FieldInfo.



8
9
10
11
12
# File 'lib/cafe_car/field_info.rb', line 8

def initialize(model:, method:)
  # raise "Cannot get FieldInfo for nil method" if method.nil?
  @method = method&.to_sym
  @model = model
end

Instance Attribute Details

#methodObject (readonly) Also known as: name

Returns the value of attribute method.



3
4
5
# File 'lib/cafe_car/field_info.rb', line 3

def method
  @method
end

#modelObject (readonly)

Returns the value of attribute model.



3
4
5
# File 'lib/cafe_car/field_info.rb', line 3

def model
  @model
end

Instance Method Details

#abrogated_keysObject



44
45
46
# File 'lib/cafe_car/field_info.rb', line 44

def abrogated_keys
  [ *reflection&.foreign_type&.to_sym ]
end

#associated?Boolean

Returns:

  • (Boolean)


25
# File 'lib/cafe_car/field_info.rb', line 25

def associated?  = reflection.present?

#association?Boolean

Returns:

  • (Boolean)


20
21
22
23
# File 'lib/cafe_car/field_info.rb', line 20

def association?
  return if @method.nil?
  model.reflect_on_association(@method).present?
end

#attachment?Boolean

Returns:

  • (Boolean)


32
# File 'lib/cafe_car/field_info.rb', line 32

def attachment?  = model.reflect_on_attachment(method)

#attachment_typeObject



64
65
66
# File 'lib/cafe_car/field_info.rb', line 64

def attachment_type
  :attachment if attachment?
end

#attribute_typeObject



58
# File 'lib/cafe_car/field_info.rb', line 58

def attribute_type  = model.type_for_attribute(@method)&.type

#autocompleteObject



76
# File 'lib/cafe_car/field_info.rb', line 76

def autocomplete = i18n(:autocomplete)

#collection(scope = reflection.klass.all) ⇒ Object



34
# File 'lib/cafe_car/field_info.rb', line 34

def collection(scope = reflection.klass.all) = scope.limit(CafeCar.max_collection_options)

#constant?Boolean

Returns:

  • (Boolean)


17
# File 'lib/cafe_car/field_info.rb', line 17

def constant?  = method.in? %i[id created_at updated_at]

#default_typeObject



50
51
52
53
54
# File 'lib/cafe_car/field_info.rb', line 50

def default_type
  case method
  when :controls, :select then method
  end
end

#digest?Boolean

Returns:

  • (Boolean)


27
# File 'lib/cafe_car/field_info.rb', line 27

def digest?      = method =~ /_digest$/

#digest_typeObject



59
60
61
62
# File 'lib/cafe_car/field_info.rb', line 59

def digest_type
  key = @method.to_s.chomp("_confirmation")
  model.type_for_attribute("#{key}_digest")&.type && :password
end

#displayableObject



48
# File 'lib/cafe_car/field_info.rb', line 48

def displayable = reflection&.name&.then { info(_1) } || self

#enum?Boolean

Returns:

  • (Boolean)


28
# File 'lib/cafe_car/field_info.rb', line 28

def enum?        = model.try(:defined_enums)&.key?(@method.to_s)

#enum_typeObject



57
# File 'lib/cafe_car/field_info.rb', line 57

def enum_type       = (:enum if enum?)

#hintObject



77
# File 'lib/cafe_car/field_info.rb', line 77

def hint         = i18n(:hint)

#humanObject



80
# File 'lib/cafe_car/field_info.rb', line 80

def human(...)   = @method&.then { model.human_attribute_name(_1, ...) }

#i18n(key, **opts) ⇒ Object



88
89
90
91
92
# File 'lib/cafe_car/field_info.rb', line 88

def i18n(key, **opts)
  return if @method.nil?
  I18n.t(@method, scope: [ :helpers, key, i18n_key ], raise: true, **opts)
rescue I18n::MissingTranslationData
end

#i18n_keyObject



87
# File 'lib/cafe_car/field_info.rb', line 87

def i18n_key = model_name.i18n_key

#id?Boolean

Returns:

  • (Boolean)


16
# File 'lib/cafe_car/field_info.rb', line 16

def id?        = method =~ /_ids?$/

#info(method) ⇒ Object



14
# File 'lib/cafe_car/field_info.rb', line 14

def info(method) = model.info.field(method)

#inputObject



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/cafe_car/field_info.rb', line 112

def input
  case type
  when :string   then :text_field
  when :decimal  then :text_field
  when :text, :json then :text_area
  when :integer  then :number_field
  when :boolean  then :check_box
  when :date     then :date_field
  when :datetime then :datetime_field
  when :password then :password_field
  when :nested   then :fields_for
  when :enum     then :enum
  when :attachment then :file_field
  when :belongs_to, :has_many then :association
  when :has_one
    rich_text? ? :rich_text_area : nil
  else raise "Missing input type for #{model_name}##{@method} of type :#{type}"
  end
end

#input_keyObject



132
133
134
135
136
137
# File 'lib/cafe_car/field_info.rb', line 132

def input_key
  case type
  when :belongs_to then reflection.foreign_key
  else method
  end
end

#labelObject



78
# File 'lib/cafe_car/field_info.rb', line 78

def label        = i18n(:label, default: human)

#multiple?Boolean

Returns:

  • (Boolean)


33
# File 'lib/cafe_car/field_info.rb', line 33

def multiple?    = attachment?&.macro == :has_many_attached

#nested_attributes_typeObject



68
69
70
71
# File 'lib/cafe_car/field_info.rb', line 68

def nested_attributes_type
  key = @method.to_s.chomp("_attributes").to_sym
  :nested if model.nested_attributes_options.key?(key)
end

#password?Boolean

Returns:

  • (Boolean)


30
# File 'lib/cafe_car/field_info.rb', line 30

def password?    = type == :password

#placeholderObject



75
# File 'lib/cafe_car/field_info.rb', line 75

def placeholder  = i18n(:placeholder)

#polymorphic?Boolean

Returns:

  • (Boolean)


26
# File 'lib/cafe_car/field_info.rb', line 26

def polymorphic? = reflection&.polymorphic?

#polymorphic_methodsObject



73
# File 'lib/cafe_car/field_info.rb', line 73

def polymorphic_methods = [ reflection.foreign_type, reflection.foreign_key ]

#promptObject



79
# File 'lib/cafe_car/field_info.rb', line 79

def prompt       = i18n(:prompt, default: "Select #{human.downcase}...")

#reflectionObject



35
36
37
38
39
40
41
42
# File 'lib/cafe_car/field_info.rb', line 35

def reflection
  return if @method.nil?
  # A nested-attributes permit names the key `<assoc>_attributes` (what Rails'
  # strong-params and `fields_for` use) — resolve it back to the association,
  # the same way `nested_attributes_type` chomps the suffix.
  key = @method.to_s.chomp("_attributes")
  model.try(:reflect_on_association, key) || reflections_by_attribute[key]
end

#reflection_typeObject



56
# File 'lib/cafe_car/field_info.rb', line 56

def reflection_type = reflection&.macro

#reflections_by_attributeObject



140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/cafe_car/field_info.rb', line 140

def reflections_by_attribute
  return {} unless model.respond_to? :reflections
  @@reflections_by_attribute[model] ||=
    model.reflections.values.index_by do |r|
      case [ r.macro, r.name ]
      in [ :belongs_to, * ]                then r.foreign_key
      in [ :has_many, * ]                  then "#{r.name.to_s.singularize}_ids"
      in [ :has_one, /^rich_text_(\w+)$/ ] then $1
      in [ :has_one, * ]                   then r.name
      else raise NoMethodError.new("Not yet implemented :#{r.macro}")
      end
    end.with_indifferent_access
end

#required?Boolean

Returns:

  • (Boolean)


81
# File 'lib/cafe_car/field_info.rb', line 81

def required?    = validator?(:presence)

#rich_text?Boolean

Returns:

  • (Boolean)


31
# File 'lib/cafe_car/field_info.rb', line 31

def rich_text?   = reflection&.name =~ /^rich_text_(\w+)$/

#timestamp?Boolean

Returns:

  • (Boolean)


18
# File 'lib/cafe_car/field_info.rb', line 18

def timestamp? = type.in? %i[date datetime time]

#typeObject



94
95
96
97
98
99
# File 'lib/cafe_car/field_info.rb', line 94

def type
  return if @method.nil?
  @type ||= nested_attributes_type || reflection_type || enum_type || attribute_type || digest_type ||
            attachment_type || default_type ||
            raise(NoMethodError.new "Can't find attribute :#{@method} on #{model_name}", @method)
end

#validator?(kind, **options) ⇒ Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/cafe_car/field_info.rb', line 83

def validator?(kind, **options)
  model.validators_on(@method).any? { _1.kind == kind and _1.options >= options }
end

#valuesObject



29
# File 'lib/cafe_car/field_info.rb', line 29

def values       = model.defined_enums[@method.to_s].keys

#widthObject



101
102
103
104
105
106
107
108
109
110
# File 'lib/cafe_car/field_info.rb', line 101

def width
  case type
  when :text, :string, :json
    # "minmax(10em, fit-content)"
    # "minmax(10em, 1fr)"
    "minmax(10em, auto)"
    # "minmax(min-content, auto)"
  else "min-content"
  end
end