super_map 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,91 @@
1
+ Introduction
2
+ ============
3
+
4
+ SuperMap is a two-way, multi-purpose ordered hash with some additional
5
+ capabilities (additional attributes, translations). It is meant to be used with
6
+ Rails, but it doesn't depend on them, so someone might find it useful in other
7
+ environments.
8
+
9
+ Ever had an enumerable field in a model that would map to a select box in a
10
+ form? Wanted some nice way to operate it as symbol in the code, integer in
11
+ database and label in views? That's where SuperMap comes into play.
12
+
13
+ Example
14
+ =======
15
+
16
+ Let's say there is a Payment model with two enumerable fields:
17
+ - payment_type: Cash In, Cash Out, Bonus, Commission
18
+ - payment_method: Paypal, Bank Transfer, Manual
19
+
20
+ We define 2 SuperMaps:
21
+
22
+ class Payment
23
+
24
+ PAYMENT_TYPES = SuperMap.new(
25
+ [:cash_in, 1, { :label => "Cash In", :tax => 0.01 }],
26
+ [:cash_out, 2, { :label => "Cash Out", :tax => 0.02 }],
27
+ [:bonus, 123, { :label => "Bonus Payment", :tax => 0.03 }],
28
+ [:commission, 384728, { :tax => 0.01 }]
29
+ )
30
+
31
+ PAYMENT_METHODS = SuperMap.new(
32
+ [:paypal, 1, { :label => "Paid via Paypal", :favorite => true }],
33
+ [:bank_transfer, 2],
34
+ [:manual, 3],
35
+ :translation_scope => "activerecord.models.payment.payment_methods"
36
+ )
37
+
38
+ end
39
+
40
+ PAYMENT_TYPES defines options for payment_type field. Some labels are set
41
+ explicitly, for other options key.titleize will be used implicitly. We also
42
+ defined a custom attribute :tax, which can be accessed using SuperMap#attribute
43
+ method.
44
+
45
+ PAYMENT_METHODS define translation_scope, which means labels will be present in
46
+ translations file (I18n). :paypal will have custom label, which overrides
47
+ translations.
48
+
49
+ We can than use those SuperMaps to handle attributes for Payment instances:
50
+
51
+ payment = Payment.new( :payment_method => 1 )
52
+ payment.payment_type = PAYMENT_TYPES[:cash_out] # Set to 2
53
+ label = PAYMENT_TYPES.label( payment.payment_type ) # "Cash Out"
54
+ label = PAYMENT_TYPES.label( :bonus ) # "Bonus Payment"
55
+ value = PAYMENT_METHODS[payment.payment_method] # 1
56
+ tax = PAYMENT_TYPES.attribute( payment.payment_type, :tax ) # 0.02
57
+ tax = PAYMENT_TYPES.attribute( :bonus, :tax ) # 0.03
58
+
59
+ We can also use
60
+
61
+ PAYMENT_TYPES.labeled_values
62
+
63
+ to get the form directly suitable to pass options to f.select helper method.
64
+
65
+
66
+
67
+ To make things even simpler, we can use super_mapped_attr:
68
+
69
+ class Payment
70
+ ... # SuperMaps declarations
71
+
72
+ super_mapped_attr :payment_type, PAYMENT_TYPES
73
+ super_mapped_attr :payment_method, PAYMENT_METHODS
74
+
75
+ end
76
+
77
+ Now a whole lot of new methods comes into play:
78
+
79
+ p = Payment.new( :payment_method => 1 )
80
+ p.payment_method_label # "Paid via Paypal"
81
+ p.payment_method_key # :paypal
82
+ p.payment_method_attr( :favorite ) # true
83
+ p.payment_method_key = :manual
84
+ p.payment_method # 3
85
+
86
+ Thanks
87
+ ======
88
+
89
+ Many thanks go to Stefan Nothegger and Sharewise project
90
+ (http://www.sharewise.com), where the original idea and large parts of the code
91
+ originate from.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ PKG_FILES = FileList[
7
+ '[a-zA-Z]*',
8
+ 'generators/**/*',
9
+ 'lib/**/*',
10
+ 'rails/**/*',
11
+ 'tasks/**/*',
12
+ 'test/**/*'
13
+ ]
14
+
15
+ spec = Gem::Specification.new do |s|
16
+ s.name = "super_map"
17
+ s.version = "1.0.0"
18
+ s.author = "Marek Janukowicz"
19
+ s.email = "marek@janukowicz.net"
20
+ s.homepage = ""
21
+ s.platform = Gem::Platform::RUBY
22
+ s.summary = "Swiss army knife of enum-based attributes"
23
+ s.files = PKG_FILES.to_a
24
+ s.require_path = "lib"
25
+ s.has_rdoc = false
26
+ s.extra_rdoc_files = ["README"]
27
+ s.add_dependency "i18n", ">= 0.4.0"
28
+ end
29
+
30
+ desc 'Create gem'
31
+ Rake::GemPackageTask.new(spec) do |pkg|
32
+ pkg.gem_spec = spec
33
+ end
34
+
35
+ desc 'Run tests'
36
+ Rake::TestTask.new(:test) do |t|
37
+ t.libs << 'lib'
38
+ t.libs << 'test'
39
+ t.pattern = 'test/**/*_test.rb'
40
+ t.verbose = true
41
+ end
42
+
43
+ desc 'Generate RDoc documentation'
44
+ Rake::RDocTask.new(:rdoc) do |rdoc|
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = 'SuperMap'
47
+ rdoc.options << '--line-numbers' << '--inline-source'
48
+ rdoc.rdoc_files.include('README')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
51
+
@@ -0,0 +1,18 @@
1
+ class String
2
+
3
+ # Some poor man's implementations of Rails String extensions in case we can't
4
+ # use those
5
+
6
+ unless method_defined?( :pluralize )
7
+ def pluralize
8
+ [-1,1] == "s" ? self : self + "s"
9
+ end
10
+ end
11
+
12
+ unless method_defined?( :titleize )
13
+ def titleize
14
+ gsub( "_", " " ).capitalize
15
+ end
16
+ end
17
+
18
+ end
data/lib/super_map.rb ADDED
@@ -0,0 +1,240 @@
1
+ begin
2
+ require 'i18n'
3
+ rescue LoadError
4
+ nil # We don't really need I18n, but if it's not present, :translation_scope can't be used
5
+ end
6
+ require 'string_extensions'
7
+ require 'super_mapped_attr'
8
+
9
+ # Two-way hash with additional custom data, autogenerated labels and human
10
+ # readable strings - swiss army knife of collections :) Especially suited for
11
+ # emulating enumerable fields in database.
12
+ #
13
+ # Immutable objects - most element accessing methods are cached, there is no
14
+ # designed way to get "raw" elements. If you need another (similar) SuperMap,
15
+ # use +/- methods to get a new one. The methods accessing Element objects
16
+ # directly (each, first, last) should be used with awareness of aforementioned
17
+ # limitations. Usually (when not enumerating over all elements) you would use
18
+ # key/value/attribute etc. accessing methods, eg. map.key( value ), map.value(
19
+ # key ), map.attribute( key, attr_name ).
20
+ class SuperMap
21
+
22
+ include Enumerable
23
+
24
+ class Error < ::StandardError; end
25
+ class UnknownKey < Error; end
26
+ class UnknownValue < Error; end
27
+
28
+ attr_reader :options
29
+
30
+ # Map element. Rarely used directly, usually the map object itself is queried
31
+ # for element properties.
32
+ class Element
33
+
34
+ # Key (symbol) - used for getting element by symbolic name
35
+ attr_reader :key
36
+ # Value - corresponds to key, usually an integer or string to be stored in
37
+ # database
38
+ attr_reader :value
39
+ # Additional information about element, eg. time frame, additional
40
+ # attributes. Also contains the label if not default or provided by I18n.
41
+ # Takes the form of a Hash.
42
+ attr_reader :attributes
43
+
44
+ # Creates new list element.
45
+ def initialize( list, key, value, attributes = {} )
46
+ raise "Key must be a Symbol" unless key.is_a? Symbol
47
+ raise "Value must NOT be a Symbol" if value.is_a? Symbol
48
+ @list, @key, @value, @attributes = list, key, value, attributes
49
+ end
50
+
51
+ def label
52
+ translated_attribute( :label )
53
+ end
54
+
55
+ def description
56
+ translated_attribute( :description )
57
+ end
58
+
59
+ def method_missing( name, *args )
60
+ return @attributes[name] || super( name, *args )
61
+ end
62
+
63
+ # Returns all initializing arguments as passed to SuperMap constructor - this
64
+ # method is used for cloning to another SuperMap.
65
+ def to_a
66
+ [key, value, attributes]
67
+ end
68
+
69
+ protected
70
+ def translated_attribute( attr )
71
+ return @attributes[attr.to_sym] if @attributes[attr.to_sym]
72
+ if @list.options[:translation_scope]
73
+ return I18n.t( @key, :scope => [@list.options[:translation_scope], attr.to_s.pluralize], :default => @key.to_s.titleize )
74
+ else
75
+ return @key.to_s.titleize
76
+ end
77
+ end
78
+ end
79
+
80
+ # Creates new list.
81
+ # Elements - arrays with arguments for Element.new (key, value, attributes)
82
+ # Options (optional last argument):
83
+ # :translation_scope - where to find translated labels/descriptions
84
+ # :unknown_value_fatal - whether to raise an exception when accessing
85
+ # objects via unknown value (unknown key is always fatal)
86
+ #
87
+ # Example:
88
+ #
89
+ # class User
90
+ # KINDS = SuperMap.new(
91
+ # [:member, 1, "Casual member", { :min_age => 18.years, :welcome_string => "Welcome member!" }],
92
+ # [:admin, 2, "Administrator", { :min_age => nil, :welcome_string => "Hello admin!" }],
93
+ # :translation_scope => "user.kinds"
94
+ # )
95
+ # end
96
+ # TODO: enforce key/value uniqueness
97
+ def initialize( *elements )
98
+ if elements.last.is_a?( Hash )
99
+ @options = elements.pop
100
+ else
101
+ @options = {}
102
+ end
103
+ # TODO: handle old syntax here (elements passed in an Array and not as
104
+ # separate arguments)
105
+ @elements = elements.collect { |elem| Element.new( *([self] + elem) ) }
106
+ # We store elements in 2 Hashes, because we need to be able to lookup both
107
+ # on key (when in need of value) and on value (when in need of a key/label
108
+ # corresponding to the value fetched from database)
109
+ @elements_by_key = @elements.inject( {} ) { |hash, elem| hash[elem.key] = elem; hash }
110
+ @elements_by_value = @elements.inject( {} ) { |hash, elem| hash[elem.value] = elem; hash }
111
+ end
112
+
113
+ # Elements in the form of array (original form as passed as argument)
114
+ def element_array
115
+ @element_array ||= @elements.collect { |elem| elem.to_a }
116
+ end
117
+
118
+ # Iterates over the list return Element objects - required for Enumerable
119
+ # methods to work.
120
+ def each
121
+ @elements.each { |elem| yield elem }
122
+ end
123
+
124
+ def first
125
+ @elements.first
126
+ end
127
+
128
+ def last
129
+ @elements.last
130
+ end
131
+
132
+ # Removes element(s) corresponding to key(s) (returns new map)
133
+ # TODO: this should also handle another map as argument
134
+ def - (*keys)
135
+ keys = keys.flatten
136
+ new_elements = select { |el| !keys.include?( el.key ) }.collect { |elem| elem.to_a }
137
+ return self.class.new( *(new_elements + [@options]) )
138
+ end
139
+
140
+ # Adds elements from another map or array (returns new map)
141
+ def + (map_or_array)
142
+ if map_or_array.is_a?( self.class ) # Map
143
+ return self.class.new( *(element_array + map_or_array.element_array + [@options]) )
144
+ else # Array
145
+ return self.class.new( *(element_array + map_or_array + [@options]) )
146
+ end
147
+ end
148
+
149
+ # Returns array of label/value pairs, useful for SELECT tags - eg.
150
+ # f.select( :kind, User::KINDS.labeled_values )
151
+ def labeled_values
152
+ @labeled_values ||= @elements.collect { |elem| [elem.label, elem.value] }
153
+ end
154
+
155
+ # Array of values
156
+ def values
157
+ @values ||= @elements.collect { |elem| elem.value }
158
+ end
159
+
160
+ # Array of keys
161
+ def keys
162
+ @keys ||= @elements.collect { |elem| elem.key }
163
+ end
164
+
165
+ # Number of elements
166
+ def size
167
+ @size ||= @elements.size
168
+ end
169
+
170
+ # Key for given value - raises exception on unknown value depending on
171
+ # :unknown_value_fatal setting
172
+ def key(value)
173
+ elem = element_by_value_or_key( value )
174
+ # Exception handling in element_by_value_or_key
175
+ return elem ? elem.key : nil
176
+ end
177
+
178
+ # Value for given key - raises exception when key does not exist (to avoid bogus
179
+ # error messages when called with non-existant key)
180
+ def value(key)
181
+ # Exception handling in element_by_value_or_key
182
+ element_by_value_or_key( key ).value
183
+ end
184
+
185
+ # Label for given value or key (it is determined basing on argument type -
186
+ # keys must be Symbols while values can not)
187
+ def label( value_or_key )
188
+ elem = element_by_value_or_key( value_or_key )
189
+ return elem ? elem.label : nil
190
+ end
191
+
192
+ # Attribute field for given value or key
193
+ def attribute(value_or_key, attr_key)
194
+ elem = element_by_value_or_key( value_or_key )
195
+ if elem
196
+ return elem.attributes[attr_key]
197
+ else
198
+ return nil
199
+ end
200
+ end
201
+
202
+ # Returns true if given key exists
203
+ def has_key?( key )
204
+ @elements_by_key.has_key?( key )
205
+ end
206
+
207
+ # Returns true if given value exists
208
+ def has_value?( value )
209
+ @elements_by_value.has_key?( value )
210
+ end
211
+
212
+ # Returns true if given key (Symbol) or value (non-Symbol) exists
213
+ def include?( value_or_key )
214
+ return has_key?( value_or_key ) || has_value?( value_or_key )
215
+ end
216
+
217
+ alias_method '[]', :value
218
+
219
+ def element_by_value_or_key( value_or_key )
220
+
221
+ if value_or_key.is_a?( Symbol ) # Key
222
+ elem = @elements_by_key[value_or_key]
223
+ if elem
224
+ return elem
225
+ else
226
+ raise UnknownKey, "Unknown key #{value_or_key}"
227
+ end
228
+ else
229
+ elem = @elements_by_value[value_or_key]
230
+ return elem if elem
231
+ if @options[:unknown_value_fatal]
232
+ raise UnknownValue, "Unknown value #{value_or_key}"
233
+ else
234
+ return nil
235
+ end
236
+ end
237
+
238
+ end
239
+
240
+ end
@@ -0,0 +1,65 @@
1
+ module SuperMappedAttr
2
+
3
+ def self.included( klass )
4
+ klass.extend( ClassMethods )
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ # Makes the attribute mapped by a SuperMap, eg.
10
+ # class User
11
+ #
12
+ # super_mapped_attr :kind, SuperMap.new(
13
+ # [:member, 1, "Casual member", { :min_age => 18.years, :welcome_string => "Welcome member!" }],
14
+ # [:admin, 2, "Administrator", { :min_age => nil, :welcome_string => "Hello admin!" }]
15
+ # )
16
+ #
17
+ # end
18
+ #
19
+ # u = User.new( :kind => 1 )
20
+ #
21
+ # u.kind_label # "Casual member"
22
+ #
23
+ # u.kind_key # :member
24
+ #
25
+ # u.kind_attr( :member, :min_age ) # 18.years
26
+ #
27
+ # u.kind_key = :admin
28
+ #
29
+ # u.kind # 2
30
+ #
31
+ # User.kinds # #SuperMap:0x....
32
+ def super_mapped_attr( attr, map, options = {} )
33
+
34
+ if respond_to?( :validates_inclusion_of )
35
+ validates_inclusion_of attr, { :in => map.values }.merge( options )
36
+ end
37
+
38
+ define_method( "#{attr}_key" ) do
39
+ map.key(send(attr))
40
+ end
41
+
42
+ define_method( "#{attr}_key=" ) do |key|
43
+ send( "#{attr}=", map.value( key ))
44
+ end
45
+
46
+ define_method( "#{attr}_label" ) do
47
+ map.label(send(attr))
48
+ end
49
+
50
+ define_method( "#{attr}_attr" ) do |key|
51
+ map.attribute(send(attr), key)
52
+ end
53
+
54
+ (class << self; self; end).instance_eval do
55
+ define_method attr.to_s.pluralize, lambda { map }
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+
62
+ # Include in ActiveRecord models by default
63
+ if defined?( ActiveRecord::Base )
64
+ ActiveRecord::Base.send( :include, SuperMappedAttr )
65
+ end
data/test/en.yml ADDED
@@ -0,0 +1,9 @@
1
+ en:
2
+ enum:
3
+ map:
4
+ labels:
5
+ one: NEVER DISPLAY
6
+ two: Two label
7
+ descriptions:
8
+ one: NEVER DISPLAY
9
+ two: Two description
@@ -0,0 +1,144 @@
1
+ require 'test/unit'
2
+ require 'super_map'
3
+ I18n.load_path << File.join( File.dirname( __FILE__ ), "en.yml" )
4
+
5
+ class SuperMapTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @color_map = SuperMap.new(
9
+ [:red, 5, { :rgb => "AA0000", :priority => 3 }],
10
+ [:green, 2, { :rgb => "00AA00", :priority => 12 }],
11
+ [:blue, 123, { :rgb => "0000AA", :preference => 8823 }],
12
+ :translation_scope => "color.map"
13
+ )
14
+ end
15
+
16
+ def test_element_label_and_description
17
+ enum_map = SuperMap.new(
18
+ [:one, 1, { :label => "One label", :description => "One description" }],
19
+ [:two, 2],
20
+ :translation_scope => "enum.map"
21
+ )
22
+ assert_equal 'One label', enum_map.first.label
23
+ assert_equal 'One description', enum_map.first.description
24
+ assert_equal 'Two label', enum_map.last.label
25
+ assert_equal 'Two description', enum_map.last.description
26
+ end
27
+
28
+ def test_element_attributes
29
+ red = @color_map.first
30
+ assert_equal "AA0000", red.rgb
31
+ assert_equal 3, red.priority
32
+ blue = @color_map.last
33
+ assert_equal "0000AA", blue.rgb
34
+ assert_equal 8823, blue.preference
35
+ end
36
+
37
+ def test_enumeration
38
+ keys = []
39
+ @color_map.each do |elem|
40
+ keys << elem.key
41
+ end
42
+ assert_equal [:red, :green, :blue], keys
43
+ elems = @color_map.select { |elem| elem.value < 10 }.collect { |elem| elem.key }
44
+ assert_equal [:red, :green], elems
45
+ elems = @color_map.sort_by { |elem| elem.value }.collect { |elem| elem.key }
46
+ assert_equal [:green, :red, :blue], elems
47
+ assert_equal :red, @color_map.first.key
48
+ assert_equal :blue, @color_map.last.key
49
+ end
50
+
51
+ def test_subtraction
52
+ # Existing key
53
+ map = @color_map - :red
54
+ assert_equal [:green, :blue], map.keys
55
+ # Non-existant key
56
+ map = @color_map - :yellow
57
+ assert_equal [:red, :green, :blue], map.keys
58
+ # multiple keys
59
+ map = @color_map - [:green, :blue]
60
+ assert_equal [:red], map.keys
61
+ assert_equal @color_map.options[:translation_scope], map.options[:translation_scope]
62
+ end
63
+
64
+ def test_addition
65
+ # Single element
66
+ map = @color_map + [[:yellow, 3]]
67
+ assert_equal [:red, :green, :blue, :yellow], map.keys
68
+ # Multiple elements
69
+ map = @color_map + [[:yellow, 3], [:brown, 4]]
70
+ assert_equal [:red, :green, :blue, :yellow, :brown], map.keys
71
+ # Add another SuperMap
72
+ second_map = SuperMap.new( [:yellow, 3], [:brown, 4, {:label => "Blah"}], :translation_scope => "another.scope" )
73
+ map = @color_map + second_map
74
+ assert_equal [:red, :green, :blue, :yellow, :brown], map.keys
75
+ assert_equal @color_map.options[:translation_scope], map.options[:translation_scope]
76
+ # Original array is untouched
77
+ assert_equal [:red, :green, :blue], @color_map.keys
78
+ end
79
+
80
+ def test_labeled_values
81
+ assert_equal [["Red", 5], ["Green", 2], ["Blue", 123]], @color_map.labeled_values
82
+ end
83
+
84
+ def test_values
85
+ assert_equal [5,2,123], @color_map.values
86
+ end
87
+
88
+ def test_keys
89
+ assert_equal [:red, :green, :blue], @color_map.keys
90
+ end
91
+
92
+ def test_size
93
+ assert_equal 3, @color_map.size
94
+ end
95
+
96
+ def test_key
97
+ assert_equal :red, @color_map.key( 5 )
98
+ assert_equal :green, @color_map.key( 2 )
99
+ assert_nil @color_map.key( 3 )
100
+ @color_map.options[:unknown_value_fatal] = true
101
+ assert_raise( SuperMap::UnknownValue ) { @color_map.key( 3 ) }
102
+ end
103
+
104
+ def test_value
105
+ assert_equal 5, @color_map.value( :red )
106
+ assert_equal 123, @color_map.value( :blue )
107
+ assert_raise( SuperMap::UnknownKey ) { @color_map.value( :yellow ) }
108
+ end
109
+
110
+ def test_label
111
+ assert_equal "Blue", @color_map.label( :blue )
112
+ assert_equal "Red", @color_map.label( 5 )
113
+ end
114
+
115
+ def test_attribute
116
+ assert_equal 8823, @color_map.attribute( :blue, :preference )
117
+ assert_equal nil, @color_map.attribute( :blue, :priority ) # This attribute doesn't exist for this element
118
+ assert_equal 3, @color_map.attribute( :red, :priority )
119
+ end
120
+
121
+ def test_has_key
122
+ assert @color_map.has_key?( :green )
123
+ assert !@color_map.has_key?( :yellow )
124
+ end
125
+
126
+ def test_has_value
127
+ assert @color_map.has_value?( 2 )
128
+ assert !@color_map.has_value?( 55 )
129
+ end
130
+
131
+ def test_include
132
+ assert @color_map.include?( :blue )
133
+ assert !@color_map.include?( :yellow )
134
+ assert @color_map.include?( 123 )
135
+ assert !@color_map.include?( 122 )
136
+ end
137
+
138
+ # [] is an alias to #value
139
+ def test_brackets
140
+ assert_equal 5, @color_map[ :red ]
141
+ assert_equal 123, @color_map[ :blue ]
142
+ assert_raise( SuperMap::UnknownKey ) { @color_map[ :yellow ] }
143
+ end
144
+ end
@@ -0,0 +1,52 @@
1
+ require 'test/unit'
2
+ require 'super_map'
3
+ I18n.load_path << File.join( File.dirname( __FILE__ ), "en.yml" )
4
+
5
+ class SuperMappedAttrTest < Test::Unit::TestCase
6
+
7
+ class User
8
+
9
+ include SuperMappedAttr
10
+
11
+ attr_accessor :color
12
+ super_mapped_attr :color, SuperMap.new(
13
+ [:red, 5, { :rgb => "AA0000", :priority => 3 }],
14
+ [:green, 2, { :rgb => "00AA00", :priority => 12 }],
15
+ [:blue, 123, { :rgb => "0000AA", :preference => 8823 }],
16
+ :translation_scope => "color.map"
17
+ )
18
+
19
+ end
20
+
21
+ def setup
22
+ @user = User.new
23
+ end
24
+
25
+ def test_label
26
+ @user.color_key = :red
27
+ assert_equal "Red", @user.color_label
28
+ assert_raise( SuperMap::UnknownKey ) do
29
+ @user.color_key = :asdjfkajf
30
+ end
31
+ end
32
+
33
+ def test_key
34
+ # This test DOES make sense - color_key getter and setter both pass through
35
+ # underlying SuperMap
36
+ @user.color_key = :red
37
+ assert_equal :red, @user.color_key
38
+ end
39
+
40
+ def test_attr
41
+ @user.color_key = :blue
42
+ assert_nil @user.color_attr( :priority )
43
+ assert_equal "0000AA", @user.color_attr( :rgb )
44
+ end
45
+
46
+ def test_map_accessor
47
+ assert_equal "Green", User.colors.label( :green )
48
+ assert_equal 123, User.colors[ :blue ]
49
+ assert_equal 12, User.colors.attribute( :green, :priority )
50
+ end
51
+
52
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: super_map
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Marek Janukowicz
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-08 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: i18n
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 0
32
+ - 4
33
+ - 0
34
+ version: 0.4.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description:
38
+ email: marek@janukowicz.net
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - README
45
+ files:
46
+ - Rakefile
47
+ - README
48
+ - lib/super_map.rb
49
+ - lib/super_mapped_attr.rb
50
+ - lib/string_extensions.rb
51
+ - test/super_mapped_attr_test.rb
52
+ - test/en.yml
53
+ - test/super_map_test.rb
54
+ has_rdoc: true
55
+ homepage: ""
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.3.7
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Swiss army knife of enum-based attributes
88
+ test_files: []
89
+