Module: Emu
- Defined in:
- lib/emu.rb,
lib/emu/result.rb,
lib/emu/decoder.rb,
lib/emu/version.rb
Defined Under Namespace
Classes: DecodeError, Decoder, Err, Ok
Constant Summary collapse
- VERSION =
"0.2.0"
Class Method Summary collapse
-
.array(decoder) ⇒ Emu::Decoder<b>
Creates a decoder which decodes the values of an array and returns the decoded array.
-
.at_index(index, decoder) ⇒ Emu::Decoder<b>
Creates a decoder which extracts the value of an array at the given index.
-
.boolean ⇒ Emu::Decoder<TrueClass|FalseClass>
Creates a decoder which only accepts booleans.
-
.fail(message) ⇒ Emu::Decoder<Void>
Creates a decoder which always fails with the provided message.
-
.float ⇒ Emu::Decoder<Float>
Creates a decoder which only accepts floats (including integers).
-
.from_key(key, decoder) ⇒ Emu::Decoder<b>
Creates a decoder which extracts the value of a hash map according to the given key.
-
.from_key_or_nil(key, decoder) ⇒ Emu::Decoder<b, NilClass>
Creates a decoder which extracts the value of a hash map according to the given key.
-
.integer ⇒ Emu::Decoder<Integer>
Creates a decoder which only accepts integers.
-
.lazy ⇒ Emu::Decoder<a>
Wraps a decoder
d
in a lazily evaluated block to avoid endless recursion when dealing with recursive data structures. -
.map_n(*decoders) {|a, b, c, ...| ... } ⇒ Emu::Decoder<z>
Builds a decoder out of
n
decoders and maps a function over the result of the passed in decoders. -
.match(constant) ⇒ Emu::Decoder<a>
Returns a decoder which succeeds if the input value matches
constant
. -
.nil ⇒ Emu::Decoder<NilClass>
Creates a decoder which only accepts ‘nil` values.
-
.raw ⇒ Emu::Decoder<a>
Creates a decoder which always succeeds and yields the input.
-
.str_to_bool ⇒ Emu::Decoder<TrueClass|FalseClass>
Creates a decoder which converts a string to a boolean (
true
,false
) value. -
.str_to_float ⇒ Emu::Decoder<Float>
Creates a decoder which converts a string to a float.
-
.str_to_int ⇒ Emu::Decoder<Integer>
Creates a decoder which converts a string to an integer.
-
.string ⇒ Emu::Decoder<String>
Creates a decoder which only accepts strings.
-
.succeed(value) ⇒ Emu::Decoder<a>
Creates a decoder which always succeeds with the provided value.
Class Method Details
.array(decoder) ⇒ Emu::Decoder<b>
Creates a decoder which decodes the values of an array and returns the decoded array.
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/emu.rb', line 275 def self.array(decoder) Decoder.new do |array| next Err.new("`#{array.inspect}` is not an Array") unless array.is_a?(Array) result = [] i = 0 error_found = nil while i < array.length && !error_found r = decoder.run(array[i]) if r.error? error_found = r else result << r.unwrap end i += 1 end if error_found error_found else Ok.new(result) end end end |
.at_index(index, decoder) ⇒ Emu::Decoder<b>
Creates a decoder which extracts the value of an array at the given index.
258 259 260 261 262 263 264 |
# File 'lib/emu.rb', line 258 def self.at_index(index, decoder) Decoder.new do |array| next Err.new("`#{array.inspect}` doesn't contain index `#{index.inspect}`") if index >= array.length decoder.run(array[index]) end end |
.boolean ⇒ Emu::Decoder<TrueClass|FalseClass>
Creates a decoder which only accepts booleans.
100 101 102 103 104 105 106 |
# File 'lib/emu.rb', line 100 def self.boolean Decoder.new do |b| next Err.new("`#{b.inspect}` is not a Boolean") unless b.is_a?(TrueClass) || b.is_a?(FalseClass) Ok.new(b) end end |
.fail(message) ⇒ Emu::Decoder<Void>
Creates a decoder which always fails with the provided message.
170 171 172 173 174 |
# File 'lib/emu.rb', line 170 def self.fail() Decoder.new do |_| Err.new() end end |
.float ⇒ Emu::Decoder<Float>
Creates a decoder which only accepts floats (including integers). Integers are converted to floats because the result type should be uniform.
84 85 86 87 88 89 90 |
# File 'lib/emu.rb', line 84 def self.float Decoder.new do |i| next Err.new("`#{i.inspect}` is not a Float") unless i.is_a?(Float) || i.is_a?(Integer) Ok.new(i.to_f) end end |
.from_key(key, decoder) ⇒ Emu::Decoder<b>
Creates a decoder which extracts the value of a hash map according to the given key.
213 214 215 216 217 218 219 220 |
# File 'lib/emu.rb', line 213 def self.from_key(key, decoder) Decoder.new do |hash| next Err.new("`#{hash.inspect}` is not a Hash") unless hash.respond_to?(:has_key?) && hash.respond_to?(:fetch) next Err.new("`#{hash.inspect}` doesn't contain key `#{key.inspect}`") unless hash.has_key?(key) decoder.run(hash.fetch(key)) end end |
.from_key_or_nil(key, decoder) ⇒ Emu::Decoder<b, NilClass>
Creates a decoder which extracts the value of a hash map according to the given key. If the key cannot be found nil
will be returned.
Note: If a key can be found, but the value decoder fails from_key_or_nil
will fail as well. This is usually what you want, because this indicates bad data you don’t know how to handle.
237 238 239 240 241 242 243 244 245 246 |
# File 'lib/emu.rb', line 237 def self.from_key_or_nil(key, decoder) Decoder.new do |hash| next Err.new("`#{hash.inspect}` is not a Hash") unless hash.respond_to?(:has_key?) && hash.respond_to?(:fetch) if hash.has_key?(key) decoder.run(hash.fetch(key)) else Ok.new(nil) end end end |
.integer ⇒ Emu::Decoder<Integer>
Creates a decoder which only accepts integers.
68 69 70 71 72 73 74 |
# File 'lib/emu.rb', line 68 def self.integer Decoder.new do |i| next Err.new("`#{i.inspect}` is not an Integer") unless i.is_a?(Integer) Ok.new(i) end end |
.lazy ⇒ Emu::Decoder<a>
Wraps a decoder d
in a lazily evaluated block to avoid endless recursion when dealing with recursive data structures. Emu.lazy { d }.run!
behaves exactly like d.run!
.
349 350 351 352 353 354 |
# File 'lib/emu.rb', line 349 def self.lazy Decoder.new do |input| inner_decoder = yield inner_decoder.run(input) end end |
.map_n(*decoders) {|a, b, c, ...| ... } ⇒ Emu::Decoder<z>
Builds a decoder out of n
decoders and maps a function over the result of the passed in decoders. For the block to be called all decoders must succeed.
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/emu.rb', line 316 def self.map_n(*decoders, &block) raise "decoder count must match argument count of provided block" unless decoders.size == block.arity Decoder.new do |input| results = decoders.map do |c| c.run(input) end first_error = results.find(&:error?) if first_error first_error else Ok.new(block.call(*results.map(&:unwrap))) end end end |
.match(constant) ⇒ Emu::Decoder<a>
Returns a decoder which succeeds if the input value matches constant
. If the decoder succeeds it resolves to the input value. #== is used for comparision, no type checks are performed.
185 186 187 188 189 |
# File 'lib/emu.rb', line 185 def self.match(constant) Decoder.new do |s| s == constant ? Ok.new(s) : Err.new("Input `#{s.inspect}` doesn't match expected value `#{constant.inspect}`") end end |
.nil ⇒ Emu::Decoder<NilClass>
Creates a decoder which only accepts ‘nil` values.
197 198 199 200 201 |
# File 'lib/emu.rb', line 197 def self.nil Decoder.new do |s| s.nil? ? Ok.new(s) : Err.new("`#{s.inspect}` isn't `nil`") end end |
.raw ⇒ Emu::Decoder<a>
Creates a decoder which always succeeds and yields the input.
This might be useful if you don’t care about the exact shape of of your data and don’t have a need to inspect it (e.g. some binary data).
146 147 148 149 150 |
# File 'lib/emu.rb', line 146 def self.raw Decoder.new do |s| Ok.new(s) end end |
.str_to_bool ⇒ Emu::Decoder<TrueClass|FalseClass>
Creates a decoder which converts a string to a boolean (true
, false
) value.
"0"
and "false"
are considered false
, "1"
and "true"
are considered true
. Trying to decode any other value will fail.
122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/emu.rb', line 122 def self.str_to_bool Decoder.new do |s| next Err.new("`#{s.inspect}` is not a String") unless s.is_a?(String) if s == "true" || s == "1" Ok.new(true) elsif s == "false" || s == "0" Ok.new(false) else Err.new("`#{s.inspect}` can't be converted to a Boolean") end end end |
.str_to_float ⇒ Emu::Decoder<Float>
Creates a decoder which converts a string to a float. It uses Float
for the conversion.
50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/emu.rb', line 50 def self.str_to_float Decoder.new do |s| next Err.new("`#{s.inspect}` is not a String") unless s.is_a?(String) begin Ok.new(Float(s)) rescue TypeError, ArgumentError Err.new("`#{s.inspect}` can't be converted to a Float") end end end |
.str_to_int ⇒ Emu::Decoder<Integer>
Creates a decoder which converts a string to an integer. It uses Integer
for the conversion.
29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/emu.rb', line 29 def self.str_to_int Decoder.new do |s| next Err.new("`#{s.inspect}` is not a String") unless s.is_a?(String) begin Ok.new(Integer(s)) rescue TypeError, ArgumentError Err.new("`#{s.inspect}` can't be converted to an Integer") end end end |
.string ⇒ Emu::Decoder<String>
Creates a decoder which only accepts strings.
13 14 15 16 17 18 19 |
# File 'lib/emu.rb', line 13 def self.string Decoder.new do |s| next Err.new("`#{s.inspect}` is not a String") unless s.is_a?(String) Ok.new(s) end end |
.succeed(value) ⇒ Emu::Decoder<a>
Creates a decoder which always succeeds with the provided value.
158 159 160 161 162 |
# File 'lib/emu.rb', line 158 def self.succeed(value) Decoder.new do |_| Ok.new(value) end end |