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.1.0"

Class Method Summary collapse

Class Method Details

.booleanObject



41
42
43
44
45
46
47
# File 'lib/emu.rb', line 41

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(e) ⇒ Object



75
76
77
78
79
# File 'lib/emu.rb', line 75

def self.fail(e)
  Decoder.new do |_|
    Err.new(e)
  end
end

.from_key(key, decoder) ⇒ Object



95
96
97
98
99
100
101
# File 'lib/emu.rb', line 95

def self.from_key(key, decoder)
  Decoder.new do |hash|
    next Err.new("'#{hash}' doesn't contain key '#{key}'") unless hash.has_key?(key)

    decoder.run(hash.fetch(key))
  end
end

.idObject



63
64
65
66
67
# File 'lib/emu.rb', line 63

def self.id
  Decoder.new do |s|
    Ok.new(s)
  end
end

.integerObject



33
34
35
36
37
38
39
# File 'lib/emu.rb', line 33

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

.map_n(*decoders, &block) ⇒ Object



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

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<Object>

Returns a decoder which succeeds if the input value matches constant. #== is used for comparision, no type checks are performed.

Examples:

Emu.match(42).run!(42) # => 42
Emu.match(42).run!(41) # => raise DecodeError, "`41` doesn't match `42`"

Parameters:

  • constant (Object)

    the value to match against

Returns:



89
90
91
92
93
# File 'lib/emu.rb', line 89

def self.match(constant)
  Decoder.new do |s|
    s == constant ? Ok.new(s) : Err.new("`#{s.inspect}` doesn't match `#{constant.inspect}`")
  end
end

.str_to_boolObject



49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/emu.rb', line 49

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 not be converted to a Boolean")
    end
  end
end

.str_to_intObject



21
22
23
24
25
26
27
28
29
30
31
# File 'lib/emu.rb', line 21

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

.stringEmu::Decoder<String>

Creates a decoder which only accepts strings.

Examples:

Emu.string.run!("2") # => "2"
Emu.string.run!(2) # => raise DecodeError, "`2` is not a String"

Returns:



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(v) ⇒ Object



69
70
71
72
73
# File 'lib/emu.rb', line 69

def self.succeed(v)
  Decoder.new do |_|
    Ok.new(v)
  end
end