rasn1 0.6.6 → 0.8.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/README.md +11 -2
- data/lib/rasn1.rb +7 -6
- data/lib/rasn1/model.rb +108 -59
- data/lib/rasn1/types.rb +49 -20
- data/lib/rasn1/types/any.rb +26 -16
- data/lib/rasn1/types/base.rb +146 -115
- data/lib/rasn1/types/bit_string.rb +20 -24
- data/lib/rasn1/types/boolean.rb +17 -14
- data/lib/rasn1/types/choice.rb +11 -16
- data/lib/rasn1/types/constructed.rb +9 -9
- data/lib/rasn1/types/enumerated.rb +4 -2
- data/lib/rasn1/types/generalized_time.rb +24 -26
- data/lib/rasn1/types/ia5string.rb +7 -5
- data/lib/rasn1/types/integer.rb +32 -45
- data/lib/rasn1/types/null.rb +8 -8
- data/lib/rasn1/types/numeric_string.rb +7 -7
- data/lib/rasn1/types/object_id.rb +18 -48
- data/lib/rasn1/types/octet_string.rb +6 -6
- data/lib/rasn1/types/primitive.rb +2 -1
- data/lib/rasn1/types/printable_string.rb +8 -7
- data/lib/rasn1/types/sequence.rb +21 -7
- data/lib/rasn1/types/sequence_of.rb +15 -13
- data/lib/rasn1/types/set.rb +4 -2
- data/lib/rasn1/types/set_of.rb +4 -3
- data/lib/rasn1/types/utc_time.rb +6 -4
- data/lib/rasn1/types/utf8_string.rb +6 -4
- data/lib/rasn1/types/visible_string.rb +4 -3
- data/lib/rasn1/version.rb +3 -1
- metadata +27 -50
- data/.gitignore +0 -11
- data/.rubocop.yml +0 -29
- data/.travis.yml +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -12
- data/rasn1.gemspec +0 -32
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# ASN.1 Bit String
|
5
6
|
# @author Sylvain Daubert
|
6
7
|
class BitString < Primitive
|
7
|
-
|
8
|
+
# BitString id value
|
9
|
+
ID = 3
|
8
10
|
|
9
11
|
# @param [Integer] bit_length
|
10
12
|
# @return [Integer]
|
@@ -22,14 +24,12 @@ module RASN1
|
|
22
24
|
# @see Base#initialize common options to all ASN.1 types
|
23
25
|
def initialize(value_or_options={}, options={})
|
24
26
|
super
|
25
|
-
opts = value_or_options.is_a?(Hash) ? value_or_options : options
|
26
27
|
if @default
|
27
|
-
if
|
28
|
-
|
29
|
-
|
30
|
-
@default_bit_length = opts[:bit_length]
|
28
|
+
raise ASN1Error, "#{@name}: default bit length is not defined" if @options[:bit_length].nil?
|
29
|
+
|
30
|
+
@default_bit_length = @options[:bit_length]
|
31
31
|
end
|
32
|
-
@bit_length =
|
32
|
+
@bit_length = @options[:bit_length]
|
33
33
|
end
|
34
34
|
|
35
35
|
# Get bit length
|
@@ -44,38 +44,34 @@ module RASN1
|
|
44
44
|
# @param [Integer] level
|
45
45
|
# @return [String]
|
46
46
|
def inspect(level=0)
|
47
|
-
str =
|
48
|
-
str <<
|
49
|
-
str << "#{name} " unless @name.nil?
|
50
|
-
str << "#{type}: #{value.inspect} (bit length: #{bit_length})"
|
47
|
+
str = common_inspect(level)
|
48
|
+
str << " #{value.inspect} (bit length: #{bit_length})"
|
51
49
|
end
|
52
50
|
|
53
51
|
private
|
54
52
|
|
55
|
-
def
|
56
|
-
!(!@default.nil?
|
57
|
-
@bit_length == @default_bit_length))
|
58
|
-
!(optional?
|
53
|
+
def can_build?
|
54
|
+
!(!@default.nil? && (@value.nil? || (@value == @default) &&
|
55
|
+
(@bit_length == @default_bit_length))) &&
|
56
|
+
!(optional? && @value.nil?)
|
59
57
|
end
|
60
58
|
|
61
59
|
def value_to_der
|
62
|
-
raise ASN1Error, "
|
60
|
+
raise ASN1Error, "#{@name}: bit length is not set" if bit_length.nil?
|
63
61
|
|
64
|
-
while @value.length * 8 < @bit_length
|
65
|
-
@value << "\x00"
|
66
|
-
end
|
62
|
+
@value << "\x00" while @value.length * 8 < @bit_length
|
67
63
|
@value.force_encoding('BINARY')
|
68
64
|
|
69
65
|
if @value.length * 8 > @bit_length
|
70
|
-
max_len = @bit_length / 8 + (@bit_length % 8
|
66
|
+
max_len = @bit_length / 8 + ((@bit_length % 8).positive? ? 1 : 0)
|
71
67
|
@value = @value[0, max_len]
|
72
68
|
end
|
73
69
|
|
74
70
|
unused = @value.length * 8 - @bit_length
|
75
71
|
der = [unused, @value].pack('CA*')
|
76
72
|
|
77
|
-
if unused
|
78
|
-
last_byte = @value[-1].
|
73
|
+
if unused.positive?
|
74
|
+
last_byte = @value[-1].unpack1('C')
|
79
75
|
last_byte &= (0xff >> unused) << unused
|
80
76
|
der[-1] = [last_byte].pack('C')
|
81
77
|
end
|
@@ -83,7 +79,7 @@ module RASN1
|
|
83
79
|
der
|
84
80
|
end
|
85
81
|
|
86
|
-
def der_to_value(der, ber:false)
|
82
|
+
def der_to_value(der, ber: false)
|
87
83
|
unused, @value = der.unpack('CA*')
|
88
84
|
@bit_length = @value.length * 8 - unused
|
89
85
|
end
|
data/lib/rasn1/types/boolean.rb
CHANGED
@@ -1,34 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# ASN.1 Boolean
|
5
6
|
# @author Sylvain Daubert
|
6
7
|
class Boolean < Primitive
|
7
|
-
|
8
|
+
# Boolean id value
|
9
|
+
ID = 0x01
|
10
|
+
|
11
|
+
# @private
|
12
|
+
DER_TRUE = 0xff
|
13
|
+
# @private
|
14
|
+
DER_FALSE = 0
|
8
15
|
|
9
16
|
private
|
10
17
|
|
11
18
|
def value_to_der
|
12
|
-
[@value ?
|
19
|
+
[@value ? DER_TRUE : DER_FALSE].pack('C')
|
13
20
|
end
|
14
21
|
|
15
22
|
def der_to_value(der, ber: false)
|
16
|
-
unless der.size == 1
|
17
|
-
raise ASN1Error, "tag #@name: BOOLEAN should have a length of 1"
|
18
|
-
end
|
23
|
+
raise ASN1Error, "tag #{@name}: BOOLEAN should have a length of 1" unless der.size == 1
|
19
24
|
|
20
|
-
bool = der.
|
25
|
+
bool = der.unpack1('C')
|
21
26
|
case bool
|
22
|
-
when
|
27
|
+
when DER_FALSE
|
23
28
|
@value = false
|
24
|
-
when
|
29
|
+
when DER_TRUE
|
25
30
|
@value = true
|
26
31
|
else
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
raise ASN1Error, "tag #@name: bad value 0x%02x for BOOLEAN" % bool
|
31
|
-
end
|
32
|
+
raise ASN1Error, "tag #{@name}: bad value 0x%02x for BOOLEAN" % bool unless ber
|
33
|
+
|
34
|
+
@value = true
|
32
35
|
end
|
33
36
|
end
|
34
37
|
end
|
data/lib/rasn1/types/choice.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# A ASN.1 CHOICE is a choice between different types.
|
5
6
|
#
|
6
7
|
# == Create a CHOICE
|
@@ -31,7 +32,6 @@ module RASN1
|
|
31
32
|
# choice.chosen_value # => "abc"
|
32
33
|
# @author Sylvain Daubert
|
33
34
|
class Choice < Base
|
34
|
-
|
35
35
|
# Chosen type
|
36
36
|
# @return [Integer] index of type in choice value
|
37
37
|
attr_accessor :chosen
|
@@ -79,7 +79,7 @@ module RASN1
|
|
79
79
|
@value.each_with_index do |element, i|
|
80
80
|
begin
|
81
81
|
@chosen = i
|
82
|
-
nb_bytes = element.parse!(der)
|
82
|
+
nb_bytes = element.parse!(der, ber: ber)
|
83
83
|
parsed = true
|
84
84
|
return nb_bytes
|
85
85
|
rescue ASN1Error
|
@@ -87,28 +87,23 @@ module RASN1
|
|
87
87
|
next
|
88
88
|
end
|
89
89
|
end
|
90
|
-
raise ASN1Error, "CHOICE
|
90
|
+
raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless parsed
|
91
91
|
end
|
92
92
|
|
93
93
|
def inspect(level=0)
|
94
|
-
str =
|
95
|
-
str <<
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
else
|
101
|
-
str << "\n#{@value[@chosen].inspect(level+1)}"
|
102
|
-
end
|
94
|
+
str = common_inspect(level)
|
95
|
+
str << if !defined? @chosen
|
96
|
+
' not chosen!'
|
97
|
+
else
|
98
|
+
"\n#{@value[@chosen].inspect(level + 1)}"
|
99
|
+
end
|
103
100
|
end
|
104
101
|
|
105
102
|
private
|
106
103
|
|
107
104
|
def check_chosen
|
108
|
-
raise ChoiceError if @chosen.nil?
|
105
|
+
raise ChoiceError if !defined?(@chosen) || @chosen.nil?
|
109
106
|
end
|
110
107
|
end
|
111
108
|
end
|
112
109
|
end
|
113
|
-
|
114
|
-
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# @abstract This class SHOULD be used as base class for all ASN.1 primitive
|
5
6
|
# types.
|
6
7
|
# Base class for all ASN.1 constructed types
|
@@ -12,19 +13,18 @@ module RASN1
|
|
12
13
|
def inspect(level=0)
|
13
14
|
case @value
|
14
15
|
when Array
|
15
|
-
str =
|
16
|
-
str <<
|
17
|
-
|
18
|
-
level = level.abs
|
19
|
-
str << "#{type}:\n"
|
20
|
-
level += 1
|
16
|
+
str = common_inspect(level)
|
17
|
+
str << "\n"
|
18
|
+
level = level.abs + 1
|
21
19
|
@value.each do |item|
|
22
20
|
case item
|
23
21
|
when Base, Model
|
24
|
-
next if item.optional?
|
22
|
+
next if item.optional? && item.value.nil?
|
23
|
+
|
25
24
|
str << "#{item.inspect(level)}\n"
|
26
25
|
else
|
27
|
-
str <<
|
26
|
+
str << ' ' * level
|
27
|
+
str << "#{item.inspect}\n"
|
28
28
|
end
|
29
29
|
end
|
30
30
|
str
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# ASN.1 Enumerated
|
5
6
|
#
|
6
7
|
# An enumerated type permits to assign names to integer values. It may be defined
|
@@ -21,7 +22,8 @@ module RASN1
|
|
21
22
|
# @!attribute enum
|
22
23
|
# @return [Hash]
|
23
24
|
|
24
|
-
|
25
|
+
# Enumerated id value
|
26
|
+
ID = 0x0a
|
25
27
|
|
26
28
|
# @overload initialize(options={})
|
27
29
|
# @option options [Hash] :enum enumeration hash. Keys are names, and values
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# ASN.1 GeneralizedTime
|
5
6
|
#
|
6
7
|
# +{#value} of a {GeneralizedTime} should be a ruby Time.
|
@@ -20,7 +21,8 @@ module RASN1
|
|
20
21
|
# second are supported.
|
21
22
|
# @author Sylvain Daubert
|
22
23
|
class GeneralizedTime < Primitive
|
23
|
-
|
24
|
+
# GeneralizedTime id value
|
25
|
+
ID = 24
|
24
26
|
|
25
27
|
# Get ASN.1 type
|
26
28
|
# @return [String]
|
@@ -29,50 +31,48 @@ module RASN1
|
|
29
31
|
end
|
30
32
|
|
31
33
|
private
|
32
|
-
|
34
|
+
|
33
35
|
def value_to_der
|
34
|
-
if @value.nsec
|
36
|
+
if @value.nsec.positive?
|
35
37
|
der = @value.getutc.strftime('%Y%m%d%H%M%S.%9NZ')
|
36
38
|
der.sub(/0+Z/, 'Z')
|
37
39
|
else
|
38
40
|
@value.getutc.strftime('%Y%m%d%H%M%SZ')
|
39
41
|
end
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
def der_to_value(der, ber: false)
|
43
45
|
date_hour, fraction = der.split('.')
|
44
46
|
utc_offset_forced = false
|
45
47
|
if fraction.nil?
|
46
|
-
if date_hour[-1] != 'Z'
|
48
|
+
if (date_hour[-1] != 'Z') && (date_hour !~ /[+-]\d+$/)
|
47
49
|
# If not UTC, have to add offset with UTC to force
|
48
50
|
# DateTime#strptime to generate a local time. But this difference
|
49
51
|
# may be errored because of DST.
|
50
52
|
date_hour << Time.now.strftime('%z')
|
51
53
|
utc_offset_forced = true
|
52
54
|
end
|
55
|
+
elsif fraction[-1] == 'Z'
|
56
|
+
fraction = fraction[0...-1]
|
57
|
+
date_hour << 'Z'
|
53
58
|
else
|
54
|
-
|
55
|
-
|
56
|
-
|
59
|
+
match = fraction.match(/(\d+)([+-]\d+)/)
|
60
|
+
if match
|
61
|
+
# fraction contains fraction and timezone info. Split them
|
62
|
+
fraction = match[1]
|
63
|
+
date_hour << match[2]
|
57
64
|
else
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
# fraction only contains fraction.
|
65
|
-
# Have to add offset with UTC to force DateTime#strptime to
|
66
|
-
# generate a local time. But this difference may be errored
|
67
|
-
# because of DST.
|
68
|
-
date_hour << Time.now.strftime('%z')
|
69
|
-
utc_offset_forced = true
|
70
|
-
end
|
65
|
+
# fraction only contains fraction.
|
66
|
+
# Have to add offset with UTC to force DateTime#strptime to
|
67
|
+
# generate a local time. But this difference may be errored
|
68
|
+
# because of DST.
|
69
|
+
date_hour << Time.now.strftime('%z')
|
70
|
+
utc_offset_forced = true
|
71
71
|
end
|
72
72
|
end
|
73
73
|
format = case date_hour.size
|
74
74
|
when 11
|
75
|
-
frac_base = 60*60
|
75
|
+
frac_base = 60 * 60
|
76
76
|
'%Y%m%d%HZ'
|
77
77
|
when 13
|
78
78
|
frac_base = 60
|
@@ -100,9 +100,7 @@ module RASN1
|
|
100
100
|
# Check DST. There may be a shift of one hour...
|
101
101
|
if utc_offset_forced
|
102
102
|
compare_time = Time.new(*@value.to_a[0..5].reverse)
|
103
|
-
if compare_time.utc_offset != @value.utc_offset
|
104
|
-
@value = compare_time
|
105
|
-
end
|
103
|
+
@value = compare_time if compare_time.utc_offset != @value.utc_offset
|
106
104
|
end
|
107
105
|
@value += ".#{fraction}".to_r * frac_base unless fraction.nil?
|
108
106
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# ASN.1 IA5 String
|
5
6
|
# @author Sylvain Daubert
|
6
7
|
class IA5String < OctetString
|
7
|
-
|
8
|
+
# IA5String id value
|
9
|
+
ID = 22
|
8
10
|
|
9
11
|
# Get ASN.1 type
|
10
12
|
# @return [String]
|
@@ -13,15 +15,15 @@ module RASN1
|
|
13
15
|
end
|
14
16
|
|
15
17
|
private
|
16
|
-
|
18
|
+
|
17
19
|
def value_to_der
|
18
20
|
@value.to_s.force_encoding('US-ASCII').force_encoding('BINARY')
|
19
21
|
end
|
20
22
|
|
21
|
-
def der_to_value(der, ber:false)
|
23
|
+
def der_to_value(der, ber: false)
|
22
24
|
super
|
23
25
|
@value.force_encoding('US-ASCII')
|
24
26
|
end
|
25
|
-
|
27
|
+
end
|
26
28
|
end
|
27
29
|
end
|
data/lib/rasn1/types/integer.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# ASN.1 Integer
|
5
6
|
# @author Sylvain Daubert
|
6
7
|
class Integer < Primitive
|
7
8
|
# @return [Hash,nil]
|
8
9
|
attr_reader :enum
|
9
10
|
|
10
|
-
|
11
|
+
# Integer id value
|
12
|
+
ID = 2
|
11
13
|
|
12
14
|
# @overload initialize(options={})
|
13
15
|
# @option options [Hash] :enum enumeration hash. Keys are names, and values
|
@@ -21,8 +23,7 @@ module RASN1
|
|
21
23
|
# @see Base#initialize common options to all ASN.1 types
|
22
24
|
def initialize(value_or_options={}, options={})
|
23
25
|
super
|
24
|
-
|
25
|
-
@enum = opts[:enum]
|
26
|
+
@enum = @options[:enum]
|
26
27
|
|
27
28
|
return if @enum.nil?
|
28
29
|
|
@@ -30,45 +31,39 @@ module RASN1
|
|
30
31
|
self.value = @value
|
31
32
|
|
32
33
|
case @default
|
33
|
-
when String,Symbol
|
34
|
-
unless @enum.
|
35
|
-
raise EnumeratedError, "TAG #@name: unknwon enumerated default value #@default"
|
36
|
-
end
|
34
|
+
when String, Symbol
|
35
|
+
raise EnumeratedError, "#{@name}: unknwon enumerated default value #@{default}" unless @enum.key? @default
|
37
36
|
when ::Integer
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
raise EnumeratedError, "TAG #@name: default value #@default not in enumeration"
|
42
|
-
end
|
37
|
+
raise EnumeratedError, "#{@name}: default value #@{default} not in enumeration" unless @enum.value? @default
|
38
|
+
|
39
|
+
@default = @enum.key(@default)
|
43
40
|
when nil
|
44
41
|
else
|
45
|
-
raise TypeError, "
|
42
|
+
raise TypeError, "#{@name}: #{@default.class} not handled as default value"
|
46
43
|
end
|
47
44
|
end
|
48
45
|
|
49
46
|
# @param [Integer,String,Symbol,nil] v
|
50
47
|
# @return [String,Symbol,nil]
|
51
|
-
def value=(
|
52
|
-
case
|
53
|
-
when String,Symbol
|
54
|
-
raise EnumeratedError,
|
48
|
+
def value=(val)
|
49
|
+
case val
|
50
|
+
when String, Symbol
|
51
|
+
raise EnumeratedError, "#{@name} has no :enum" if @enum.nil?
|
52
|
+
raise EnumeratedError, "#{@name}: unknwon enumerated value #{val}" unless @enum.key? val
|
55
53
|
|
56
|
-
|
57
|
-
raise EnumeratedError, "TAG #@name: unknwon enumerated value #{v}"
|
58
|
-
end
|
59
|
-
@value = v
|
54
|
+
@value = val
|
60
55
|
when ::Integer
|
61
56
|
if @enum.nil?
|
62
|
-
@value =
|
63
|
-
elsif @enum.
|
64
|
-
@value = @enum.key(
|
57
|
+
@value = val
|
58
|
+
elsif @enum.value? val
|
59
|
+
@value = @enum.key(val)
|
65
60
|
else
|
66
|
-
raise EnumeratedError, "
|
61
|
+
raise EnumeratedError, "#{@name}: #{val} not in enumeration"
|
67
62
|
end
|
68
63
|
when nil
|
69
64
|
@value = nil
|
70
65
|
else
|
71
|
-
raise EnumeratedError, "
|
66
|
+
raise EnumeratedError, "#{@name}: not in enumeration"
|
72
67
|
end
|
73
68
|
end
|
74
69
|
|
@@ -86,18 +81,12 @@ module RASN1
|
|
86
81
|
|
87
82
|
def int_value_to_der(value=nil)
|
88
83
|
v = value || @value
|
89
|
-
size = v.bit_length / 8 + (v.bit_length % 8
|
90
|
-
size = 1 if size
|
91
|
-
comp_value =
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
v
|
96
|
-
else
|
97
|
-
~(v.abs) + 1
|
98
|
-
end
|
99
|
-
ary = []
|
100
|
-
size.times { ary << (comp_value & 0xff); comp_value >>= 8 }
|
84
|
+
size = v.bit_length / 8 + ((v.bit_length % 8).positive? ? 1 : 0)
|
85
|
+
size = 1 if size.zero?
|
86
|
+
comp_value = v >= 0 ? v : (~(-v) + 1) & ((1 << (size * 8)) - 1)
|
87
|
+
ary = comp_value.digits(256)
|
88
|
+
# v is > 0 and its MSBit is 1. Add a 0 byte to mark it as positive
|
89
|
+
ary << 0 if v.positive? && (v >> (size * 8 - 1) == 1)
|
101
90
|
ary.reverse.pack('C*')
|
102
91
|
end
|
103
92
|
|
@@ -108,16 +97,14 @@ module RASN1
|
|
108
97
|
when ::Integer
|
109
98
|
int_value_to_der
|
110
99
|
else
|
111
|
-
raise TypeError, "
|
100
|
+
raise TypeError, "#{@name}: #{@value.class} not handled"
|
112
101
|
end
|
113
102
|
end
|
114
103
|
|
115
104
|
def der_to_int_value(der, ber: false)
|
116
105
|
ary = der.unpack('C*')
|
117
106
|
v = ary.reduce(0) { |len, b| (len << 8) | b }
|
118
|
-
if ary[0] & 0x80 == 0x80
|
119
|
-
v = -((~v & ((1 << v.bit_length) - 1)) + 1)
|
120
|
-
end
|
107
|
+
v = -((~v & ((1 << v.bit_length) - 1)) + 1) if ary[0] & 0x80 == 0x80
|
121
108
|
v
|
122
109
|
end
|
123
110
|
|
@@ -126,11 +113,11 @@ module RASN1
|
|
126
113
|
return if @enum.nil?
|
127
114
|
|
128
115
|
@value = @enum.key(@value)
|
129
|
-
raise EnumeratedError, "
|
116
|
+
raise EnumeratedError, "#{@name}: value #{v} not in enumeration" if @value.nil?
|
130
117
|
end
|
131
118
|
|
132
119
|
def explicit_type
|
133
|
-
|
120
|
+
self.class.new(name: @name, enum: @enum)
|
134
121
|
end
|
135
122
|
end
|
136
123
|
end
|