akm-selectable_attr 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 3
3
+ :patch: 0
4
+ :major: 0
@@ -0,0 +1,304 @@
1
+ # -*- coding: utf-8 -*-
2
+ module SelectableAttr
3
+ module Base
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ ENUM_ARRAY_METHODS = {
9
+ :none => {
10
+ :to_hash_array => Proc.new do |enum, attr_value|
11
+ value = (attr_value || []).map(&:to_s)
12
+ enum.to_hash_array do |hash|
13
+ hash[:select] = value.include?(hash[:id].to_s)
14
+ end
15
+ end,
16
+
17
+ :to_attr_value => Proc.new do |enum, hash_array|
18
+ hash_array.select{|hash| hash[:select]}.map{|hash| hash[:id]}
19
+ end
20
+ },
21
+
22
+ :comma_string => {
23
+ :to_hash_array => Proc.new do |enum, attr_value|
24
+ values = attr_value.is_a?(Array) ? attr_value.map{|v|v.to_s} :
25
+ (attr_value || '').split(',')
26
+ enum.to_hash_array do |hash|
27
+ hash[:select] = values.include?(hash[:id].to_s)
28
+ end
29
+ end,
30
+
31
+ :to_attr_value => Proc.new do |enum, hash_array|
32
+ hash_array.select{|hash| hash[:select]}.map{|hash| hash[:id]}.join(',')
33
+ end
34
+ },
35
+
36
+
37
+ :binary_string => {
38
+ :to_hash_array => Proc.new do |enum, attr_value|
39
+ value = attr_value || ''
40
+ idx = 0
41
+ enum.to_hash_array do |hash|
42
+ hash[:select] = (value[idx, 1] == '1')
43
+ idx += 1
44
+ end
45
+ end,
46
+
47
+ :to_attr_value => Proc.new do |enum, hash_array|
48
+ result = ''
49
+ hash_map = hash_array.inject({}){|dest, hash| dest[hash[:id]] = hash; dest}
50
+ enum.each do |entry|
51
+ hash = hash_map[entry.id]
52
+ result << (hash[:select] ? '1' : '0')
53
+ end
54
+ result
55
+ end
56
+ }
57
+ }
58
+
59
+ module ClassMethods
60
+ def single_selectable_attrs
61
+ @single_selectable_attrs_hash ||= {};
62
+ @single_selectable_attrs_hash[self] ||= []
63
+ end
64
+
65
+ def multi_selectable_attrs
66
+ @multi_selectable_attrs_hash ||= {};
67
+ @multi_selectable_attrs_hash[self] ||= []
68
+ end
69
+
70
+ def selectable_attr_type_for(attr)
71
+ single_selectable_attrs.include?(attr) ? :single :
72
+ multi_selectable_attrs.include?(attr) ? :multi : nil
73
+ end
74
+
75
+ def enum(*args, &block)
76
+ process_definition(block, *args) do |enum, context|
77
+ self.single_selectable_attrs << context[:attr].to_s
78
+ define_enum_class_methods(context)
79
+ define_enum_instance_methods(context)
80
+ end
81
+ end
82
+ alias_method :single_selectable_attr, :enum
83
+ alias_method :selectable_attr, :enum
84
+
85
+
86
+ def enum_array(*args, &block)
87
+ base_options = args.last.is_a?(Hash) ? args.pop : {}
88
+ args << base_options # .update({:attr_accessor => false})
89
+ process_definition(block, *args) do |enum, context|
90
+ self.multi_selectable_attrs << context[:attr].to_s
91
+ define_enum_class_methods(context)
92
+ define_enum_array_instance_methods(context)
93
+ end
94
+ end
95
+ alias_method :multi_selectable_attr, :enum_array
96
+
97
+ def process_definition(block, *args)
98
+ base_options = args.last.is_a?(Hash) ? args.pop : {}
99
+ enum = base_options[:enum] || create_enum(&block)
100
+ args.each do |attr|
101
+ context = {
102
+ :enum => enum,
103
+ :attr_accessor => !has_attr(attr),
104
+ :attr => attr,
105
+ :base_name => enum_base_name(attr)
106
+ }.update(base_options)
107
+ define_enum(context)
108
+ define_accessor(context)
109
+ yield(enum, context)
110
+ unless enum.i18n_scope
111
+ paths = [:selectable_attrs] + self.name.to_s.split('::').map{|s| s.to_sym}
112
+ paths << attr.to_sym
113
+ enum.i18n_scope(*paths)
114
+ end
115
+ end
116
+ enum
117
+ end
118
+
119
+ def has_attr(attr)
120
+ return true if self.method_defined?(attr)
121
+ return false unless self.respond_to?(:columns)
122
+ if self.respond_to?(:connection) and self.respond_to?(:connected?)
123
+ begin
124
+ self.connection unless self.connected?
125
+ rescue Exception
126
+ return nil if !self.connected?
127
+ end
128
+ end
129
+ (self.columns || []).any?{|col|col.name.to_s == attr.to_s}
130
+ end
131
+
132
+ def attr_enumeable_base(*args, &block)
133
+ @base_name_processor = block
134
+ end
135
+
136
+ def enum_base_name(attr)
137
+ if @base_name_processor
138
+ @base_name_processor.call(attr).to_s
139
+ else
140
+ attr.to_s.gsub(selectable_attr_name_pattern, '')
141
+ end
142
+ end
143
+
144
+ DEFAULT_SELECTABLE_ATTR_NAME_PATTERN = /(_cd$|_code$|_cds$|_codes$)/
145
+
146
+ def selectable_attr_name_pattern
147
+ @selectable_attr_name_pattern ||= DEFAULT_SELECTABLE_ATTR_NAME_PATTERN
148
+ end
149
+ alias_method :enum_name_pattern, :selectable_attr_name_pattern
150
+
151
+ def selectable_attr_name_pattern=(value)
152
+ @selectable_attr_name_pattern = value
153
+ end
154
+ alias_method :enum_name_pattern=, :selectable_attr_name_pattern=
155
+
156
+ def create_enum(&block)
157
+ result = Enum.new
158
+ result.instance_eval(&block)
159
+ result
160
+ end
161
+
162
+ def define_enum(context)
163
+ base_name = context[:base_name]
164
+ const_name = "#{base_name.upcase}_ENUM"
165
+ const_set(const_name, context[:enum]) unless const_defined?(const_name)
166
+ end
167
+
168
+ def enum_for(attr)
169
+ base_name = enum_base_name(attr)
170
+ const_get("#{base_name.upcase}_ENUM")
171
+ end
172
+
173
+ def define_accessor(context)
174
+ attr = context[:attr]
175
+ return unless (instance_methods & [attr, "#{attr}="]).empty?
176
+ if context[:attr_accessor]
177
+ if context[:default]
178
+ if respond_to?(:attr_accessor_with_default)
179
+ attr_accessor_with_default(attr, context[:default])
180
+ else
181
+ instance_var_name = "@#{attr}"
182
+ attr_writer(attr)
183
+ define_method(attr) do
184
+ value = instance_variable_get(instance_var_name)
185
+ instance_variable_set(instance_var_name, value = context[:default]) unless value
186
+ value
187
+ end
188
+ end
189
+ else
190
+ attr_accessor(attr)
191
+ end
192
+ else
193
+ if context[:default]
194
+ $stderr.puts "WARNING! :default option ignored for #{attr}"
195
+ end
196
+ end
197
+ end
198
+
199
+ def define_enum_class_methods(context)
200
+ base_name = context[:base_name]
201
+ enum = context[:enum]
202
+ mod = Module.new
203
+ mod.module_eval do
204
+ define_method("#{base_name}_enum"){enum}
205
+ define_method("#{base_name}_hash_array"){enum.to_hash_array}
206
+ define_method("#{base_name}_entries"){enum.entries}
207
+ define_method("#{base_name}_options"){|*ids_or_keys|enum.options(*ids_or_keys)}
208
+ define_method("#{base_name}_ids"){|*ids_or_keys| enum.ids(*ids_or_keys)}
209
+ define_method("#{base_name}_keys"){|*ids_or_keys|enum.keys(*ids_or_keys)}
210
+ define_method("#{base_name}_names"){|*ids_or_keys|enum.names(*ids_or_keys)}
211
+ define_method("#{base_name}_key_by_id"){|id|enum.key_by_id(id)}
212
+ define_method("#{base_name}_id_by_key"){|key|enum.id_by_key(key)}
213
+ define_method("#{base_name}_name_by_id"){|id|enum.name_by_id(id)}
214
+ define_method("#{base_name}_name_by_key"){|key|enum.name_by_key(key)}
215
+ define_method("#{base_name}_entry_by_id"){|id|enum.entry_by_id(id)}
216
+ define_method("#{base_name}_entry_by_key"){|key|enum.entry_by_key(key)}
217
+ end
218
+ if convertors = ENUM_ARRAY_METHODS[context[:convert_with] || :none]
219
+ mod.module_eval do
220
+ define_method("#{base_name}_to_hash_array", convertors[:to_hash_array])
221
+ define_method("hash_array_to_#{base_name}", convertors[:to_attr_value])
222
+ end
223
+ end
224
+ self.extend(mod)
225
+ end
226
+
227
+ def define_enum_instance_methods(context)
228
+ attr = context[:attr]
229
+ base_name = context[:base_name]
230
+ instance_methods = <<-EOS
231
+ def #{base_name}_key
232
+ self.class.#{base_name}_key_by_id(#{attr})
233
+ end
234
+ def #{base_name}_key=(key)
235
+ self.#{attr} = self.class.#{base_name}_id_by_key(key)
236
+ end
237
+ def #{base_name}_name
238
+ self.class.#{base_name}_name_by_id(#{attr})
239
+ end
240
+ def #{base_name}_entry
241
+ self.class.#{base_name}_entry_by_id(#{attr})
242
+ end
243
+ def #{base_name}_entry
244
+ self.class.#{base_name}_entry_by_id(#{attr})
245
+ end
246
+ EOS
247
+ self.module_eval(instance_methods)
248
+ end
249
+
250
+ def define_enum_array_instance_methods(context)
251
+ attr = context[:attr]
252
+ base_name = context[:base_name]
253
+ # ActiveRecord::Baseから継承している場合は、基本カラムに対応するメソッドはない
254
+ self.module_eval(<<-"EOS")
255
+ def #{base_name}_ids
256
+ #{base_name}_hash_array_selected.map{|hash|hash[:id]}
257
+ end
258
+ def #{base_name}_ids=(ids)
259
+ ids = ids.split(',') if ids.is_a?(String)
260
+ ids = ids ? ids.map(&:to_s) : []
261
+ update_#{base_name}_hash_array{|hash|ids.include?(hash[:id].to_s)}
262
+ end
263
+ EOS
264
+ self.module_eval(<<-"EOS")
265
+ def #{base_name}_hash_array
266
+ self.class.#{base_name}_to_hash_array(self.class.#{base_name}_enum, #{attr})
267
+ end
268
+ def #{base_name}_hash_array=(hash_array)
269
+ self.#{attr} = self.class.hash_array_to_#{base_name}(self.class.#{base_name}_enum, hash_array)
270
+ end
271
+ def #{base_name}_hash_array_selected
272
+ #{base_name}_hash_array.select{|hash|!!hash[:select]}
273
+ end
274
+ def update_#{base_name}_hash_array(&block)
275
+ hash_array = #{base_name}_hash_array.map do |hash|
276
+ hash.merge(:select => yield(hash))
277
+ end
278
+ self.#{base_name}_hash_array = hash_array
279
+ end
280
+ def #{base_name}_keys
281
+ #{base_name}_hash_array_selected.map{|hash|hash[:key]}
282
+ end
283
+ def #{base_name}_keys=(keys)
284
+ update_#{base_name}_hash_array{|hash|keys.include?(hash[:key])}
285
+ end
286
+ def #{base_name}_selection
287
+ #{base_name}_hash_array.map{|hash|!!hash[:select]}
288
+ end
289
+ def #{base_name}_selection=(selection)
290
+ idx = -1
291
+ update_#{base_name}_hash_array{|hash| idx += 1; !!selection[idx]}
292
+ end
293
+ def #{base_name}_names
294
+ #{base_name}_hash_array_selected.map{|hash|hash[:name]}
295
+ end
296
+ def #{base_name}_entries
297
+ ids = #{base_name}_ids
298
+ self.class.#{base_name}_enum.select{|entry|ids.include?(entry.id)}
299
+ end
300
+ EOS
301
+ end
302
+ end
303
+ end
304
+ end
@@ -0,0 +1,190 @@
1
+ module SelectableAttr
2
+
3
+ class Enum
4
+ include Enumerable
5
+
6
+ class << self
7
+ def instances
8
+ @@instances ||= []
9
+ end
10
+
11
+ if defined?(I18n)
12
+ def i18n_export
13
+ result = {}
14
+ instances.each do |instance|
15
+ unless instance.i18n_scope
16
+ # puts "no i18n_scope of #{instance.inspect}"
17
+ next
18
+ end
19
+ paths = instance.i18n_scope.dup
20
+ current = result
21
+ paths.each do |path|
22
+ current = current[path] ||= {}
23
+ end
24
+ instance.entries.each do |entry|
25
+ current[entry.key] = entry.name
26
+ end
27
+ end
28
+ result
29
+ end
30
+ end
31
+ end
32
+
33
+ def initialize(&block)
34
+ @entries = []
35
+ instance_eval(&block) if block_given?
36
+ SelectableAttr::Enum.instances << self if defined?(I18n)
37
+ end
38
+
39
+ def entries
40
+ @entries
41
+ end
42
+
43
+ def each(&block)
44
+ entries.each(&block)
45
+ end
46
+
47
+ def define(id, key, name, options = nil, &block)
48
+ entry = Entry.new(self, id, key, name, options, &block)
49
+ entry.instance_variable_set(:@defined_in_code, true)
50
+ @entries << entry
51
+ end
52
+ alias_method :entry, :define
53
+
54
+ def i18n_scope(*path)
55
+ @i18n_scope = path unless path.empty?
56
+ @i18n_scope
57
+ end
58
+
59
+ def match_entry(entry, value, *attrs)
60
+ attrs.any?{|attr| entry[attr].to_s == value.to_s}
61
+ end
62
+
63
+ def entry_by(value, *attrs)
64
+ entries.detect{|entry| match_entry(entry, value, *attrs)} || Entry::NULL
65
+ end
66
+
67
+ def entry_by_id(id)
68
+ entry_by(id, :id)
69
+ end
70
+
71
+ def entry_by_key(key)
72
+ entry_by(key, :key)
73
+ end
74
+
75
+ def entry_by_id_or_key(id_or_key)
76
+ entry_by(id_or_key, :id, :key)
77
+ end
78
+
79
+ def entry_by_hash(attrs)
80
+ entries.detect{|entry| attrs.all?{|(attr, value)| entry[attr].to_s == value.to_s }} || Entry::NULL
81
+ end
82
+
83
+ def [](arg)
84
+ arg.is_a?(Hash) ? entry_by_hash(arg) : entry_by_id_or_key(arg)
85
+ end
86
+
87
+ def values(*args)
88
+ args = args.empty? ? [:name, :id] : args
89
+ result = entries.collect{|entry| args.collect{|arg| entry.send(arg) }}
90
+ (args.length == 1) ? result.flatten : result
91
+ end
92
+
93
+ def map_attrs(attrs, *ids_or_keys)
94
+ if attrs.is_a?(Array)
95
+ ids_or_keys.empty? ?
96
+ entries.map{|entry| attrs.map{|attr|entry.send(attr)}} :
97
+ ids_or_keys.map do |id_or_key|
98
+ entry = entry_by_id_or_key(id_or_key)
99
+ attrs.map{|attr|entry.send(attr)}
100
+ end
101
+ else
102
+ attr = attrs
103
+ ids_or_keys.empty? ?
104
+ entries.map(&attr.to_sym) :
105
+ ids_or_keys.map{|id_or_key|entry_by_id_or_key(id_or_key).send(attr)}
106
+ end
107
+ end
108
+
109
+ def ids(*ids_or_keys); map_attrs(:id, *ids_or_keys); end
110
+ def keys(*ids_or_keys); map_attrs(:key, *ids_or_keys); end
111
+ def names(*ids_or_keys); map_attrs(:name, *ids_or_keys); end
112
+ def options(*ids_or_keys); map_attrs([:name, :id], *ids_or_keys); end
113
+
114
+ def key_by_id(id); entry_by_id(id).key; end
115
+ def id_by_key(key); entry_by_key(key).id; end
116
+ def name_by_id(id); entry_by_id(id).name; end
117
+ def name_by_key(key); entry_by_key(key).name; end
118
+
119
+ def find(options = nil, &block)
120
+ entries.detect{|entry|
121
+ block_given? ? yield(entry) : entry.match?(options)
122
+ } || Entry::NULL
123
+ end
124
+
125
+ def to_hash_array
126
+ entries.map do |entry|
127
+ result = entry.to_hash
128
+ yield(result) if defined? yield
129
+ result
130
+ end
131
+ end
132
+
133
+ def length
134
+ entries.length
135
+ end
136
+ alias_method :size, :length
137
+
138
+ class Entry
139
+ BASE_ATTRS = [:id, :key, :name]
140
+ attr_reader :id, :key
141
+ attr_reader :defined_in_code
142
+ def initialize(enum, id, key, name, options = nil, &block)
143
+ @enum = enum
144
+ @id = id
145
+ @key = key
146
+ @name = name
147
+ @options = options
148
+ self.instance_eval(&block) if block
149
+ end
150
+
151
+ if defined?(I18n)
152
+ def name
153
+ I18n.locale.nil? ? @name :
154
+ @enum.i18n_scope.blank? ? @name :
155
+ I18n.translate(key, :scope => @enum.i18n_scope, :default => @name)
156
+ end
157
+ else
158
+ attr_reader :name
159
+ end
160
+
161
+ def [](option_key)
162
+ BASE_ATTRS.include?(option_key) ? send(option_key) :
163
+ @options ? @options[option_key] : nil
164
+ end
165
+
166
+ def match?(options)
167
+ @options === options
168
+ end
169
+
170
+ def null?
171
+ false
172
+ end
173
+
174
+ def null_object?
175
+ self.null?
176
+ end
177
+
178
+ def to_hash
179
+ (@options || {}).merge(:id => @id, :key => @key, :name => @name)
180
+ end
181
+
182
+ NULL = new(nil, nil, nil, nil) do
183
+ def null?; true; end
184
+ def name; nil; end
185
+ end
186
+ end
187
+
188
+ end
189
+
190
+ end
@@ -0,0 +1,3 @@
1
+ module SelectableAttr
2
+ VERSION = '0.0.3'
3
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ module SelectableAttr
3
+ autoload :VERSION, 'selectable_attr/version'
4
+ autoload :Enum, 'selectable_attr/enum'
5
+ autoload :Base, 'selectable_attr/base'
6
+ end
@@ -0,0 +1,331 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.join(File.dirname(__FILE__), 'spec_helper')
3
+
4
+ describe SelectableAttr do
5
+
6
+ def assert_enum_class_methods(klass, attr = :enum1)
7
+ klass.send("#{attr}_enum").length.should == 3
8
+ expected_hash_array = [
9
+ {:id => 1, :key => :entry1, :name => "エントリ1"},
10
+ {:id => 2, :key => :entry2, :name => "エントリ2"},
11
+ {:id => 3, :key => :entry3, :name => "エントリ3"}
12
+ ]
13
+ klass.send("#{attr}_hash_array").should == expected_hash_array
14
+ klass.send("#{attr}_enum").to_hash_array.should == expected_hash_array
15
+ klass.send("#{attr}_entries").
16
+ map{|entry| {:id => entry.id, :key => entry.key, :name => entry.name} }.
17
+ should == expected_hash_array
18
+
19
+ klass.send("#{attr}_ids").should == [1,2,3]
20
+ klass.send("#{attr}_ids", :entry2, :entry3).should == [2,3]
21
+ klass.send("#{attr}_keys").should == [:entry1, :entry2, :entry3]
22
+ klass.send("#{attr}_keys", 1,3).should == [:entry1, :entry3]
23
+ klass.send("#{attr}_names").should == ["エントリ1", "エントリ2", "エントリ3"]
24
+ klass.send("#{attr}_names", 1,2).should == ["エントリ1", "エントリ2"]
25
+ klass.send("#{attr}_names", :entry1, :entry2).should == ["エントリ1", "エントリ2"]
26
+ klass.send("#{attr}_options").should == [['エントリ1', 1], ['エントリ2', 2], ['エントリ3', 3]]
27
+ klass.send("#{attr}_options", :entry2, :entry3).should == [['エントリ2', 2], ['エントリ3', 3]]
28
+ klass.send("#{attr}_options", 1,2).should == [['エントリ1', 1], ['エントリ2', 2]]
29
+
30
+ klass.send("#{attr}_id_by_key", nil).should be_nil
31
+ klass.send("#{attr}_id_by_key", :entry1).should == 1
32
+ klass.send("#{attr}_id_by_key", :entry2).should == 2
33
+ klass.send("#{attr}_id_by_key", :entry3).should == 3
34
+ klass.send("#{attr}_name_by_key", nil).should be_nil
35
+ klass.send("#{attr}_name_by_key", :entry1).should == "エントリ1"
36
+ klass.send("#{attr}_name_by_key", :entry2).should == "エントリ2"
37
+ klass.send("#{attr}_name_by_key", :entry3).should == "エントリ3"
38
+
39
+ klass.send("#{attr}_key_by_id", nil).should be_nil
40
+ klass.send("#{attr}_key_by_id", 1).should == :entry1
41
+ klass.send("#{attr}_key_by_id", 2).should == :entry2
42
+ klass.send("#{attr}_key_by_id", 3).should == :entry3
43
+ klass.send("#{attr}_name_by_id", nil).should be_nil
44
+ klass.send("#{attr}_name_by_id", 1).should == "エントリ1"
45
+ klass.send("#{attr}_name_by_id", 2).should == "エントリ2"
46
+ klass.send("#{attr}_name_by_id", 3).should == "エントリ3"
47
+ end
48
+
49
+ def assert_single_enum_instance_methods(obj, attr = :enum1)
50
+ obj.send("#{attr}=", 1)
51
+ obj.send(attr).should == 1
52
+ obj.enum1_key.should == :entry1
53
+ obj.enum1_name.should == "エントリ1"
54
+ obj.enum1_entry.to_hash.should == {:id => 1, :key => :entry1, :name => "エントリ1"}
55
+
56
+ obj.enum1_key = :entry2
57
+ obj.send(attr).should == 2
58
+ obj.enum1_key.should == :entry2
59
+ obj.enum1_name.should == "エントリ2"
60
+ obj.enum1_entry.to_hash.should == {:id => 2, :key => :entry2, :name => "エントリ2"}
61
+
62
+ obj.send("#{attr}=", 3)
63
+ obj.send(attr).should == 3
64
+ obj.enum1_key.should == :entry3
65
+ obj.enum1_name.should == "エントリ3"
66
+ obj.enum1_entry.to_hash.should == {:id => 3, :key => :entry3, :name => "エントリ3"}
67
+ end
68
+
69
+ class EnumBase
70
+ include ::SelectableAttr::Base
71
+ end
72
+
73
+ class EnumMock1 < EnumBase
74
+ selectable_attr :enum1, :default => 2 do
75
+ entry 1, :entry1, "エントリ1"
76
+ entry 2, :entry2, "エントリ2"
77
+ entry 3, :entry3, "エントリ3"
78
+ end
79
+ end
80
+
81
+ class EnumMock1WithEnum < EnumBase
82
+ selectable_attr :enum1, :default => 2 do
83
+ entry 1, :entry1, "エントリ1"
84
+ entry 2, :entry2, "エントリ2"
85
+ entry 3, :entry3, "エントリ3"
86
+ end
87
+ end
88
+
89
+ it "test_selectable_attr1" do
90
+ assert_enum_class_methods(EnumMock1)
91
+ mock1 = EnumMock1.new
92
+ mock1.enum1.should == 2
93
+ assert_single_enum_instance_methods(mock1)
94
+
95
+ assert_enum_class_methods(EnumMock1WithEnum)
96
+ mock1 = EnumMock1WithEnum.new
97
+ mock1.enum1.should == 2
98
+ assert_single_enum_instance_methods(mock1)
99
+ end
100
+
101
+
102
+ class EnumMock2 < EnumBase
103
+ attr_enumeable_base do |attr|
104
+ attr.to_s.gsub(/(.*)_code(.*)$/){"#{$1}#{$2}"}
105
+ end
106
+
107
+ selectable_attr :enum_code1 do
108
+ entry 1, :entry1, "エントリ1"
109
+ entry 2, :entry2, "エントリ2"
110
+ entry 3, :entry3, "エントリ3"
111
+ end
112
+ end
113
+
114
+ class EnumMock2WithEnum < EnumBase
115
+ attr_enumeable_base do |attr|
116
+ attr.to_s.gsub(/(.*)_code(.*)$/){"#{$1}#{$2}"}
117
+ end
118
+
119
+ enum :enum_code1 do
120
+ entry 1, :entry1, "エントリ1"
121
+ entry 2, :entry2, "エントリ2"
122
+ entry 3, :entry3, "エントリ3"
123
+ end
124
+ end
125
+
126
+ it "test_selectable_attr2" do
127
+ assert_enum_class_methods(EnumMock2)
128
+ assert_single_enum_instance_methods(EnumMock2.new, :enum_code1)
129
+ assert_enum_class_methods(EnumMock2WithEnum)
130
+ assert_single_enum_instance_methods(EnumMock2WithEnum.new, :enum_code1)
131
+ end
132
+
133
+
134
+
135
+ def assert_multi_enum_instance_methods(obj, patterns)
136
+ obj.enum_array1_hash_array.should == [
137
+ {:id => 1, :key => :entry1, :name => "エントリ1", :select => false},
138
+ {:id => 2, :key => :entry2, :name => "エントリ2", :select => false},
139
+ {:id => 3, :key => :entry3, :name => "エントリ3", :select => false}
140
+ ]
141
+ obj.enum_array1_selection.should == [false, false, false]
142
+ obj.enum_array1.should be_nil
143
+ obj.enum_array1_entries.should == []
144
+ obj.enum_array1_keys.should == []
145
+ obj.enum_array1_names.should == []
146
+
147
+ obj.enum_array1 = patterns[0]
148
+ obj.enum_array1.should == patterns[0]
149
+ obj.enum_array1_hash_array.should == [
150
+ {:id => 1, :key => :entry1, :name => "エントリ1", :select => false},
151
+ {:id => 2, :key => :entry2, :name => "エントリ2", :select => false},
152
+ {:id => 3, :key => :entry3, :name => "エントリ3", :select => false}
153
+ ]
154
+ obj.enum_array1_selection.should == [false, false, false]
155
+ obj.enum_array1_entries.should == []
156
+ obj.enum_array1_keys.should == []
157
+ obj.enum_array1_names.should == []
158
+
159
+ obj.enum_array1 = patterns[1]
160
+ obj.enum_array1.should == patterns[1]
161
+ obj.enum_array1_hash_array.should == [
162
+ {:id => 1, :key => :entry1, :name => "エントリ1", :select => false},
163
+ {:id => 2, :key => :entry2, :name => "エントリ2", :select => false},
164
+ {:id => 3, :key => :entry3, :name => "エントリ3", :select => true}
165
+ ]
166
+ obj.enum_array1_selection.should == [false, false, true]
167
+ obj.enum_array1_entries.map(&:id).should == [3]
168
+ obj.enum_array1_keys.should == [:entry3]
169
+ obj.enum_array1_names.should == ['エントリ3']
170
+
171
+ obj.enum_array1 = patterns[3]
172
+ obj.enum_array1.should == patterns[3]
173
+ obj.enum_array1_hash_array.should == [
174
+ {:id => 1, :key => :entry1, :name => "エントリ1", :select => false},
175
+ {:id => 2, :key => :entry2, :name => "エントリ2", :select => true},
176
+ {:id => 3, :key => :entry3, :name => "エントリ3", :select => true}
177
+ ]
178
+ obj.enum_array1_selection.should == [false, true, true]
179
+ obj.enum_array1_entries.map(&:id).should == [2, 3]
180
+ obj.enum_array1_keys.should == [:entry2, :entry3]
181
+ obj.enum_array1_names.should == ['エントリ2', 'エントリ3']
182
+
183
+ obj.enum_array1 = patterns[7]
184
+ obj.enum_array1.should == patterns[7]
185
+ obj.enum_array1_hash_array.should == [
186
+ {:id => 1, :key => :entry1, :name => "エントリ1", :select => true},
187
+ {:id => 2, :key => :entry2, :name => "エントリ2", :select => true},
188
+ {:id => 3, :key => :entry3, :name => "エントリ3", :select => true}
189
+ ]
190
+ obj.enum_array1_selection.should == [true, true, true]
191
+ obj.enum_array1_ids.should == [1, 2, 3]
192
+ obj.enum_array1_entries.map(&:id).should == [1, 2, 3]
193
+ obj.enum_array1_keys.should == [:entry1, :entry2, :entry3]
194
+ obj.enum_array1_names.should == ['エントリ1', 'エントリ2', 'エントリ3']
195
+
196
+ obj.enum_array1_ids = [1,3]; obj.enum_array1.should == patterns[5]
197
+ obj.enum_array1_ids = [1,2]; obj.enum_array1.should == patterns[6]
198
+ obj.enum_array1_ids = [2]; obj.enum_array1.should == patterns[2]
199
+
200
+ obj.enum_array1_keys = [:entry1,:entry3]; obj.enum_array1.should == patterns[5]
201
+ obj.enum_array1_keys = [:entry1,:entry2]; obj.enum_array1.should == patterns[6]
202
+ obj.enum_array1_keys = [:entry2]; obj.enum_array1.should == patterns[2]
203
+
204
+ obj.enum_array1_selection = [true, false, true]; obj.enum_array1.should == patterns[5]
205
+ obj.enum_array1_selection = [true, true, false]; obj.enum_array1.should == patterns[6]
206
+ obj.enum_array1_selection = [false, true, false]; obj.enum_array1.should == patterns[2]
207
+
208
+ obj.enum_array1_ids = "1,3"; obj.enum_array1.should == patterns[5]
209
+ obj.enum_array1_ids = "1,2"; obj.enum_array1.should == patterns[6]
210
+ obj.enum_array1_ids = "2"; obj.enum_array1.should == patterns[2]
211
+ end
212
+
213
+ class EnumMock3 < EnumBase
214
+ multi_selectable_attr :enum_array1, :convert_with => :binary_string do
215
+ entry 1, :entry1, "エントリ1"
216
+ entry 2, :entry2, "エントリ2"
217
+ entry 3, :entry3, "エントリ3"
218
+ end
219
+ end
220
+
221
+ class EnumMock3WithEnumArray < EnumBase
222
+ enum_array :enum_array1, :convert_with => :binary_string do
223
+ entry 1, :entry1, "エントリ1"
224
+ entry 2, :entry2, "エントリ2"
225
+ entry 3, :entry3, "エントリ3"
226
+ end
227
+ end
228
+
229
+ it "test_multi_selectable_attr_with_binary_string" do
230
+ expected = (0..7).map{|i| '%-03b' % i} # ["000", "001", "010", "011", "100", "101", "110", "111"]
231
+ assert_enum_class_methods(EnumMock3, :enum_array1)
232
+ assert_multi_enum_instance_methods(EnumMock3.new, expected)
233
+ assert_enum_class_methods(EnumMock3WithEnumArray, :enum_array1)
234
+ assert_multi_enum_instance_methods(EnumMock3WithEnumArray.new, expected)
235
+ end
236
+
237
+ class EnumMock4 < EnumBase
238
+ multi_selectable_attr :enum_array1 do
239
+ entry 1, :entry1, "エントリ1"
240
+ entry 2, :entry2, "エントリ2"
241
+ entry 3, :entry3, "エントリ3"
242
+ end
243
+ end
244
+
245
+ class EnumMock4WithEnumArray < EnumBase
246
+ enum_array :enum_array1 do
247
+ entry 1, :entry1, "エントリ1"
248
+ entry 2, :entry2, "エントリ2"
249
+ entry 3, :entry3, "エントリ3"
250
+ end
251
+ end
252
+
253
+ it "test_multi_selectable_attr2" do
254
+ # [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]]
255
+ expected =
256
+ (0..7).map do |i|
257
+ s = '%03b' % i
258
+ a = s.split('').map{|v| v.to_i}
259
+ ret = []
260
+ a.each_with_index{|val, pos| ret << pos + 1 if val == 1}
261
+ ret
262
+ end
263
+ assert_enum_class_methods(EnumMock4, :enum_array1)
264
+ assert_multi_enum_instance_methods(EnumMock4.new, expected)
265
+ assert_enum_class_methods(EnumMock4WithEnumArray, :enum_array1)
266
+ assert_multi_enum_instance_methods(EnumMock4WithEnumArray.new, expected)
267
+ end
268
+
269
+ class EnumMock5 < EnumBase
270
+ multi_selectable_attr :enum_array1, :convert_with => :comma_string do
271
+ entry 1, :entry1, "エントリ1"
272
+ entry 2, :entry2, "エントリ2"
273
+ entry 3, :entry3, "エントリ3"
274
+ end
275
+ end
276
+
277
+ class EnumMock5WithEnumArray < EnumBase
278
+ enum_array :enum_array1, :convert_with => :comma_string do
279
+ entry 1, :entry1, "エントリ1"
280
+ entry 2, :entry2, "エントリ2"
281
+ entry 3, :entry3, "エントリ3"
282
+ end
283
+ end
284
+
285
+ it "test_multi_selectable_attr_with_comma_string" do
286
+ # ["", "3", "2", "2,3", "1", "1,3", "1,2", "1,2,3"]
287
+ expected =
288
+ (0..7).map do |i|
289
+ s = '%03b' % i
290
+ a = s.split('').map{|v| v.to_i}
291
+ ret = []
292
+ a.each_with_index{|val, pos| ret << pos + 1 if val == 1}
293
+ ret.join(',')
294
+ end
295
+ assert_enum_class_methods(EnumMock5, :enum_array1)
296
+ assert_multi_enum_instance_methods(EnumMock5.new, expected)
297
+ assert_enum_class_methods(EnumMock5WithEnumArray, :enum_array1)
298
+ assert_multi_enum_instance_methods(EnumMock5WithEnumArray.new, expected)
299
+ end
300
+
301
+ class EnumMock6 < EnumBase
302
+ # self.selectable_attr_name_pattern = /(_cd$|_code$|_cds$|_codes$)/
303
+ selectable_attr :category_id do
304
+ entry "01", :category1, "カテゴリ1"
305
+ entry "02", :category2, "カテゴリ2"
306
+ end
307
+ end
308
+
309
+ class EnumMock7 < EnumBase
310
+ self.selectable_attr_name_pattern = /(_cd$|_id$|_cds$|_ids$)/
311
+ selectable_attr :category_id do
312
+ entry "01", :category1, "カテゴリ1"
313
+ entry "02", :category2, "カテゴリ2"
314
+ end
315
+ end
316
+
317
+ it "test_selectable_attr_name_pattern" do
318
+ EnumMock6.selectable_attr_name_pattern.should == /(_cd$|_code$|_cds$|_codes$)/
319
+ EnumMock6.respond_to?(:category_enum).should == false
320
+ EnumMock6.respond_to?(:category_id_enum).should == true
321
+ EnumMock6.new.respond_to?(:category_key).should == false
322
+ EnumMock6.new.respond_to?(:category_id_key).should == true
323
+
324
+ EnumMock7.selectable_attr_name_pattern.should == /(_cd$|_id$|_cds$|_ids$)/
325
+ EnumMock7.respond_to?(:category_enum).should == true
326
+ EnumMock7.respond_to?(:category_id_enum).should == false
327
+ EnumMock7.new.respond_to?(:category_key).should == true
328
+ EnumMock7.new.respond_to?(:category_id_key).should == false
329
+ end
330
+
331
+ end
@@ -0,0 +1,117 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.join(File.dirname(__FILE__), 'spec_helper')
3
+
4
+ describe SelectableAttr::Enum do
5
+
6
+ Enum1 = SelectableAttr::Enum.new do
7
+ entry 1, :book, '書籍'
8
+ entry 2, :dvd, 'DVD'
9
+ entry 3, :cd, 'CD'
10
+ entry 4, :vhs, 'VHS'
11
+ end
12
+
13
+ it "test_define" do
14
+ Enum1[1].id.should == 1
15
+ Enum1[2].id.should == 2
16
+ Enum1[3].id.should == 3
17
+ Enum1[4].id.should == 4
18
+ Enum1[1].key.should == :book
19
+ Enum1[2].key.should == :dvd
20
+ Enum1[3].key.should == :cd
21
+ Enum1[4].key.should == :vhs
22
+ Enum1[1].name.should == '書籍'
23
+ Enum1[2].name.should == 'DVD'
24
+ Enum1[3].name.should == 'CD'
25
+ Enum1[4].name.should == 'VHS'
26
+
27
+ Enum1[:book].id.should == 1
28
+ Enum1[:dvd ].id.should == 2
29
+ Enum1[:cd ].id.should == 3
30
+ Enum1[:vhs ].id.should == 4
31
+ Enum1[:book].key.should == :book
32
+ Enum1[:dvd ].key.should == :dvd
33
+ Enum1[:cd ].key.should == :cd
34
+ Enum1[:vhs ].key.should == :vhs
35
+ Enum1[:book].name.should == '書籍'
36
+ Enum1[:dvd].name.should == 'DVD'
37
+ Enum1[:cd].name.should == 'CD'
38
+ Enum1[:vhs].name.should == 'VHS'
39
+
40
+ Enum1.values.should == [['書籍', 1], ['DVD', 2], ['CD', 3], ['VHS', 4]]
41
+ Enum1.values(:name, :id).should == [['書籍', 1], ['DVD', 2], ['CD', 3], ['VHS', 4]]
42
+ Enum1.values(:name, :key).should == [['書籍', :book], ['DVD', :dvd], ['CD', :cd], ['VHS', :vhs]]
43
+ end
44
+
45
+ InetAccess = SelectableAttr::Enum.new do
46
+ entry 1, :email, 'Eメール', :protocol => 'mailto:'
47
+ entry 2, :website, 'ウェブサイト', :protocol => 'http://'
48
+ entry 3, :ftp, 'FTP', :protocol => 'ftp://'
49
+ end
50
+
51
+ it "test_define_with_options" do
52
+ InetAccess[1].id.should == 1
53
+ InetAccess[2].id.should == 2
54
+ InetAccess[3].id.should == 3
55
+ InetAccess[1].key.should == :email
56
+ InetAccess[2].key.should == :website
57
+ InetAccess[3].key.should == :ftp
58
+
59
+
60
+ InetAccess[1].name.should == 'Eメール'
61
+ InetAccess[2].name.should == 'ウェブサイト'
62
+ InetAccess[3].name.should == 'FTP'
63
+ InetAccess[1][:protocol].should == 'mailto:'
64
+ InetAccess[2][:protocol].should == 'http://'
65
+ InetAccess[3][:protocol].should == 'ftp://'
66
+
67
+ InetAccess[9].id.should be_nil
68
+ InetAccess[9].key.should be_nil
69
+ InetAccess[9].name.should be_nil
70
+ InetAccess[9][:protocol].should be_nil
71
+ InetAccess[9][:xxxx].should be_nil
72
+ end
73
+
74
+ it "test_get_by_option" do
75
+ InetAccess[:protocol => 'mailto:'].should == InetAccess[1]
76
+ InetAccess[:protocol => 'http://'].should == InetAccess[2]
77
+ InetAccess[:protocol => 'ftp://'].should == InetAccess[3]
78
+ end
79
+
80
+ it "test_null?" do
81
+ InetAccess[1].null?.should == false
82
+ InetAccess[2].null?.should == false
83
+ InetAccess[3].null?.should == false
84
+ InetAccess[9].null?.should == true
85
+ InetAccess[:protocol => 'mailto:'].null?.should == false
86
+ InetAccess[:protocol => 'http://'].null?.should == false
87
+ InetAccess[:protocol => 'ftp://'].null?.should == false
88
+ InetAccess[:protocol => 'svn://'].null?.should == true
89
+ end
90
+
91
+ it "test_null_object?" do
92
+ InetAccess[1].null_object?.should == false
93
+ InetAccess[2].null_object?.should == false
94
+ InetAccess[3].null_object?.should == false
95
+ InetAccess[9].null_object?.should == true
96
+ InetAccess[:protocol => 'mailto:'].null_object?.should == false
97
+ InetAccess[:protocol => 'http://'].null_object?.should == false
98
+ InetAccess[:protocol => 'ftp://'].null_object?.should == false
99
+ InetAccess[:protocol => 'svn://'].null_object?.should == true
100
+ end
101
+
102
+ it "test_to_hash_array" do
103
+ Enum1.to_hash_array.should == [
104
+ {:id => 1, :key => :book, :name => '書籍'},
105
+ {:id => 2, :key => :dvd, :name => 'DVD'},
106
+ {:id => 3, :key => :cd, :name => 'CD'},
107
+ {:id => 4, :key => :vhs, :name => 'VHS'}
108
+ ]
109
+
110
+ InetAccess.to_hash_array.should == [
111
+ {:id => 1, :key => :email, :name => 'Eメール', :protocol => 'mailto:'},
112
+ {:id => 2, :key => :website, :name => 'ウェブサイト', :protocol => 'http://'},
113
+ {:id => 3, :key => :ftp, :name => 'FTP', :protocol => 'ftp://'}
114
+ ]
115
+ end
116
+
117
+ end
@@ -0,0 +1,11 @@
1
+ $KCODE='u'
2
+
3
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require File.join(File.dirname(__FILE__), '..', 'init')
5
+
6
+ def assert_hash(expected, actual)
7
+ keys = (expected.keys + actual.keys).uniq
8
+ keys.each do |key|
9
+ assert_equal expected[key], actual[key], "unmatch value for #{key.inspect}"
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: akm-selectable_attr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Takeshi Akima
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-27 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: selectable_attr generates extra methods dynamically for attribute which has options
17
+ email: akima@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - VERSION.yml
26
+ - lib/selectable_attr
27
+ - lib/selectable_attr/base.rb
28
+ - lib/selectable_attr/enum.rb
29
+ - lib/selectable_attr/version.rb
30
+ - lib/selectable_attr.rb
31
+ - spec/selectable_attr_base_alias_spec.rb
32
+ - spec/selectable_attr_enum_spec.rb
33
+ - spec/spec_helper.rb
34
+ has_rdoc: true
35
+ homepage: http://github.com/akm/selectable_attr/
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --inline-source
39
+ - --charset=UTF-8
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: selectable_attr generates extra methods dynamically
61
+ test_files: []
62
+