Class: SuperMap

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/super_map.rb

Overview

Two-way hash with additional custom data, autogenerated labels and human readable strings - swiss army knife of collections :) Especially suited for emulating enumerable fields in database.

Immutable objects - most element accessing methods are cached, there is no designed way to get “raw” elements. If you need another (similar) SuperMap, use +/- methods to get a new one. The methods accessing Element objects directly (each, first, last) should be used with awareness of aforementioned limitations. Usually (when not enumerating over all elements) you would use key/value/attribute etc. accessing methods, eg. map.key( value ), map.value( key ), map.attribute( key, attr_name ).

Defined Under Namespace

Classes: Element, Error, UnknownKey, UnknownValue

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*elements) ⇒ SuperMap

Creates new list. Elements - arrays with arguments for Element.new (key, value, attributes)

key - a Symbol representing the element (to be used within the app to
  access this element)
value - integer, string or whatever: real, meaningful value of element
attributes - any additional attributes this element might need in form of
  a Hash

Options (optional last argument):

:translation_scope - where to find translated labels/descriptions
:unknown_value_fatal - whether to raise an exception when accessing
  objects via unknown value (unknown key is always fatal)

Example:

class User

KINDS = SuperMap.new(
  [:member, 1, "Casual member", { :min_age => 18.years, :welcome_string => "Welcome member!" }],
  [:admin, 2, "Administrator", { :min_age => nil, :welcome_string => "Hello admin!" }],
  :translation_scope => "user.kinds"
)

end TODO: enforce key/value uniqueness



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/super_map.rb', line 104

def initialize( *elements )
  if elements.last.is_a?( Hash )
    @options = elements.pop
  else
    @options = {}
  end
  # TODO: handle old syntax here (elements passed in an Array and not as
  # separate arguments)
  @elements = elements.collect { |elem| Element.new( *([self] + elem) ) }
  # We store elements in 2 Hashes, because we need to be able to lookup both
  # on key (when in need of value) and on value (when in need of a key/label
  # corresponding to the value fetched from database)
  @elements_by_key = @elements.inject( {} ) { |hash, elem| hash[elem.key] = elem; hash }
  @elements_by_value = @elements.inject( {} ) { |hash, elem| hash[elem.value] = elem; hash }
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



30
31
32
# File 'lib/super_map.rb', line 30

def options
  @options
end

Instance Method Details

#+(map_or_array) ⇒ Object

Adds elements from another map or array (returns new map)



148
149
150
151
152
153
154
# File 'lib/super_map.rb', line 148

def + (map_or_array)
  if map_or_array.is_a?( self.class ) # Map
    return self.class.new( *(element_array + map_or_array.element_array + [@options]) )
  else # Array
    return self.class.new( *(element_array + map_or_array + [@options]) )
  end
end

#-(*keys) ⇒ Object

Removes element(s) corresponding to key(s) (returns new map) TODO: this should also handle another map as argument



141
142
143
144
145
# File 'lib/super_map.rb', line 141

def - (*keys)
  keys = keys.flatten
  new_elements = select { |el| !keys.include?( el.key ) }.collect { |elem| elem.to_a }
  return self.class.new( *(new_elements + [@options]) )
end

#attribute(value_or_key, attr_key) ⇒ Object

Attribute field for given value or key



200
201
202
203
204
205
206
207
# File 'lib/super_map.rb', line 200

def attribute(value_or_key, attr_key)
  elem = element_by_value_or_key( value_or_key )
  if elem
    return elem.attributes[attr_key]
  else
    return nil
  end
end

#eachObject

Iterates over the list return Element objects - required for Enumerable methods to work.



127
128
129
# File 'lib/super_map.rb', line 127

def each
  @elements.each { |elem| yield elem }
end

#element_arrayObject

Elements in the form of array (original form as passed as argument)



121
122
123
# File 'lib/super_map.rb', line 121

def element_array
  @elements.collect { |elem| elem.to_a }
end

#element_by_value_or_key(value_or_key) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/super_map.rb', line 226

def element_by_value_or_key( value_or_key )
 
  if value_or_key.is_a?( Symbol ) # Key
    elem = @elements_by_key[value_or_key]
    if elem
      return elem
    else
      raise UnknownKey, "Unknown key #{value_or_key}"
    end
  else
    elem = @elements_by_value[value_or_key]
    return elem if elem
    if @options[:unknown_value_fatal]
      raise UnknownValue, "Unknown value #{value_or_key}"
    else
      return nil
    end
  end
    
end

#firstObject



131
132
133
# File 'lib/super_map.rb', line 131

def first
  @elements.first
end

#has_key?(key) ⇒ Boolean

Returns true if given key exists

Returns:

  • (Boolean)


210
211
212
# File 'lib/super_map.rb', line 210

def has_key?( key )
  @elements_by_key.has_key?( key )
end

#has_value?(value) ⇒ Boolean

Returns true if given value exists

Returns:

  • (Boolean)


215
216
217
# File 'lib/super_map.rb', line 215

def has_value?( value )
  @elements_by_value.has_key?( value )
end

#include?(value_or_key) ⇒ Boolean

Returns true if given key (Symbol) or value (non-Symbol) exists

Returns:

  • (Boolean)


220
221
222
# File 'lib/super_map.rb', line 220

def include?( value_or_key )
  return has_key?( value_or_key ) || has_value?( value_or_key )
end

#key(value) ⇒ Object

Key for given value - raises exception on unknown value depending on :unknown_value_fatal setting



179
180
181
182
183
# File 'lib/super_map.rb', line 179

def key(value)
  elem = element_by_value_or_key( value )
  # Exception handling in element_by_value_or_key
  return elem ? elem.key : nil
end

#keysObject

Array of keys



168
169
170
# File 'lib/super_map.rb', line 168

def keys
  @elements.collect { |elem| elem.key }
end

#label(value_or_key) ⇒ Object

Label for given value or key (it is determined basing on argument type - keys must be Symbols while values can not)



194
195
196
197
# File 'lib/super_map.rb', line 194

def label( value_or_key )
  elem = element_by_value_or_key( value_or_key )
  return elem ? elem.label : nil
end

#labeled_valuesObject

Returns array of label/value pairs, useful for SELECT tags - eg. f.select( :kind, User::KINDS.labeled_values )



158
159
160
# File 'lib/super_map.rb', line 158

def labeled_values
  @elements.collect { |elem| [elem.label, elem.value] }
end

#lastObject



135
136
137
# File 'lib/super_map.rb', line 135

def last
  @elements.last
end

#sizeObject

Number of elements



173
174
175
# File 'lib/super_map.rb', line 173

def size
  @elements.size
end

#value(key) ⇒ Object Also known as: []

Value for given key - raises exception when key does not exist (to avoid bogus error messages when called with non-existant key)



187
188
189
190
# File 'lib/super_map.rb', line 187

def value(key)
  # Exception handling in element_by_value_or_key
  element_by_value_or_key( key ).value
end

#valuesObject

Array of values



163
164
165
# File 'lib/super_map.rb', line 163

def values
  @elements.collect { |elem| elem.value }
end