ya_enum 0.1.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 22dc33f31631f9421973d4cbf319e8fa23dd876d0e10003ac8d367399280161c
4
+ data.tar.gz: 3484534969f78c6106116b005a2c23b28842fa58b5b9bc8927d6af645099bb45
5
+ SHA512:
6
+ metadata.gz: 8365627b9e546a61bef20649b475e5a7d07fcf3004fc48812399543dd5c5997fa4827f2430ce58dc27ca66c2cc0115ff54dec4d3b8ec59b5521ee9f2b254fa6e
7
+ data.tar.gz: 5bcde80986b96a157c52cff7ff076882d7d559916af017cb5f341828cb6f1d7bca055073d0d06d166a3230d5cf091023458d2d28449c0e459ff2383f3dc92f92
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
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 ya_enum.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ya_enum (0.1.1)
5
+ takes_macro (>= 1.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ minitest (5.11.3)
11
+ rake (10.5.0)
12
+ takes_macro (1.0.0)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ bundler (~> 1.16)
19
+ minitest (~> 5.0)
20
+ rake (~> 10.0)
21
+ ya_enum!
22
+
23
+ BUNDLED WITH
24
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # YaEnum
2
+
3
+ **NOTE:** This gem is not stable yet. Use at your own risk.
4
+
5
+ Easily define enums in Ruby that aren't just symbols. Your enums can have associated values and methods. Imagine if Ruby had Rust's or Swift's enums.
6
+
7
+ ## Installation
8
+
9
+ Since this isn't published on rubygems.org yet, you'll have to
10
+
11
+ 1. Download the repo
12
+ 2. `bundle install`
13
+ 3. `rake install`
14
+
15
+ ## Usage
16
+
17
+ Basic API for defining enums:
18
+
19
+ ```ruby
20
+ module Colors
21
+ extend YaEnum
22
+
23
+ variant :Red do
24
+ def rgb
25
+ [255, 0, 0]
26
+ end
27
+ end
28
+
29
+ variant :Blue do
30
+ def rgb
31
+ [0, 0, 255]
32
+ end
33
+ end
34
+ end
35
+
36
+ p Colors::Red.rgb # => [255, 0, 0]
37
+ p Colors::Blue.rgb # => [0, 0, 255]
38
+ ```
39
+
40
+ You can also define methods outside the variants, which will be inherited by each variant
41
+
42
+ ```ruby
43
+ module Colors
44
+ extend YaEnum
45
+
46
+ variant :Red do
47
+ def rgb
48
+ [max, 0, 0]
49
+ end
50
+ end
51
+
52
+ variant :Blue do
53
+ def rgb
54
+ [0, 0, max]
55
+ end
56
+ end
57
+
58
+ def max
59
+ 255
60
+ end
61
+ end
62
+
63
+ p Colors::Blue.rgb # => [0, 0, 255]
64
+ p Colors::Blue.max # 255
65
+ ```
66
+
67
+ Enums can also have associated values:
68
+
69
+ ```ruby
70
+ module Colors
71
+ extend YaEnum
72
+
73
+ variant :Red, [:max] do
74
+ def rgb
75
+ [max, 0, 0]
76
+ end
77
+ end
78
+
79
+ variant :Blue, [:max] do
80
+ def rgb
81
+ [0, 0, max]
82
+ end
83
+ end
84
+ end
85
+
86
+ p Colors::Red.new(max: 255).rgb # => [255, 0, 0]
87
+ p Colors::Blue.new(max: 255).rgb # => [0, 0, 255]
88
+ ```
89
+
90
+ Enums can also be used in `case` statements:
91
+
92
+ ```ruby
93
+ module Colors
94
+ extend YaEnum
95
+
96
+ variant :Red, [:max]
97
+ variant :Blue, [:max]
98
+ end
99
+
100
+ color = Colors::Red.new(max: 255)
101
+
102
+ case color
103
+ when Colors::Red
104
+ puts "red!"
105
+ when Colors::Blue
106
+ puts "blue!"
107
+ else
108
+ raise "Unknown case"
109
+ end # => red!
110
+ ```
111
+
112
+ If you forget to implement a method for a variant you'll get an exception:
113
+
114
+ ```ruby
115
+ module Colors
116
+ extend YaEnum
117
+
118
+ variant :Red, [:max] do
119
+ def rgb
120
+ [max, 0, 0]
121
+ end
122
+ end
123
+
124
+ variant :Blue, [:max] do
125
+ end
126
+ end
127
+
128
+ # Traceback (most recent call last):
129
+ # 5: from readme.rb:3:in `<main>'
130
+ # 4: from readme.rb:12:in `<module:Colors>'
131
+ # 3: from .../ya_enum/lib/ya_enum.rb:35:in `variant'
132
+ # 2: from .../ya_enum/lib/ya_enum.rb:49:in `ensure_all_methods_defined_for_each_variant!'
133
+ # 1: from .../ya_enum/lib/ya_enum.rb:49:in `each'
134
+ # .../ya_enum/lib/ya_enum.rb:54:in `block in ensure_all_methods_defined_for_each_variant!': Variant Blue is missing the following methods: (YaEnum::MissingMethods)
135
+ # rgb
136
+ ```
137
+
138
+ You can also use the `.case` method to ensure you're handling all cases
139
+
140
+ ```ruby
141
+ module Colors
142
+ extend YaEnum
143
+
144
+ variant :Red
145
+ variant :Blue
146
+ end
147
+
148
+ color = Colors::Red
149
+
150
+ Colors.case(color) do
151
+ on(Colors::Red) do
152
+ puts "red!"
153
+ end
154
+
155
+ on(Colors::Blue) do
156
+ puts "blue!"
157
+ end
158
+ end
159
+ ```
160
+
161
+ This is different from a normal `case` statement because you'll get an `YaEnum::Matcher::NonExhaustiveMatch` exception if you forget to handle a case:
162
+
163
+ ```ruby
164
+ module Colors
165
+ extend YaEnum
166
+
167
+ variant :Red
168
+ variant :Blue
169
+ end
170
+
171
+ color = Colors::Red
172
+
173
+ Colors.case(color) do
174
+ on(Colors::Red) do
175
+ puts "red!"
176
+ end
177
+ end
178
+
179
+ # Traceback (most recent call last):
180
+ # 5: from readme.rb:12:in `<main>'
181
+ # 4: from .../ya_enum/lib/ya_enum.rb:42:in `case'
182
+ # 3: from .../ya_enum/lib/ya_enum/matcher.rb:13:in `match_on'
183
+ # 2: from .../ya_enum/lib/ya_enum/matcher.rb:31:in `ensure_all_variants_handled!'
184
+ # 1: from .../ya_enum/lib/ya_enum/matcher.rb:31:in `each'
185
+ # .../ya_enum/lib/ya_enum/matcher.rb:33:in `block in ensure_all_variants_handled!': Variant Blue is not handled (YaEnum::Matcher::NonExhaustiveMatch)
186
+ ```
187
+
188
+ ## Development
189
+
190
+ 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.
191
+
192
+ 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).
193
+
194
+ ## Contributing
195
+
196
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tonsser/ya_enum
data/Rakefile ADDED
@@ -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
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ya_enum"
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__)
data/bin/setup ADDED
@@ -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,41 @@
1
+ module YaEnum
2
+ class Matcher
3
+ def initialize(all_variants:)
4
+ @cases = []
5
+ @all_variants = all_variants
6
+ end
7
+
8
+ def on(a_case, &block)
9
+ cases << [a_case, block]
10
+ end
11
+
12
+ def match_on(variant)
13
+ ensure_all_variants_handled!
14
+
15
+ match = cases.detect do |a_case, _block|
16
+ a_case === variant
17
+ end
18
+
19
+ if match
20
+ match.last.call
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :cases, :all_variants
27
+
28
+ def ensure_all_variants_handled!
29
+ variants_handled = cases.map(&:first)
30
+
31
+ all_variants.each do |name, variant|
32
+ next if variants_handled.include?(variant)
33
+ raise NonExhaustiveMatch, <<~EOS
34
+ Variant #{name} is not handled
35
+ EOS
36
+ end
37
+ end
38
+
39
+ class NonExhaustiveMatch < StandardError; end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module YaEnum
2
+ VERSION = "0.1.1"
3
+ end
data/lib/ya_enum.rb ADDED
@@ -0,0 +1,79 @@
1
+ require "takes_macro"
2
+ require "ya_enum/version"
3
+ require "ya_enum/matcher"
4
+
5
+ module YaEnum
6
+ attr_accessor :variants
7
+
8
+ def variant(name, associated_values = [], &block)
9
+ if variants.nil?
10
+ self.variants = []
11
+ end
12
+
13
+ enum = Class.new do
14
+ extend TakesMacro
15
+ takes associated_values.map { |value| :"#{value}!" }
16
+
17
+ def self.===(other)
18
+ self == other || super
19
+ end
20
+ end
21
+
22
+ if block
23
+ if associated_values.any?
24
+ enum.class_eval(&block)
25
+ enum.include(self)
26
+ else
27
+ enum.instance_eval(&block)
28
+ enum.extend(self)
29
+ end
30
+ end
31
+
32
+ const_set(name, enum)
33
+ variants << [name.to_s, enum]
34
+ ensure_all_methods_defined_for_each_variant!
35
+ end
36
+
37
+ def case(variant, &block)
38
+ matcher = Matcher.new(all_variants: variants)
39
+ matcher.instance_eval(&block)
40
+ matcher.match_on(variant)
41
+ end
42
+
43
+ private
44
+
45
+ def all_methods_from_all_variants
46
+ variants.flat_map do |_name, enum|
47
+ unique_methods_of(enum)
48
+ end
49
+ end
50
+
51
+ def ensure_all_methods_defined_for_each_variant!
52
+ all_methods = all_methods_from_all_variants
53
+
54
+ variants.each do |name, enum|
55
+ missing_methods = all_methods - unique_methods_of(enum)
56
+
57
+ next if missing_methods.empty?
58
+
59
+ raise MissingMethods, <<~EOS
60
+ Variant #{name} is missing the following methods:
61
+ #{missing_methods.to_a.join(", ")}
62
+ EOS
63
+ end
64
+ end
65
+
66
+ def unique_methods_of(obj)
67
+ (
68
+ (obj.methods - Object.methods) +
69
+ (obj.instance_methods - Object.instance_methods) -
70
+ takes_macro_methods
71
+ ).uniq
72
+ end
73
+
74
+ def takes_macro_methods
75
+ [:takes, :after_takes]
76
+ end
77
+
78
+ class MissingMethods < StandardError; end
79
+ end
data/ya_enum.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "ya_enum/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "ya_enum"
7
+ spec.version = YaEnum::VERSION
8
+ spec.authors = ["David Pedersen"]
9
+ spec.email = ["david.pdrsn@gmail.com"]
10
+
11
+ spec.summary = %q{Enums in Ruby}
12
+ spec.description = %q{Enums in Ruby, that aren't just Java enums. We can do better than that}
13
+ spec.homepage = "http://github.com/tonsser/ya_enum"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.16"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "minitest", "~> 5.0"
25
+
26
+ spec.add_dependency "takes_macro", ">= 1.0.0"
27
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ya_enum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - David Pedersen
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-07-19 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.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: takes_macro
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.0
69
+ description: Enums in Ruby, that aren't just Java enums. We can do better than that
70
+ email:
71
+ - david.pdrsn@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - README.md
81
+ - Rakefile
82
+ - bin/console
83
+ - bin/setup
84
+ - lib/ya_enum.rb
85
+ - lib/ya_enum/matcher.rb
86
+ - lib/ya_enum/version.rb
87
+ - ya_enum.gemspec
88
+ homepage: http://github.com/tonsser/ya_enum
89
+ licenses: []
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.7.3
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Enums in Ruby
111
+ test_files: []