emu 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 80e58ff8f1a11f69581ad745836d6a0af99aae8e46b51615d154f3114730c250
4
+ data.tar.gz: 942c66c48155f9298b0069df9a35dfa5ea96f0895e2cc8403a5f4441e8778e57
5
+ SHA512:
6
+ metadata.gz: aecb49f1cd38e186d347cdbe4991a091e4e8318f48d87bd553b1c8da7aae61277bedbab55fcbc78fc9da876b96af974f44e95783f202cc0a776313646f1e4e9f
7
+ data.tar.gz: 4146a50e2062ae91ceb7a403e5510602a4725fa4c4320fa82fa6f40c41cc268bd2eb294732f3e724efd637df9767dd02b771a92b7e8b7038054eb032065fffc1
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in emu.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 12.3"
9
+ gem "minitest", "~> 5.0"
10
+ # Used for documentation output of minitest
11
+ gem "minitest-reporters", "~> 1.3"
12
+ gem "yard", "~> 0.9"
13
+
14
+ gem 'simplecov', group: :test, require: false
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Tim Habermaas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # Emu
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/emu`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'emu'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install emu
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/emu.
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "emu"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,39 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "emu/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "emu"
7
+ spec.version = Emu::VERSION
8
+ spec.authors = ["Tim Habermaas"]
9
+ spec.email = ["emu@timhabermaas.com"]
10
+
11
+ spec.summary = %q{Composable decoding library in the spirit of Json.Decode from Elm}
12
+ spec.description = %q{Emu acts as a replacement for ad-hoc type coercions and strong_parameters.}
13
+ spec.homepage = "https://github.com/timhabermaas/emu.git"
14
+ spec.license = "MIT"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
20
+
21
+ #spec.metadata["homepage_uri"] = spec.homepage
22
+ #spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
23
+ #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
24
+ else
25
+ #raise "RubyGems 2.0 or newer is required to protect against " \
26
+ # "public gem pushes."
27
+ end
28
+
29
+ # Specify which files should be added to the gem when it is released.
30
+ spec.files = ["Gemfile", "LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup", "emu.gemspec",
31
+ "lib/emu.rb", "lib/emu/version.rb", "lib/emu/decoder.rb", "lib/emu/result.rb"]
32
+ spec.bindir = "exe"
33
+ spec.executables = []
34
+ spec.require_paths = ["lib"]
35
+
36
+ spec.add_development_dependency "bundler", "~> 1.17"
37
+ #spec.add_development_dependency "rake", "~> 10.0"
38
+ #spec.add_development_dependency "minitest", "~> 5.0"
39
+ end
@@ -0,0 +1,119 @@
1
+ require "emu/version"
2
+ require "emu/result"
3
+ require "emu/decoder"
4
+
5
+ module Emu
6
+
7
+ # Creates a decoder which only accepts strings.
8
+ #
9
+ # @example
10
+ # Emu.string.run!("2") # => "2"
11
+ # Emu.string.run!(2) # => raise DecodeError, "`2` is not a String"
12
+ # @return [Emu::Decoder<String>]
13
+ def self.string
14
+ Decoder.new do |s|
15
+ next Err.new("`#{s.inspect}` is not a String") unless s.is_a?(String)
16
+
17
+ Ok.new(s)
18
+ end
19
+ end
20
+
21
+ def self.str_to_int
22
+ Decoder.new do |s|
23
+ next Err.new("`#{s.inspect}` is not a String") unless s.is_a?(String)
24
+
25
+ begin
26
+ Ok.new(Integer(s))
27
+ rescue TypeError, ArgumentError
28
+ Err.new("`#{s.inspect}` can't be converted to an integer")
29
+ end
30
+ end
31
+ end
32
+
33
+ def self.integer
34
+ Decoder.new do |i|
35
+ next Err.new("`#{i.inspect}` is not an Integer") unless i.is_a?(Integer)
36
+
37
+ Ok.new(i)
38
+ end
39
+ end
40
+
41
+ def self.boolean
42
+ Decoder.new do |b|
43
+ next Err.new("`#{b.inspect}` is not a Boolean") unless b.is_a?(TrueClass) || b.is_a?(FalseClass)
44
+
45
+ Ok.new(b)
46
+ end
47
+ end
48
+
49
+ def self.str_to_bool
50
+ Decoder.new do |s|
51
+ next Err.new("`#{s.inspect}` is not a String") unless s.is_a?(String)
52
+
53
+ if s == "true" || s == "1"
54
+ Ok.new(true)
55
+ elsif s == "false" || s == "0"
56
+ Ok.new(false)
57
+ else
58
+ Err.new("`#{s.inspect}` can not be converted to a Boolean")
59
+ end
60
+ end
61
+ end
62
+
63
+ def self.id
64
+ Decoder.new do |s|
65
+ Ok.new(s)
66
+ end
67
+ end
68
+
69
+ def self.succeed(v)
70
+ Decoder.new do |_|
71
+ Ok.new(v)
72
+ end
73
+ end
74
+
75
+ def self.fail(e)
76
+ Decoder.new do |_|
77
+ Err.new(e)
78
+ end
79
+ end
80
+
81
+ # Returns a decoder which succeeds if the input value matches ++constant++.
82
+ # #== is used for comparision, no type checks are performed.
83
+ #
84
+ # @example
85
+ # Emu.match(42).run!(42) # => 42
86
+ # Emu.match(42).run!(41) # => raise DecodeError, "`41` doesn't match `42`"
87
+ # @param constant [Object] the value to match against
88
+ # @return [Emu::Decoder<Object>]
89
+ def self.match(constant)
90
+ Decoder.new do |s|
91
+ s == constant ? Ok.new(s) : Err.new("`#{s.inspect}` doesn't match `#{constant.inspect}`")
92
+ end
93
+ end
94
+
95
+ def self.from_key(key, decoder)
96
+ Decoder.new do |hash|
97
+ next Err.new("'#{hash}' doesn't contain key '#{key}'") unless hash.has_key?(key)
98
+
99
+ decoder.run(hash.fetch(key))
100
+ end
101
+ end
102
+
103
+ def self.map_n(*decoders, &block)
104
+ raise "decoder count must match argument count of provided block" unless decoders.size == block.arity
105
+
106
+ Decoder.new do |input|
107
+ results = decoders.map do |c|
108
+ c.run(input)
109
+ end
110
+
111
+ first_error = results.find(&:error?)
112
+ if first_error
113
+ first_error
114
+ else
115
+ Ok.new(block.call(*results.map(&:unwrap)))
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,57 @@
1
+ module Emu
2
+ class DecodeError < StandardError
3
+ end
4
+
5
+ class Decoder
6
+ def initialize(&block)
7
+ @f = block
8
+ end
9
+
10
+ def run(value)
11
+ @f.call(value)
12
+ end
13
+
14
+ def run!(value)
15
+ result = run(value)
16
+ if result.error?
17
+ raise DecodeError, result.unwrap_err
18
+ else
19
+ result.unwrap
20
+ end
21
+ end
22
+
23
+ def fmap
24
+ Decoder.new do |input|
25
+ result = run(input)
26
+ if result.error?
27
+ result
28
+ else
29
+ Ok.new(yield result.unwrap)
30
+ end
31
+ end
32
+ end
33
+
34
+ def then
35
+ Decoder.new do |input|
36
+ run(input).then do |result|
37
+ (yield result).run(input)
38
+ end
39
+ end
40
+ end
41
+
42
+ def |(decoder)
43
+ Decoder.new do |input|
44
+ result = run(input)
45
+ if result.error?
46
+ decoder.run(input)
47
+ else
48
+ result
49
+ end
50
+ end
51
+ end
52
+
53
+ def >(value)
54
+ fmap { |_| value }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,53 @@
1
+ module Emu
2
+ class Err
3
+ def initialize(error)
4
+ @error = error
5
+ end
6
+
7
+ def to_s
8
+ "Err(#{@error})"
9
+ end
10
+
11
+ def then
12
+ self
13
+ end
14
+
15
+ def unwrap
16
+ raise "can't unwrap Err(#{@error.inspect})"
17
+ end
18
+
19
+ def unwrap_err
20
+ @error
21
+ end
22
+
23
+ def error?
24
+ true
25
+ end
26
+ end
27
+
28
+ class Ok
29
+ def initialize(value)
30
+ @value = value
31
+ end
32
+
33
+ def to_s
34
+ "Ok(#{@value})"
35
+ end
36
+
37
+ def then
38
+ yield @value
39
+ end
40
+
41
+ def unwrap
42
+ @value
43
+ end
44
+
45
+ def unwrap_err
46
+ raise "can't unwrap_err Ok(#{@value.inspect})"
47
+ end
48
+
49
+ def error?
50
+ false
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Emu
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: emu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tim Habermaas
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-12-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ description: Emu acts as a replacement for ad-hoc type coercions and strong_parameters.
28
+ email:
29
+ - emu@timhabermaas.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - LICENSE.txt
36
+ - README.md
37
+ - Rakefile
38
+ - bin/console
39
+ - bin/setup
40
+ - emu.gemspec
41
+ - lib/emu.rb
42
+ - lib/emu/decoder.rb
43
+ - lib/emu/result.rb
44
+ - lib/emu/version.rb
45
+ homepage: https://github.com/timhabermaas/emu.git
46
+ licenses:
47
+ - MIT
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 3.0.0.beta1
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Composable decoding library in the spirit of Json.Decode from Elm
69
+ test_files: []