ya_enum 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: []