aboutsummaryrefslogtreecommitdiffstats
path: root/app/models/custom_field.rb
blob: 98cb386146b9aafe69f4230dbe1f4fc8f203bc8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
class CustomField < ActiveRecord::Base

  extend Enumerize
  belongs_to :workgroup
  enumerize :field_type, in: %i{list integer string attachment}

  validates :name, uniqueness: {scope: [:resource_type, :workgroup_id]}
  validates :code, uniqueness: {scope: [:resource_type, :workgroup_id], case_sensitive: false}

  class Collection < HashWithIndifferentAccess
    def initialize object
      vals = object.class.custom_fields.map do |v|
        [v.code, CustomField::Value.new(object, v, object.custom_field_value(v.code))]
      end
      super Hash[*vals.flatten]
    end

    def to_hash
      HashWithIndifferentAccess[*self.map{|k, v| [k, v.to_hash]}.flatten(1)]
    end
  end

  class Value
    def self.new owner, custom_field, value
      field_type = custom_field.field_type
      klass_name = field_type && "CustomField::Value::#{field_type.classify}"
      klass = klass_name && const_defined?(klass_name) ? klass_name.constantize : CustomField::Value::Base
      klass.new owner, custom_field, value
    end

    class Base
      def initialize owner, custom_field, value
        @custom_field = custom_field
        @raw_value = value
        @owner = owner
        @errors = []
        @validated = false
        @valid = false
      end
      attr_accessor :owner

      delegate :code, :name, :field_type, to: :@custom_field

      def options
        @custom_field.options || {}
      end

      def validate
        @valid = true
      end

      def valid?
        validate unless @validated
        @valid
      end

      def value
        @raw_value
      end

      def errors_key
        "custom_fields.#{code}"
      end

      def to_hash
        HashWithIndifferentAccess[*%w(code name field_type options value).map{|k| [k, send(k)]}.flatten(1)]
      end

      def display_value
        value
      end

      def initialize_custom_field
      end

      def preprocess_value_for_assignment val
        val
      end

      def render_partial
        ActionView::Base.new(Rails.configuration.paths["app/views"].first).render(
          :partial => "shared/custom_fields/#{field_type}",
          :locals => { field: self}
        )
      end
    end

    class Integer < Base
      def value
        @raw_value&.to_i
      end

      def validate
        @valid = true
        return if @raw_value.is_a?(Fixnum) || @raw_value.is_a?(Float)
        unless @raw_value.to_s =~ /\A\d*\Z/
          @owner.errors.add errors_key, "'#{@raw_value}' is not a valid integer"
          @valid = false
        end
      end
    end

    class List < Integer
      def validate
        super
        return unless value.present?
        unless value >= 0 && value < options["list_values"].size
          @owner.errors.add errors_key, "'#{@raw_value}' is not a valid value"
          @valid = false
        end
      end

      def display_value
        options["list_values"][value]
      end
    end

    class Attachment < Base
      def initialize_custom_field
        custom_field_code = self.code
        _attr_name = attr_name
        _uploader_name = uploader_name
        owner.send :define_singleton_method, "read_uploader" do |attr|
          if attr.to_s == _attr_name
            custom_field_values[custom_field_code]
          else
            read_attribute attr
          end
        end

        owner.send :define_singleton_method, "write_uploader" do |attr, val|
          if attr.to_s == _attr_name
            custom_field_values[custom_field_code] = val
          else
            write_attribute attr, val
          end
        end

        owner.send :define_singleton_method, "#{_attr_name}_will_change!" do
          custom_field_values_will_change!
        end

        _extension_whitelist = options["extension_whitelist"]

        owner.send :define_singleton_method, "#{_uploader_name}_extension_whitelist" do
          _extension_whitelist
        end

        unless owner.class.uploaders.has_key? _uploader_name.to_sym
          owner.class.mount_uploader _uploader_name, CustomFieldAttachmentUploader, mount_on: "custom_field_#{code}_raw_value"
        end
      end

      def preprocess_value_for_assignment val
        owner.send "#{uploader_name}=", val
      end

      def value
        owner.send "custom_field_#{code}"
      end

      def raw_value
        @raw_value
      end

      def attr_name
        "custom_field_#{code}_raw_value"
      end

      def uploader_name
        "custom_field_#{code}"
      end

      def display_value
        render_partial
      end
    end

    class String < Base
      def value
        "#{@raw_value}"
      end
    end
  end
end