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



37
38
39
# File 'lib/cafe_car/field_info.rb', line 37

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)


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

def attachment?  = model.reflect_on_attachment(method)

#attachment_typeObject



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

def attachment_type
  :attachment if attachment?
end

#attribute_typeObject



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

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

#autocompleteObject



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

def autocomplete = i18n(:autocomplete)

#collectionObject



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

def collection   = reflection.klass.all

#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



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

def default_type
  case method
  when :controls then method
  end
end

#digest?Boolean

Returns:

  • (Boolean)


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

def digest?      = method =~ /_digest$/

#digest_typeObject



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

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

#displayableObject



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

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

#hintObject



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

def hint         = i18n(:hint)

#humanObject



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

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

#i18n(key, **opts) ⇒ Object



80
81
82
83
84
# File 'lib/cafe_car/field_info.rb', line 80

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



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

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



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/cafe_car/field_info.rb', line 104

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 :date     then :date_field
  when :datetime then :datetime_field
  when :password then :password_field
  when :nested   then :fields_for
  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



122
123
124
125
126
127
# File 'lib/cafe_car/field_info.rb', line 122

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

#labelObject



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

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

#nested_attributes_typeObject



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

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

#password?Boolean

Returns:

  • (Boolean)


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

def password?    = type == :password

#placeholderObject



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

def placeholder  = i18n(:placeholder)

#polymorphic?Boolean

Returns:

  • (Boolean)


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

def polymorphic? = reflection&.polymorphic?

#polymorphic_methodsObject



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

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

#promptObject



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

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

#reflectionObject



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

def reflection
  return if @method.nil?
  model.try(:reflect_on_association, @method) || reflections_by_attribute[@method]
end

#reflection_typeObject



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

def reflection_type = reflection&.macro

#reflections_by_attributeObject



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/cafe_car/field_info.rb', line 130

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)


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

def required?    = validator?(:presence)

#rich_text?Boolean

Returns:

  • (Boolean)


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

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



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

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

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

Returns:

  • (Boolean)


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

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

#widthObject



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

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