@@ -5,7 +5,8 @@ module Builder
5
5
class Enum
6
6
VALID_TYPES = %i[ enum enum_set ] . freeze
7
7
8
- attr_accessor :klass , :attribute , :subtype , :options , :values , :enum_module
8
+ attr_accessor :klass , :attribute , :subtype , :options , :values ,
9
+ :klass_module , :instance_module
9
10
10
11
# Start a new builder of methods for enum values on ActiveRecord::Base
11
12
def initialize ( klass , attribute , options )
@@ -40,28 +41,41 @@ def values_methods
40
41
41
42
@values_methods = begin
42
43
values . map do |val |
43
- val = val . tr ( '-' , '_ ' )
44
- scope = base % val
44
+ key = val . downcase . tr ( '- ' , '__ ' )
45
+ scope = base % key
45
46
ask = scope + '?'
46
47
bang = scope + '!'
47
- [ val , [ scope , ask , bang ] ]
48
+ [ key , [ scope , ask , bang , val ] ]
48
49
end . to_h
49
50
end
50
51
end
51
52
53
+ # Check if it's building the methods for sets
54
+ def set_features?
55
+ options [ :set_features ] . present?
56
+ end
57
+
52
58
# Check if any of the methods that will be created get in conflict
53
59
# with the base class methods
54
60
def conflicting?
55
61
return if options [ :force ] == true
56
62
attributes = attribute . pluralize
57
63
58
64
dangerous? ( attributes , true )
59
- dangerous? ( "#{ attributes } _options " , true )
65
+ dangerous? ( "#{ attributes } _keys " , true )
60
66
dangerous? ( "#{ attributes } _texts" , true )
67
+ dangerous? ( "#{ attributes } _options" , true )
61
68
dangerous? ( "#{ attribute } _text" )
62
69
63
- values_methods . each do |attr , list |
64
- list . map ( &method ( :dangerous? ) )
70
+ if set_features?
71
+ dangerous? ( "has_#{ attributes } " , true )
72
+ dangerous? ( "has_any_#{ attributes } " , true )
73
+ end
74
+
75
+ values_methods . each do |attr , ( scope , ask , bang , *) |
76
+ dangerous? ( scope , true )
77
+ dangerous? ( bang )
78
+ dangerous? ( ask )
65
79
end
66
80
rescue Interrupt => err
67
81
raise ArgumentError , <<-MSG . squish
@@ -73,14 +87,16 @@ def conflicting?
73
87
74
88
# Create all methods needed
75
89
def build
76
- @enum_module = Module . new
90
+ @klass_module = Module . new
91
+ @instance_module = Module . new
77
92
78
93
plural
79
94
stringify
80
95
all_values
96
+ set_scopes if set_features?
81
97
82
- klass . include enum_module
83
- klass . extend enum_module :: ClassMethods
98
+ klass . extend klass_module
99
+ klass . include instance_module
84
100
end
85
101
86
102
private
@@ -96,78 +112,99 @@ def dangerous?(method_name, class_method = false)
96
112
raise Interrupt , method_name . to_s
97
113
end
98
114
end
115
+ rescue Interrupt => e
116
+ raise e if Torque ::PostgreSQL . config . enum . raise_conflicting
117
+ type = class_method ? 'class method' : 'instance method'
118
+ indicator = class_method ? '.' : '#'
119
+
120
+ Torque ::PostgreSQL . logger . info ( <<~MSG . squish )
121
+ Creating #{ class_method } :#{ method_name } for enum.
122
+ Overwriting existing method #{ klass . name } #{ indicator } #{ method_name } .
123
+ MSG
99
124
end
100
125
101
126
# Create the method that allow access to the list of values
102
127
def plural
103
- attr = attribute
104
- enum_klass = subtype . klass
105
-
106
- # TODO: Rewrite these as string
107
- enum_module . const_set ( 'ClassMethods' , Module . new )
108
- enum_module ::ClassMethods . module_eval do
109
- # def self.statuses() statuses end
110
- define_method ( attr . pluralize ) do
111
- enum_klass . values
112
- end
113
-
114
- # def self.statuses_texts() members.map(&:text) end
115
- define_method ( attr . pluralize + '_texts' ) do
116
- enum_klass . members . map do |member |
117
- member . text ( attr , self )
118
- end
119
- end
128
+ enum_klass = subtype . klass . name
129
+ klass_module . module_eval <<-RUBY , __FILE__ , __LINE__ + 1
130
+ def #{ attribute . pluralize } # def roles
131
+ ::#{ enum_klass } .values # Enum::Roles.values
132
+ end # end
133
+
134
+ def #{ attribute . pluralize } _keys # def roles_keys
135
+ ::#{ enum_klass } .keys # Enum::Roles.keys
136
+ end # end
137
+
138
+ def #{ attribute . pluralize } _texts # def roles_texts
139
+ ::#{ enum_klass } .members.map do |member| # Enum::Roles.members do |member|
140
+ member.text('#{ attribute } ', self) # member.text('role', self)
141
+ end # end
142
+ end # end
143
+
144
+ def #{ attribute . pluralize } _options # def roles_options
145
+ #{ attribute . pluralize } _texts.zip(::#{ enum_klass } .values) # roles_texts.zip(Enum::Roles.values)
146
+ end # end
147
+ RUBY
148
+ end
120
149
121
- # def self.statuses_options() statuses_texts.zip(statuses) end
122
- define_method ( attr . pluralize + '_options' ) do
123
- public_send ( attr . pluralize + '_texts' ) . zip ( enum_klass . values )
124
- end
125
- end
150
+ # Create additional methods when the enum is a set, which needs
151
+ # better ways to check if values are present or not
152
+ def set_scopes
153
+ cast_type = subtype . name . chomp ( '[]' )
154
+ klass_module . module_eval <<-RUBY , __FILE__ , __LINE__ + 1
155
+ def has_#{ attribute . pluralize } (*values) # def has_roles(*values)
156
+ attr = arel_attribute('#{ attribute } ') # attr = arel_attribute('role')
157
+ where(attr.contains(::Arel.array(values, cast: '#{ cast_type } '))) # where(attr.contains(::Arel.array(values, cast: 'roles')))
158
+ end # end
159
+
160
+ def has_any_#{ attribute . pluralize } (*values) # def has_roles(*values)
161
+ attr = arel_attribute('#{ attribute } ') # attr = arel_attribute('role')
162
+ where(attr.overlaps(::Arel.array(values, cast: '#{ cast_type } '))) # where(attr.overlaps(::Arel.array(values, cast: 'roles')))
163
+ end # end
164
+ RUBY
126
165
end
127
166
128
167
# Create the method that turn the attribute value into text using
129
168
# the model scope
130
169
def stringify
131
- attr = attribute
132
-
133
- # TODO: Rewrite these as string
134
- enum_module . module_eval do
135
- # def status_text() status.text('status', self) end
136
- define_method ( "#{ attr } _text" ) { send ( attr ) &.text ( attr , self ) }
137
- end
170
+ instance_module . module_eval <<-RUBY , __FILE__ , __LINE__ + 1
171
+ def #{ attribute } _text # def role_text
172
+ #{ attribute } .text('#{ attribute } ', self) # role.text('role', self)
173
+ end # end
174
+ RUBY
138
175
end
139
176
140
177
# Create all the methods that represent actions related to the
141
178
# attribute value
142
179
def all_values
143
- attr = attribute
144
- vals = values_methods
145
-
146
- enum_klass = subtype . klass
147
- model_klass = klass
148
-
149
- # TODO: Rewrite these as string
150
- enum_module . module_eval do
151
- vals . each do |val , list |
152
- # scope :disabled, -> { where(status: 'disabled') }
153
- model_klass . scope list [ 0 ] , -> do
154
- where ( enum_klass . scope ( arel_table [ attr ] , val ) )
155
- end
156
-
157
- # def disabled? status.disabled? end
158
- define_method ( list [ 1 ] ) { send ( attr ) . public_send ( "#{ val } ?" ) }
159
-
160
- # def disabled!
161
- # changed = send(attr).public_send("#{val}!")
162
- # save! if changed && enum_save_on_bang
163
- # true
164
- define_method ( list [ 2 ] ) do
165
- changed = send ( attr ) . public_send ( "#{ val } !" )
166
- return save! if changed && enum_save_on_bang
167
- true
168
- end
169
- end
180
+ klass_content = ''
181
+ instance_content = ''
182
+ enum_klass = subtype . klass . name
183
+
184
+ values_methods . each do |key , ( scope , ask , bang , val ) |
185
+ klass_content += <<-RUBY
186
+ def #{ scope } # def admin
187
+ attr = arel_attribute('#{ attribute } ') # attr = arel_attribute('role')
188
+ where(::#{ enum_klass } .scope(attr, '#{ val } ')) # where(Enum::Roles.scope(attr, 'admin'))
189
+ end # end
190
+ RUBY
191
+
192
+ instance_content += <<-RUBY
193
+ def #{ ask } # def admin?
194
+ #{ attribute } .#{ key } ? # role.admin?
195
+ end # end
196
+
197
+ def #{ bang } # admin!
198
+ self.#{ attribute } = '#{ val } ' # self.role = 'admin'
199
+ return unless #{ attribute } _changed? # return unless role_changed?
200
+ return save! if Torque::PostgreSQL.config.enum.save_on_bang
201
+ true # true
202
+ end # end
203
+ RUBY
170
204
end
205
+
206
+ klass_module . module_eval ( klass_content )
207
+ instance_module . module_eval ( instance_content )
171
208
end
172
209
end
173
210
end
0 commit comments