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


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

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


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

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


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

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)

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

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

#configure(model) ⇒ Object


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
# File 'app/models/attributable/attribute.rb', line 82

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?
      required.validates name, numericality: { only_integer: integer?, greater_than_or_equal_to: minimum } if integer? || float?
      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


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

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

#display_nameObject


130
131
132
# File 'app/models/attributable/attribute.rb', line 130

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

142
143
144
# File 'app/models/attributable/attribute.rb', line 142

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

#fixed_selection?Boolean

Returns:

  • (Boolean)

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

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

#float?Boolean

Returns:

  • (Boolean)

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

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

#from(record) ⇒ Object


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

def from(record)
  record[name]
end

#integer?Boolean

Returns:

  • (Boolean)

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

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

#kindObject


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

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

  FieldInfo::TEXT
end

#minimumObject


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

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

#optional?Boolean

Returns:

  • (Boolean)

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

def optional?
  !required?
end

#required?Boolean

Returns:

  • (Boolean)

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

def required?
  @required
end

#selection?Boolean

Returns:

  • (Boolean)

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

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

#selection_from_metadata(validator_source) ⇒ Object


154
155
156
157
# File 'app/models/attributable/attribute.rb', line 154

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


159
160
161
# File 'app/models/attributable/attribute.rb', line 159

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

#selection_valuesObject


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

def selection_values
  @options[:in]
end

#to_field_info(validator_source = nil) ⇒ Object


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

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


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

def valid_format
  @options[:with]
end

#valid_format?Boolean

Returns:

  • (Boolean)

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

def valid_format?
  valid_format
end

#validator?Boolean

Returns:

  • (Boolean)

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

def validator?
  @validator
end