Class: Amazon::SDB::Multimap
- Inherits:
-
Object
- Object
- Amazon::SDB::Multimap
- Includes:
- Enumerable
- Defined in:
- lib/amazon_sdb/multimap.rb
Overview
A multimap is like a hash or set, but it only requires that key/value pair is unique (the same key may have multiple values). Multimaps may be created by the user to send into Amazon sdb or they may be read back from sdb as the attributes for an object.
For your convenience, multimap’s initializer can take several types of input parameters:
-
A hash of key/value pairs (for when you want keys to be unique)
-
An array of key/value pairs represented as 2-member arrays
-
Another multimap
-
Or nothing at all (an empty set)
Class Method Summary collapse
-
.numeric(float, size, precision) ⇒ Object
To be honest, floats are difficult for sdb.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Shortcut for #get.
-
#[]=(key, value) ⇒ Object
Shortcut for put(key, value, :replace => true).
- #clear_size! ⇒ Object
- #coerce(value) ⇒ Object
-
#each ⇒ Object
Support for Enumerable.
-
#each_pair ⇒ Object
Yields each key/value pair as separate parameters to the block.
-
#each_pair_with_index ⇒ Object
Yields each pair as separate key/value plus an index.
- #from_sdb(values) ⇒ Object
-
#get(key_arg, options = {}) ⇒ Object
Returns all the values that match a key.
-
#initialize(init = nil) ⇒ Multimap
constructor
A new instance of Multimap.
- #method_missing(method_symbol, *args) ⇒ Object
-
#put(key_arg, value, options = {}) ⇒ Object
Save a key/value attribute into the multimap.
- #sdb_key_escape(key) ⇒ Object
- #sdb_value_escape(value) ⇒ Object
-
#size ⇒ Object
Returns the size of the multimap.
- #string_escape(str) ⇒ Object
-
#to_a ⇒ Object
Returns the multimap as an array of 2-item arrays, one for each key-value pair.
-
#to_h ⇒ Object
Returns the multimap as a hash.
-
#to_sdb(options = {}) ⇒ Object
Outputs a multimap to sdb using Amazon’s query-string notation (and doing auto-conversions of int and date values).
Constructor Details
#initialize(init = nil) ⇒ Multimap
Returns a new instance of Multimap.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/amazon_sdb/multimap.rb', line 24 def initialize(init=nil) @mset = {} clear_size! if init.nil? # do nothing elsif init.is_a? Hash init.each {|k, v| put(k, v) } elsif init.is_a? Array # load from array if init.any? {|v| ! v.is_a? Array || v.size != 2 } raise ArgumentError, "Array must be of key/value pairs only" end init.each {|v| self.put(v[0], v[1])} elsif init.is_a? Multimap @mset = init.mset.dup else raise ArgumentError, "Wrong type passed as initializer" end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_symbol, *args) ⇒ Object
261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/amazon_sdb/multimap.rb', line 261 def method_missing(method_symbol, *args) name = method_symbol.to_s if name =~ /^\w+$/ if @mset.key? name get(name) else super end else super end end |
Class Method Details
.numeric(float, size, precision) ⇒ Object
To be honest, floats are difficult for sdb. In order to work with lexical comparisons, you need to save floats as strings padded to the same size. The problem is, automatic conversion can run afoul of rounding errors if it has a larger precision than the original float, so for the short term I’ve provided the numeric helper method for saving floats as strings into the multimap (when read back from sdb they will still be converted from floats). To use, specify the precision you want to represent as well as the total size (pick something large like 32 to be safe)
20 21 22 |
# File 'lib/amazon_sdb/multimap.rb', line 20 def self.numeric(float, size, precision) sprintf "%0#{size}.#{precision}f", float end |
Instance Method Details
#[](key) ⇒ Object
Shortcut for #get
115 116 117 |
# File 'lib/amazon_sdb/multimap.rb', line 115 def [](key) get(key) end |
#[]=(key, value) ⇒ Object
Shortcut for put(key, value, :replace => true)
121 122 123 |
# File 'lib/amazon_sdb/multimap.rb', line 121 def []=(key, value) put(key, value, :replace => true) end |
#clear_size! ⇒ Object
47 48 49 |
# File 'lib/amazon_sdb/multimap.rb', line 47 def clear_size! @size = nil end |
#coerce(value) ⇒ Object
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/amazon_sdb/multimap.rb', line 209 def coerce(value) case value when 'true' true when 'false' false when /^0+\d+$/ value.to_i when /^0+\d*.\d+$/ value.to_f when /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}([+\-]\d{2}:\d{2})?)?$/ Time.parse(value) else value end end |
#each ⇒ Object
Support for Enumerable. Yields each key/value pair as an array of 2 members.
127 128 129 130 131 132 133 |
# File 'lib/amazon_sdb/multimap.rb', line 127 def each @mset.each_pair do |key, group| group.to_a.each do |value| yield [key, value] end end end |
#each_pair ⇒ Object
Yields each key/value pair as separate parameters to the block.
137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/amazon_sdb/multimap.rb', line 137 def each_pair @mset.each_pair do |key, group| case group when Array group.each do |value| yield key, value end else yield key, group end end end |
#each_pair_with_index ⇒ Object
Yields each pair as separate key/value plus an index.
152 153 154 155 156 157 158 |
# File 'lib/amazon_sdb/multimap.rb', line 152 def each_pair_with_index index = 0 self.each_pair do |key, value| yield key, value, index index += 1 end end |
#from_sdb(values) ⇒ Object
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/amazon_sdb/multimap.rb', line 226 def from_sdb(values) @mset = {} clear_size! if values.nil? # do nothing elsif values.is_a? Array # load from array if values.any? {|v| ! v.is_a? Array || v.size != 2 } raise ArgumentError, "Array must be of key/value pairs only" end values.each do |v| self.put(v[0], v[1], :before_cast => true) self.put(v[0], coerce(v[1])) end else raise ArgumentError, "Wrong type passed as initializer" end end |
#get(key_arg, options = {}) ⇒ Object
Returns all the values that match a key. Normally, if there is only a single value entry returns just the value, with an array for multiple values, and nil for no match. If you want to always return an array, pass in :force_array => true
in the options
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/amazon_sdb/multimap.rb', line 96 def get(key_arg, = {}) key = key_arg.to_s if [:before_cast] key = "#{key}_before_cast" end k = @mset[key] if [:force_array] return [] if k.nil? k.to_a else k end end |
#put(key_arg, value, options = {}) ⇒ Object
Save a key/value attribute into the multimap. Takes additional options
-
:replace => true
remove any attributes with the same key before insert (otherwise, appends)
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/amazon_sdb/multimap.rb', line 71 def put(key_arg, value, = {}) key = key_arg.to_s if [:before_cast] key = "#{key}_before_cast" end k = @mset[key] clear_size! if k.nil? || [:replace] @mset[key] = value else if @mset[key].is_a? Array @mset[key] << value else @mset[key] = [@mset[key], value] end end end |
#sdb_key_escape(key) ⇒ Object
164 165 166 167 168 169 170 171 |
# File 'lib/amazon_sdb/multimap.rb', line 164 def sdb_key_escape(key) case key when String string_escape(key) else key.to_s end end |
#sdb_value_escape(value) ⇒ Object
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/amazon_sdb/multimap.rb', line 173 def sdb_value_escape(value) case value when TrueClass "true" when FalseClass "false" when Fixnum sprintf("%0#{Base.number_padding}d", value) when Float numeric(value, Base.number_padding, Base.float_precision) when String string_escape(value) when Time value.iso8601 else string_escape(value.to_s) end end |
#size ⇒ Object
Returns the size of the multimap. This is the total number of key/value pairs in it.
53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/amazon_sdb/multimap.rb', line 53 def size if @size.nil? @size = @mset.inject(0) do |total, pair| value = pair[1] if value.is_a? Array total + value.size else total + 1 end end end @size end |
#string_escape(str) ⇒ Object
160 161 162 |
# File 'lib/amazon_sdb/multimap.rb', line 160 def string_escape(str) str.gsub('\\') {|c| "#{c}#{c}"}.gsub('\'') {|c| "\\#{c}"} end |
#to_a ⇒ Object
Returns the multimap as an array of 2-item arrays, one for each key-value pair
249 250 251 252 253 |
# File 'lib/amazon_sdb/multimap.rb', line 249 def to_a out = [] each_pair {|k, v| out << [k, v] } out end |
#to_h ⇒ Object
Returns the multimap as a hash. In cases where there are multiple values for a key, it puts all the values into an array.
257 258 259 |
# File 'lib/amazon_sdb/multimap.rb', line 257 def to_h @mset.dup end |
#to_sdb(options = {}) ⇒ Object
Outputs a multimap to sdb using Amazon’s query-string notation (and doing auto-conversions of int and date values)
194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/amazon_sdb/multimap.rb', line 194 def to_sdb(={}) out = {} self.each_pair_with_index do |key, value, index| out["Attribute.#{index}.Name"] = sdb_key_escape(key) out["Attribute.#{index}.Value"] = sdb_value_escape(value) if .key?(:replace) && ([:replace] == :all || [*[:replace]].find {|x| x.to_s == key }) out["Attribute.#{index}.Replace"] = "true" end end out end |