File: attribute.rb

package info (click to toggle)
ruby-enumerize 2.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 432 kB
  • sloc: ruby: 3,712; makefile: 6
file content (193 lines) | stat: -rw-r--r-- 5,308 bytes parent folder | download
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
186
187
188
189
190
191
192
193
# frozen_string_literal: true

module Enumerize
  class Attribute
    attr_reader :klass, :name, :values, :default_value, :i18n_scope, :skip_validations_value, :arguments

    def initialize(klass, name, options={})
      raise ArgumentError, ':in option is required' unless options[:in]
      raise ArgumentError, ':scope option does not work with option :multiple' if options[:multiple] && options[:scope]

      extend Multiple if options[:multiple]

      @klass  = klass
      @name   = name.to_sym
      @i18n_scope = options[:i18n_scope]
      @arguments = options

      value_class = options.fetch(:value_class, Value)
      @values = Array(options[:in]).map { |v| value_class.new(self, *v).freeze }

      @value_hash = Hash[@values.map { |v| [v.value.to_s, v] }]
      @value_hash.merge! Hash[@values.map { |v| [v.to_s, v] }]

      if options[:default]
        @default_value = find_default_value(options[:default])
        raise ArgumentError, 'invalid default value' unless @default_value
      end

      @skip_validations_value = options.fetch(:skip_validations, false)
    end

    def find_default_value(value)
      if value.respond_to?(:call)
        value
      else
        find_value(value)
      end
    end

    def find_value(value)
      @value_hash[value.to_s] unless value.nil?
    end

    def find_values(*values)
      values.map { |value| find_value(value) }.compact
    end

    def each_value
      values.each { |value| yield value }
    end

    def value?(value)
      values.include?(value)
    end

    def i18n_scopes
      @i18n_scopes ||= if i18n_scope
        Array(i18n_scope)
      elsif @klass.respond_to?(:model_name)
        ["enumerize.#{@klass.model_name.i18n_key}.#{name}"]
      else
        []
      end
    end

    def options(options = {})
      values = if options.empty?
        @values
      else
        raise ArgumentError, 'Options cannot have both :only and :except' if options[:only] && options[:except]

        only = Array(options[:only]).map(&:to_s)
        except = Array(options[:except]).map(&:to_s)

        @values.reject do |value|
          if options[:only]
            !only.include?(value)
          elsif options[:except]
            except.include?(value)
          end
        end
      end

      values.map { |v| [v.text, v.to_s] }
    end

    def respond_to_missing?(method, include_private=false)
      @value_hash.include?(method.to_s) || super
    end

    def define_methods!(mod)
      mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
        def #{name}
          if defined?(super)
            self.class.enumerized_attributes[:#{name}].find_value(super)
          elsif respond_to?(:read_attribute)
            self.class.enumerized_attributes[:#{name}].find_value(read_attribute(:#{name}))
          else
            if defined?(@#{name})
              self.class.enumerized_attributes[:#{name}].find_value(@#{name})
            else
              @#{name} = nil
            end
          end
        end

        def #{name}=(new_value)
          allowed_value_or_nil = self.class.enumerized_attributes[:#{name}].find_value(new_value)
          allowed_value_or_nil = allowed_value_or_nil.value unless allowed_value_or_nil.nil?

          if defined?(super)
            super allowed_value_or_nil
          elsif respond_to?(:write_attribute, true)
            write_attribute '#{name}', allowed_value_or_nil
          else
            @#{name} = allowed_value_or_nil
          end

          _enumerized_values_for_validation['#{name}'] = new_value.nil? ? nil : new_value.to_s

          allowed_value_or_nil
        end

        def #{name}_text
          self.#{name} && self.#{name}.text
        end

        def #{name}_value
          self.#{name} && self.#{name}.value
        end
      RUBY
    end

    private

    def method_missing(method)
      if @value_hash.include?(method.to_s)
        find_value(method)
      else
        super
      end
    end
  end

  module Multiple
    def find_default_value(value)
      if value.respond_to?(:call)
        value
      else
        find_values(*value)
      end
    end

    def define_methods!(mod)
      mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
        def #{name}
          unless defined?(@_#{name}_enumerized_set)
            if defined?(super)
              self.#{name} = super
            elsif respond_to?(:read_attribute)
              self.#{name} = read_attribute(:#{name})
            else
              if defined?(@#{name})
                self.#{name} = @#{name}
              else
                self.#{name} = []
              end
            end
          end

          @_#{name}_enumerized_set
        end

        def #{name}=(values)
          @_#{name}_enumerized_set = Enumerize::Set.new(self, self.class.enumerized_attributes[:#{name}], values)
          raw_values = self.#{name}.values.map(&:value)

          if defined?(super)
            super raw_values
          elsif respond_to?(:write_attribute, true)
            write_attribute '#{name}', raw_values
          else
            @#{name} = raw_values
          end

          _enumerized_values_for_validation['#{name}'] = values.respond_to?(:map) ? values.reject(&:blank?).map(&:to_s) : values

          self.#{name}
        end
      RUBY
    end
  end
end