Class: Attributable::Attribute

Inherits:
Object
  • Object
show all
Defined in:
app/models/attributable/attribute.rb

Overview

Summarises the validations for an attribute In addition to the basis rails validation also provides: 1) Information to assist with automatically generating form elements 2) Tools to assist with validating eg. submissions prior to the creation of the

requests themselves

3) Wiping out some fields on the condition of others

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(owner, name, options = {}) ⇒ Attribute

Returns a new instance of Attribute.


15
16
17
18
19
20
21
22
# File 'app/models/attributable/attribute.rb', line 15

def initialize(owner, name, options = {})
  @owner = owner
  @name = name.to_sym
  @options = options
  @default = options.delete(:default)
  @required = options.delete(:required).present?
  @validator = options.delete(:validator).present?
end

Instance Attribute Details

#defaultObject (readonly)

Returns the value of attribute default


11
12
13
# File 'app/models/attributable/attribute.rb', line 11

def default
  @default
end

#nameObject (readonly) Also known as: assignable_attribute_name

Returns the value of attribute name


11
12
13
# File 'app/models/attributable/attribute.rb', line 11

def name
  @name
end

Class Method Details

.find_display_name(klass, name) ⇒ Object


119
120
121
122
123
124
125
126
127
128
129
130
# File 'app/models/attributable/attribute.rb', line 119

def self.find_display_name(klass, name)
  translation = I18n.t("metadata.#{klass.name.underscore.tr('/', '.')}.#{name}")

  return translation[:label] if translation.is_a?(Hash) # translation found, we return the label

  superclass = klass.superclass
  if superclass == ActiveRecord::Base # We've reached the top and have no translation
    translation # shoulb be an error message, so that's ok
  else # We still have a parent class
    find_display_name(superclass, name) # Walk up the class hierarchy and try again
  end
end

Instance Method Details

#boolean?Boolean

Returns:

  • (Boolean)

53
54
55
# File 'app/models/attributable/attribute.rb', line 53

def boolean?
  @options.key?(:boolean)
end

#configure(model) ⇒ Object


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'app/models/attributable/attribute.rb', line 81

def configure(model)
  conditions = @options.slice(:if, :on)
  save_blank_value = @options.delete(:save_blank)
  allow_blank = save_blank_value
  model.with_options(conditions) do |object|
    # false.blank? == true, so we exclude booleans here, they handle themselves further down.
    object.validates_presence_of(name) if required? && !boolean?
    object.with_options(allow_nil: optional?, allow_blank: allow_blank) do |required|
      required.validates_inclusion_of(name, in: [true, false]) if boolean?
      if integer? || float?
        required.validates name, numericality: { only_integer: integer?,
                                                 greater_than_or_equal_to: minimum }
      end
      required.validates_inclusion_of(name, in: selection_values, allow_false: true) if fixed_selection?
      required.validates_format_of(name, with: valid_format) if valid_format?
      # Custom validators should handle nil explicitly.
      required.validates name, custom: true, allow_nil: false if validator?
    end
  end

  unless save_blank_value
    model.class_eval("
      before_validation do |record|
        value = record.#{name}
        record.#{name}= nil if value and value.blank?
      end
    ")
  end

  return if conditions[:if].nil?

  model.class_eval("
    before_validation do |record|
      record.#{name}= nil unless record.#{conditions[:if]}
    end
  ")
end

#default_from(origin = nil) ⇒ Object


28
29
30
31
# File 'app/models/attributable/attribute.rb', line 28

def default_from(origin = nil)
  return nil if origin.nil?
  return origin.validator_for(name).default if validator?
end

#display_nameObject


132
133
134
# File 'app/models/attributable/attribute.rb', line 132

def display_name
  Attribute.find_display_name(@owner, name)
end

#find_default(validator_source = nil) ⇒ type

Find the default value for the attribute. Validator source needs to respond to #validator_for such as metadata or a request type. such as those on request types, you can pass in the validators here.

Parameters:

  • validator_source (#validator_for) (defaults to: nil)

    In cases where defaults are dynamic

Returns:

  • (type)
    description

144
145
146
# File 'app/models/attributable/attribute.rb', line 144

def find_default(validator_source = nil)
  default_from(validator_source) || default
end

#fixed_selection?Boolean

Returns:

  • (Boolean)

57
58
59
# File 'app/models/attributable/attribute.rb', line 57

def fixed_selection?
  @options.key?(:in)
end

#float?Boolean

Returns:

  • (Boolean)

49
50
51
# File 'app/models/attributable/attribute.rb', line 49

def float?
  @options.fetch(:positive_float, false)
end

#from(record) ⇒ Object


24
25
26
# File 'app/models/attributable/attribute.rb', line 24

def from(record)
  record[name]
end

#integer?Boolean

Returns:

  • (Boolean)

45
46
47
# File 'app/models/attributable/attribute.rb', line 45

def integer?
  @options.fetch(:integer, false)
end

#kindObject


148
149
150
151
152
153
154
# File 'app/models/attributable/attribute.rb', line 148

def kind
  return FieldInfo::SELECTION if selection?
  return FieldInfo::BOOLEAN if boolean?
  return FieldInfo::NUMERIC if integer? || float?

  FieldInfo::TEXT
end

#minimumObject


65
66
67
# File 'app/models/attributable/attribute.rb', line 65

def minimum
  @options.fetch(:minimum, 0)
end

#optional?Boolean

Returns:

  • (Boolean)

41
42
43
# File 'app/models/attributable/attribute.rb', line 41

def optional?
  !required?
end

#required?Boolean

Returns:

  • (Boolean)

37
38
39
# File 'app/models/attributable/attribute.rb', line 37

def required?
  @required
end

#selection?Boolean

Returns:

  • (Boolean)

61
62
63
# File 'app/models/attributable/attribute.rb', line 61

def selection?
  fixed_selection? || @options.key?(:selection)
end

#selection_from_metadata(validator_source) ⇒ Object


156
157
158
159
# File 'app/models/attributable/attribute.rb', line 156

def (validator_source)
  return nil if validator_source.blank?
  return validator_source.validator_for(name).valid_options.to_a if validator?
end

#selection_options(validator_source) ⇒ Object


161
162
163
# File 'app/models/attributable/attribute.rb', line 161

def selection_options(validator_source)
  selection_values || (validator_source) || []
end

#selection_valuesObject


69
70
71
# File 'app/models/attributable/attribute.rb', line 69

def selection_values
  @options[:in]
end

#to_field_info(validator_source = nil) ⇒ Object


165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'app/models/attributable/attribute.rb', line 165

def to_field_info(validator_source = nil)
  options = {
    # TODO[xxx]: currently only working for metadata, the only place attributes are used
    display_name: display_name,
    key: assignable_attribute_name,
    default_value: find_default(validator_source),
    kind: kind,
    required: required?
  }
  options.update(selection: selection_options(validator_source)) if selection?
  options.update(step: 1, min: minimum) if integer?
  options.update(step: 0.1, min: 0) if float?
  FieldInfo.new(options)
end

#valid_formatObject


73
74
75
# File 'app/models/attributable/attribute.rb', line 73

def valid_format
  @options[:with]
end

#valid_format?Boolean

Returns:

  • (Boolean)

77
78
79
# File 'app/models/attributable/attribute.rb', line 77

def valid_format?
  valid_format
end

#validator?Boolean

Returns:

  • (Boolean)

33
34
35
# File 'app/models/attributable/attribute.rb', line 33

def validator?
  @validator
end