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 +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +24 -0
- data/README.md +196 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/ya_enum/matcher.rb +41 -0
- data/lib/ya_enum/version.rb +3 -0
- data/lib/ya_enum.rb +79 -0
- data/ya_enum.gemspec +27 -0
- metadata +111 -0
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
data/.travis.yml
ADDED
data/Gemfile
ADDED
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
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,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
|
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: []
|