akm-selectable_attr 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+