ruby-enum 0.9.0 → 1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Dangerfile +2 -0
- data/Gemfile +8 -5
- data/Gemfile.lock +147 -82
- data/README.md +61 -2
- data/Rakefile +7 -0
- data/benchmarks/case.rb +45 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/0.13.2/application.css +1 -0
- data/coverage/assets/0.13.2/application.js +7 -0
- data/coverage/assets/0.13.2/colorbox/border.png +0 -0
- data/coverage/assets/0.13.2/colorbox/controls.png +0 -0
- data/coverage/assets/0.13.2/colorbox/loading.gif +0 -0
- data/coverage/assets/0.13.2/colorbox/loading_background.png +0 -0
- data/coverage/assets/0.13.2/favicon_green.png +0 -0
- data/coverage/assets/0.13.2/favicon_red.png +0 -0
- data/coverage/assets/0.13.2/favicon_yellow.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/0.13.2/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/0.13.2/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/0.13.2/loading.gif +0 -0
- data/coverage/assets/0.13.2/magnify.png +0 -0
- data/coverage/index.html +5768 -973
- data/lib/ruby-enum/enum/case.rb +84 -0
- data/lib/ruby-enum/enum/i18n_mock.rb +19 -0
- data/lib/ruby-enum/enum.rb +9 -3
- data/lib/ruby-enum/errors/base.rb +4 -4
- data/lib/ruby-enum/version.rb +1 -1
- data/lib/ruby-enum.rb +15 -3
- data/pkg/ruby-enum-0.9.0.gem +0 -0
- data/ruby-enum.gemspec +2 -1
- data/spec/ruby-enum/enum/case_spec.rb +119 -0
- data/spec/ruby-enum/enum_spec.rb +201 -71
- data/spec/spec_helper.rb +2 -2
- data/spec_i18n/Gemfile +9 -0
- data/spec_i18n/Rakefile +12 -0
- data/spec_i18n/spec/i18n_spec.rb +50 -0
- data/spec_i18n/spec/spec_helper.rb +8 -0
- metadata +45 -20
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ruby
|
|
4
|
+
module Enum
|
|
5
|
+
##
|
|
6
|
+
# Adds a method to an enum class that allows for exhaustive matching on a value.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# class Color
|
|
10
|
+
# include Ruby::Enum
|
|
11
|
+
# include Ruby::Enum::Case
|
|
12
|
+
#
|
|
13
|
+
# define :RED, :red
|
|
14
|
+
# define :GREEN, :green
|
|
15
|
+
# define :BLUE, :blue
|
|
16
|
+
# define :YELLOW, :yellow
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# Color.case(Color::RED, {
|
|
20
|
+
# [Color::RED, Color::GREEN] => -> { "red or green" },
|
|
21
|
+
# Color::BLUE => -> { "blue" },
|
|
22
|
+
# Color::YELLOW => -> { "yellow" },
|
|
23
|
+
# })
|
|
24
|
+
#
|
|
25
|
+
# Reserves the :else key for a default case:
|
|
26
|
+
# Color.case(Color::RED, {
|
|
27
|
+
# [Color::RED, Color::GREEN] => -> { "red or green" },
|
|
28
|
+
# else: -> { "blue or yellow" },
|
|
29
|
+
# })
|
|
30
|
+
module Case
|
|
31
|
+
def self.included(klass)
|
|
32
|
+
klass.extend(ClassMethods)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# @see Ruby::Enum::Case
|
|
37
|
+
module ClassMethods
|
|
38
|
+
class ValuesNotDefinedError < StandardError
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class NotAllCasesHandledError < StandardError
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def case(value, cases)
|
|
45
|
+
validate_cases(cases)
|
|
46
|
+
|
|
47
|
+
filtered_cases = cases.select do |values, _proc|
|
|
48
|
+
values = [values] unless values.is_a?(Array)
|
|
49
|
+
values.include?(value)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
return call_proc(cases[:else], value) if filtered_cases.none?
|
|
53
|
+
|
|
54
|
+
results = filtered_cases.map { |_values, proc| call_proc(proc, value) }
|
|
55
|
+
|
|
56
|
+
# Return the first result if there is only one result
|
|
57
|
+
results.size == 1 ? results.first : results
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def call_proc(proc, value)
|
|
63
|
+
return if proc.nil?
|
|
64
|
+
|
|
65
|
+
if proc.arity == 1
|
|
66
|
+
proc.call(value)
|
|
67
|
+
else
|
|
68
|
+
proc.call
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def validate_cases(cases)
|
|
73
|
+
all_values = cases.keys.flatten - [:else]
|
|
74
|
+
else_defined = cases.key?(:else)
|
|
75
|
+
superfluous_values = all_values - values
|
|
76
|
+
missing_values = values - all_values
|
|
77
|
+
|
|
78
|
+
raise ValuesNotDefinedError, "Value(s) not defined: #{superfluous_values.join(', ')}" if superfluous_values.any?
|
|
79
|
+
raise NotAllCasesHandledError, "Not all cases handled: #{missing_values.join(', ')}" if missing_values.any? && !else_defined
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :nocov:
|
|
4
|
+
module Ruby
|
|
5
|
+
module Enum
|
|
6
|
+
##
|
|
7
|
+
# Mock I18n module in case the i18n gem is not available.
|
|
8
|
+
module I18nMock
|
|
9
|
+
def self.load_path
|
|
10
|
+
[]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.translate(key, _options = {})
|
|
14
|
+
key
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
# :nocov:
|
data/lib/ruby-enum/enum.rb
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Ruby
|
|
4
4
|
module Enum
|
|
5
|
+
class << self
|
|
6
|
+
# Needed for I18n mock
|
|
7
|
+
attr_accessor :i18n
|
|
8
|
+
end
|
|
9
|
+
|
|
5
10
|
attr_reader :key, :value
|
|
6
11
|
|
|
7
12
|
def initialize(key, value)
|
|
@@ -14,6 +19,9 @@ module Ruby
|
|
|
14
19
|
base.extend ClassMethods
|
|
15
20
|
|
|
16
21
|
base.private_class_method(:new)
|
|
22
|
+
|
|
23
|
+
base.instance_variable_set(:@_enum_hash, {})
|
|
24
|
+
base.instance_variable_set(:@_enums_by_value, {})
|
|
17
25
|
end
|
|
18
26
|
|
|
19
27
|
module ClassMethods
|
|
@@ -144,9 +152,7 @@ module Ruby
|
|
|
144
152
|
end
|
|
145
153
|
|
|
146
154
|
def to_h
|
|
147
|
-
|
|
148
|
-
[key, enum.value]
|
|
149
|
-
end]
|
|
155
|
+
@_enum_hash.transform_values(&:value)
|
|
150
156
|
end
|
|
151
157
|
|
|
152
158
|
private
|
|
@@ -22,13 +22,13 @@ module Ruby
|
|
|
22
22
|
@summary = create_summary(key, attributes)
|
|
23
23
|
@resolution = create_resolution(key, attributes)
|
|
24
24
|
|
|
25
|
-
"\nProblem:\n #{@problem}"\
|
|
25
|
+
"\nProblem:\n #{@problem}" \
|
|
26
26
|
"\nSummary:\n #{@summary}" + "\nResolution:\n #{@resolution}"
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
BASE_KEY = 'ruby.enum.errors.messages' # :nodoc:
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
private
|
|
32
32
|
|
|
33
33
|
# Given the key of the specific error and the options hash, translate the
|
|
34
34
|
# message.
|
|
@@ -39,7 +39,7 @@ module Ruby
|
|
|
39
39
|
#
|
|
40
40
|
# Returns a localized error message string.
|
|
41
41
|
def translate(key, options)
|
|
42
|
-
::
|
|
42
|
+
Ruby::Enum.i18n.translate("#{BASE_KEY}.#{key}", locale: :en, **options).strip
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# Create the problem.
|
data/lib/ruby-enum/version.rb
CHANGED
data/lib/ruby-enum.rb
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'i18n'
|
|
4
|
-
|
|
5
3
|
require 'ruby-enum/version'
|
|
6
4
|
require 'ruby-enum/enum'
|
|
5
|
+
require 'ruby-enum/enum/case'
|
|
6
|
+
require 'ruby-enum/enum/i18n_mock'
|
|
7
|
+
|
|
8
|
+
# Try to load the I18n gem and provide a mock if it is not available.
|
|
9
|
+
begin
|
|
10
|
+
require 'i18n'
|
|
11
|
+
Ruby::Enum.i18n = I18n
|
|
12
|
+
rescue LoadError
|
|
13
|
+
# I18n is not available
|
|
14
|
+
# :nocov:
|
|
15
|
+
# Tests for this loading are in the spec_i18n folder
|
|
16
|
+
Ruby::Enum.i18n = Ruby::Enum::I18nMock
|
|
17
|
+
# :nocov:
|
|
18
|
+
end
|
|
7
19
|
|
|
8
|
-
|
|
20
|
+
Ruby::Enum.i18n.load_path << File.join(File.dirname(__FILE__), 'config', 'locales', 'en.yml')
|
|
9
21
|
|
|
10
22
|
require 'ruby-enum/errors/base'
|
|
11
23
|
require 'ruby-enum/errors/uninitialized_constant_error'
|
|
Binary file
|
data/ruby-enum.gemspec
CHANGED
|
@@ -10,10 +10,11 @@ Gem::Specification.new do |s|
|
|
|
10
10
|
s.email = 'dblock@dblock.org'
|
|
11
11
|
s.platform = Gem::Platform::RUBY
|
|
12
12
|
s.required_rubygems_version = '>= 1.3.6'
|
|
13
|
+
s.required_ruby_version = '>= 2.7'
|
|
13
14
|
s.files = Dir['**/*']
|
|
14
15
|
s.require_paths = ['lib']
|
|
15
16
|
s.homepage = 'http://github.com/dblock/ruby-enum'
|
|
16
17
|
s.licenses = ['MIT']
|
|
17
18
|
s.summary = 'Enum-like behavior for Ruby.'
|
|
18
|
-
s.
|
|
19
|
+
s.metadata['rubygems_mfa_required'] = 'true'
|
|
19
20
|
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
module Case
|
|
6
|
+
class Colors
|
|
7
|
+
include Ruby::Enum
|
|
8
|
+
include Ruby::Enum::Case
|
|
9
|
+
|
|
10
|
+
define :RED, :red
|
|
11
|
+
define :GREEN, :green
|
|
12
|
+
define :BLUE, :blue
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
RSpec.describe Ruby::Enum::Case do
|
|
17
|
+
describe '.case' do
|
|
18
|
+
context 'when all cases are defined' do
|
|
19
|
+
subject { Case::Colors.case(Case::Colors::RED, cases) }
|
|
20
|
+
|
|
21
|
+
let(:cases) do
|
|
22
|
+
{
|
|
23
|
+
[Case::Colors::RED, Case::Colors::GREEN] => -> { 'red or green' },
|
|
24
|
+
Case::Colors::BLUE => -> { 'blue' }
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it { is_expected.to eq('red or green') }
|
|
29
|
+
|
|
30
|
+
context 'when the value is nil' do
|
|
31
|
+
subject { Case::Colors.case(nil, cases) }
|
|
32
|
+
|
|
33
|
+
it { is_expected.to be_nil }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'when the value is empty' do
|
|
37
|
+
subject { Case::Colors.case('', cases) }
|
|
38
|
+
|
|
39
|
+
it { is_expected.to be_nil }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context 'when the value is the value of the enum' do
|
|
43
|
+
subject { Case::Colors.case(:red, cases) }
|
|
44
|
+
|
|
45
|
+
it { is_expected.to eq('red or green') }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context 'when the value is used inside the lambda' do
|
|
49
|
+
subject { Case::Colors.case(Case::Colors::RED, cases) }
|
|
50
|
+
|
|
51
|
+
let(:cases) do
|
|
52
|
+
{
|
|
53
|
+
[Case::Colors::RED, Case::Colors::GREEN] => ->(color) { "is #{color}" },
|
|
54
|
+
Case::Colors::BLUE => -> { 'blue' }
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it { is_expected.to eq('is red') }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context 'when there are mutliple matches' do
|
|
63
|
+
subject do
|
|
64
|
+
Case::Colors.case(
|
|
65
|
+
Case::Colors::RED,
|
|
66
|
+
{
|
|
67
|
+
[Case::Colors::RED, Case::Colors::GREEN] => -> { 'red or green' },
|
|
68
|
+
Case::Colors::RED => -> { 'red' },
|
|
69
|
+
Case::Colors::BLUE => -> { 'blue' }
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it { is_expected.to eq(['red or green', 'red']) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
context 'when not all cases are defined' do
|
|
78
|
+
it 'raises an error' do
|
|
79
|
+
expect do
|
|
80
|
+
Case::Colors.case(
|
|
81
|
+
Case::Colors::RED,
|
|
82
|
+
{ [Case::Colors::RED, Case::Colors::GREEN] => -> { 'red or green' } }
|
|
83
|
+
)
|
|
84
|
+
end.to raise_error(Ruby::Enum::Case::ClassMethods::NotAllCasesHandledError)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context 'when not all cases are defined but :else is specified (default case)' do
|
|
89
|
+
it 'does not raise an error' do
|
|
90
|
+
expect do
|
|
91
|
+
result = Case::Colors.case(
|
|
92
|
+
Case::Colors::BLUE,
|
|
93
|
+
{
|
|
94
|
+
[Case::Colors::RED, Case::Colors::GREEN] => -> { 'red or green' },
|
|
95
|
+
else: -> { 'blue' }
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
expect(result).to eq('blue')
|
|
100
|
+
end.not_to raise_error
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context 'when a superfluous case is defined' do
|
|
105
|
+
it 'raises an error' do
|
|
106
|
+
expect do
|
|
107
|
+
Case::Colors.case(
|
|
108
|
+
Case::Colors::RED,
|
|
109
|
+
{
|
|
110
|
+
[Case::Colors::RED, Case::Colors::GREEN] => -> { 'red or green' },
|
|
111
|
+
Case::Colors::BLUE => -> { 'blue' },
|
|
112
|
+
:something => -> { 'green' }
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
end.to raise_error(Ruby::Enum::Case::ClassMethods::ValuesNotDefinedError)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|