necromancer 0.1.0 → 0.2.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 +6 -0
- data/README.md +229 -19
- data/lib/necromancer.rb +13 -2
- data/lib/necromancer/context.rb +4 -0
- data/lib/necromancer/conversions.rb +2 -1
- data/lib/necromancer/converter.rb +11 -0
- data/lib/necromancer/converters/array.rb +29 -21
- data/lib/necromancer/converters/boolean.rb +40 -10
- data/lib/necromancer/converters/date_time.rb +33 -0
- data/lib/necromancer/converters/numeric.rb +88 -0
- data/lib/necromancer/converters/range.rb +13 -6
- data/lib/necromancer/version.rb +1 -1
- data/spec/spec_helper.rb +8 -0
- data/spec/unit/convert_spec.rb +31 -1
- data/spec/unit/converters/array/string_to_array_spec.rb +19 -1
- data/spec/unit/converters/boolean/boolean_to_integer_spec.rb +10 -0
- data/spec/unit/converters/boolean/integer_to_boolean_spec.rb +6 -0
- data/spec/unit/converters/boolean/string_to_boolean_spec.rb +19 -79
- data/spec/unit/converters/date_time/string_to_date_spec.rb +22 -0
- data/spec/unit/converters/date_time/string_to_datetime_spec.rb +32 -0
- data/spec/unit/converters/numeric/string_to_float_spec.rb +48 -0
- data/spec/unit/converters/{integer → numeric}/string_to_integer_spec.rb +28 -4
- data/spec/unit/converters/numeric/string_to_numeric_spec.rb +32 -0
- data/spec/unit/converters/range/string_to_range_spec.rb +25 -42
- data/spec/unit/register_spec.rb +17 -0
- metadata +17 -8
- data/lib/necromancer/converters/float.rb +0 -28
- data/lib/necromancer/converters/integer.rb +0 -48
- data/spec/unit/converters/float/string_to_float_spec.rb +0 -28
@@ -1,10 +1,14 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
module Necromancer
|
4
|
+
# Container for Boolean converter classes
|
4
5
|
module BooleanConverters
|
6
|
+
TRUE_MATCHER = /^(yes|y|on|t(rue)?|1)$/i.freeze
|
7
|
+
|
8
|
+
FALSE_MATCHER = /^(no|n|off|f(alse)?|0)$/i.freeze
|
9
|
+
|
5
10
|
# An object that converts a String to a Boolean
|
6
11
|
class StringToBooleanConverter < Converter
|
7
|
-
|
8
12
|
# Convert value to boolean type including range of strings
|
9
13
|
#
|
10
14
|
# @param [Object] value
|
@@ -19,32 +23,58 @@ module Necromancer
|
|
19
23
|
# converter.call("False") # => false
|
20
24
|
#
|
21
25
|
# other values coerced to false are:
|
22
|
-
# 0, f, F, FALSE, false, False, n, N, NO, no, No,
|
26
|
+
# 0, f, F, FALSE, false, False, n, N, NO, no, No, off, OFF
|
23
27
|
#
|
24
28
|
# @api public
|
25
29
|
def call(value, options = {})
|
30
|
+
strict = options.fetch(:strict, false)
|
26
31
|
case value.to_s
|
27
|
-
when
|
28
|
-
|
29
|
-
|
30
|
-
return false
|
31
|
-
else
|
32
|
-
fail ArgumentError, "Expected boolean type, got #{value}"
|
32
|
+
when TRUE_MATCHER then true
|
33
|
+
when FALSE_MATCHER then false
|
34
|
+
else strict ? fail_conversion_type(value) : value
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
39
|
# An object that converts an Integer to a Boolean
|
38
40
|
class IntegerToBooleanConverter < Converter
|
41
|
+
# Convert integer to boolean
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# converter.call(1) # => true
|
45
|
+
#
|
46
|
+
# @example
|
47
|
+
# converter.call(0) # => false
|
48
|
+
#
|
49
|
+
# @api public
|
39
50
|
def call(value, options = {})
|
40
|
-
|
51
|
+
strict = options.fetch(:strict, false)
|
52
|
+
begin
|
53
|
+
!value.zero?
|
54
|
+
rescue
|
55
|
+
strict ? fail_conversion_type(value) : value
|
56
|
+
end
|
41
57
|
end
|
42
58
|
end
|
43
59
|
|
44
60
|
# An object that converts a Boolean to an Integer
|
45
61
|
class BooleanToIntegerConverter < Converter
|
62
|
+
# Convert boolean to integer
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# converter.call(true) # => 1
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# converter.call(false) # => 0
|
69
|
+
#
|
70
|
+
# @api public
|
46
71
|
def call(value, options = {})
|
47
|
-
|
72
|
+
strict = options.fetch(:strict, false)
|
73
|
+
if ['TrueClass', 'FalseClass'].include?(value.class.name)
|
74
|
+
value ? 1 : 0
|
75
|
+
else
|
76
|
+
strict ? fail_conversion_type(value) : value
|
77
|
+
end
|
48
78
|
end
|
49
79
|
end
|
50
80
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Necromancer
|
4
|
+
# Container for Date converter classes
|
5
|
+
module DateTimeConverters
|
6
|
+
# An object that converts a String to a Date
|
7
|
+
class StringToDateConverter < Converter
|
8
|
+
def call(value, options = {})
|
9
|
+
strict = options.fetch(:strict, false)
|
10
|
+
Date.parse(value)
|
11
|
+
rescue
|
12
|
+
strict ? fail_conversion_type(value) : value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# An object that converts a String to a DateTime
|
17
|
+
class StringToDateTimeConverter < Converter
|
18
|
+
def call(value, options = {})
|
19
|
+
strict = options.fetch(:strict, false)
|
20
|
+
DateTime.parse(value)
|
21
|
+
rescue
|
22
|
+
strict ? fail_conversion_type(value) : value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.load(conversions)
|
27
|
+
conversions.register StringToDateConverter.new(:string, :date)
|
28
|
+
conversions.register NullConverter.new(:date, :date)
|
29
|
+
conversions.register StringToDateTimeConverter.new(:string, :datetime)
|
30
|
+
conversions.register NullConverter.new(:datetime, :datetime)
|
31
|
+
end
|
32
|
+
end # DateTimeConverters
|
33
|
+
end # Necromancer
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module Necromancer
|
4
|
+
# Container for Numeric converter classes
|
5
|
+
module NumericConverters
|
6
|
+
INTEGER_MATCHER = /^[-+]?(\d+)$/.freeze
|
7
|
+
|
8
|
+
FLOAT_MATCHER = /^[-+]?(\d*)(\.\d+)?([eE]?[-+]?\d+)?$/.freeze
|
9
|
+
|
10
|
+
# An object that converts a String to an Integer
|
11
|
+
class StringToIntegerConverter < Converter
|
12
|
+
# Convert string value to integer
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# converter.call('1abc') # => 1
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def call(value, options = {})
|
19
|
+
strict = options.fetch(:strict, false)
|
20
|
+
Integer(value)
|
21
|
+
rescue
|
22
|
+
strict ? fail_conversion_type(value) : value.to_i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# An object that converts an Integer to a String
|
27
|
+
class IntegerToStringConverter < Converter
|
28
|
+
# Convert integer value to string
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# converter.call(1) # => '1'
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def call(value, _)
|
35
|
+
value.to_s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# An object that converts a String to a Float
|
40
|
+
class StringToFloatConverter < Converter
|
41
|
+
# Convert string to float value
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# converter.call('1.2') # => 1.2
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def call(value, options = {})
|
48
|
+
strict = options.fetch(:strict, false)
|
49
|
+
Float(value)
|
50
|
+
rescue
|
51
|
+
strict ? fail_conversion_type(value) : value.to_f
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# An object that converts a String to a Numeric
|
56
|
+
class StringToNumericConverter < Converter
|
57
|
+
# Convert string to numeric value
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# converter.call('1.0') # => 1.0
|
61
|
+
#
|
62
|
+
# @example
|
63
|
+
# converter.call('1') # => 1
|
64
|
+
#
|
65
|
+
# @api public
|
66
|
+
def call(value, options = {})
|
67
|
+
strict = options.fetch(:strict, false)
|
68
|
+
case value
|
69
|
+
when INTEGER_MATCHER
|
70
|
+
StringToIntegerConverter.new(:string, :integer).call(value, options)
|
71
|
+
when FLOAT_MATCHER
|
72
|
+
StringToFloatConverter.new(:string, :float).call(value, options)
|
73
|
+
else
|
74
|
+
strict ? fail_conversion_type(value) : value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.load(conversions)
|
80
|
+
conversions.register StringToIntegerConverter.new(:string, :integer)
|
81
|
+
conversions.register IntegerToStringConverter.new(:integer, :string)
|
82
|
+
conversions.register NullConverter.new(:integer, :integer)
|
83
|
+
conversions.register StringToFloatConverter.new(:string, :float)
|
84
|
+
conversions.register NullConverter.new(:float, :float)
|
85
|
+
conversions.register StringToNumericConverter.new(:string, :numeric)
|
86
|
+
end
|
87
|
+
end # Conversion
|
88
|
+
end # Necromancer
|
@@ -3,6 +3,12 @@
|
|
3
3
|
module Necromancer
|
4
4
|
# Container for Range converter classes
|
5
5
|
module RangeConverters
|
6
|
+
SINGLE_DIGIT_MATCHER = /^(\-?\d+)$/.freeze
|
7
|
+
|
8
|
+
DIGIT_MATCHER = /^(-?\d+?)(\.{2}\.?|-|,)(-?\d+)$/.freeze
|
9
|
+
|
10
|
+
LETTER_MATCHER = /^(\w)(\.{2}\.?|-|,)(\w)$/.freeze
|
11
|
+
|
6
12
|
# An object that converts a String to a Range
|
7
13
|
class StringToRangeConverter < Converter
|
8
14
|
# Convert value to Range type with possible ranges
|
@@ -17,22 +23,23 @@ module Necromancer
|
|
17
23
|
#
|
18
24
|
# @api public
|
19
25
|
def call(value, options = {})
|
20
|
-
|
21
|
-
|
26
|
+
strict = options.fetch(:strict, false)
|
27
|
+
case value
|
28
|
+
when SINGLE_DIGIT_MATCHER
|
22
29
|
::Range.new($1.to_i, $1.to_i)
|
23
|
-
when
|
30
|
+
when DIGIT_MATCHER
|
24
31
|
::Range.new($1.to_i, $3.to_i, $2 == '...')
|
25
|
-
when
|
32
|
+
when LETTER_MATCHER
|
26
33
|
::Range.new($1.to_s, $3.to_s, $2 == '...')
|
27
34
|
else
|
28
|
-
|
29
|
-
"from #{source} into Range type"
|
35
|
+
strict ? fail_conversion_type(value) : value
|
30
36
|
end
|
31
37
|
end
|
32
38
|
end
|
33
39
|
|
34
40
|
def self.load(conversions)
|
35
41
|
conversions.register StringToRangeConverter.new(:string, :range)
|
42
|
+
conversions.register NullConverter.new(:range, :range)
|
36
43
|
end
|
37
44
|
end # RangeConverters
|
38
45
|
end # Necromancer
|
data/lib/necromancer/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -42,4 +42,12 @@ RSpec.configure do |config|
|
|
42
42
|
config.order = :random
|
43
43
|
|
44
44
|
Kernel.srand config.seed
|
45
|
+
|
46
|
+
config.before :each do
|
47
|
+
[:UpcaseConverter].each do |class_name|
|
48
|
+
if Object.const_defined?(class_name)
|
49
|
+
Object.send(:remove_const, class_name)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
45
53
|
end
|
data/spec/unit/convert_spec.rb
CHANGED
@@ -6,7 +6,7 @@ RSpec.describe Necromancer, '.convert' do
|
|
6
6
|
|
7
7
|
subject(:converter) { described_class.new }
|
8
8
|
|
9
|
-
context 'when
|
9
|
+
context 'when numeric' do
|
10
10
|
it "converts string to integer" do
|
11
11
|
expect(converter.convert('1').to(:integer)).to eq(1)
|
12
12
|
end
|
@@ -28,6 +28,18 @@ RSpec.describe Necromancer, '.convert' do
|
|
28
28
|
converter.convert('1a').to(:integer, strict: true)
|
29
29
|
}.to raise_error(Necromancer::ConversionTypeError)
|
30
30
|
end
|
31
|
+
|
32
|
+
it "doesn't raise error when in non-strict mode" do
|
33
|
+
expect(converter.convert('1').to(:integer, strict: false)).to eql(1)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "converts string to float" do
|
37
|
+
expect(converter.convert('1.0').to(:float)).to eql(1.0)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "converts string to numeric" do
|
41
|
+
expect(converter.convert('1.0').to(:numeric)).to eql(1.0)
|
42
|
+
end
|
31
43
|
end
|
32
44
|
|
33
45
|
context 'when boolean' do
|
@@ -62,5 +74,23 @@ RSpec.describe Necromancer, '.convert' do
|
|
62
74
|
it "converts array to numeric " do
|
63
75
|
expect(converter.convert(['1','2.3','3.0']).to(:numeric)).to eq([1,2.3,3.0])
|
64
76
|
end
|
77
|
+
|
78
|
+
it "fails to convert in strict mode" do
|
79
|
+
expect {
|
80
|
+
converter.convert(['1', '2.3', false]).to(:numeric, strict: true)
|
81
|
+
}.to raise_error(Necromancer::ConversionTypeError)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when datetime' do
|
86
|
+
it "converts string to date" do
|
87
|
+
expect(converter.convert('2014-12-07').to(:date)).
|
88
|
+
to eq(Date.parse('2014-12-07'))
|
89
|
+
end
|
90
|
+
|
91
|
+
it "converts string to datetime" do
|
92
|
+
expect(converter.convert('2014-12-07 17:35:44').to(:datetime)).
|
93
|
+
to eq(DateTime.parse('2014-12-07 17:35:44'))
|
94
|
+
end
|
65
95
|
end
|
66
96
|
end
|
@@ -3,7 +3,17 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
RSpec.describe Necromancer::ArrayConverters::StringToArrayConverter, '.call' do
|
6
|
-
subject(:converter) { described_class.new }
|
6
|
+
subject(:converter) { described_class.new(:string, :array) }
|
7
|
+
|
8
|
+
it "converts empty string to array" do
|
9
|
+
expect(converter.call('', strict: false)).to eq([''])
|
10
|
+
end
|
11
|
+
|
12
|
+
it "fails to convert empty string to array in strict mode" do
|
13
|
+
expect {
|
14
|
+
converter.call('', strict: true)
|
15
|
+
}.to raise_error(Necromancer::ConversionTypeError)
|
16
|
+
end
|
7
17
|
|
8
18
|
it "converts `1,2,3` to array" do
|
9
19
|
expect(converter.call('1,2,3')).to eq([1,2,3])
|
@@ -12,4 +22,12 @@ RSpec.describe Necromancer::ArrayConverters::StringToArrayConverter, '.call' do
|
|
12
22
|
it "converts `a,b,c` to array" do
|
13
23
|
expect(converter.call('a,b,c')).to eq(['a','b','c'])
|
14
24
|
end
|
25
|
+
|
26
|
+
it "converts '1-2-3' to array" do
|
27
|
+
expect(converter.call('1-2-3')).to eq([1,2,3])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "converts ' 1 - 2 - 3 ' to array" do
|
31
|
+
expect(converter.call(' 1 - 2 - 3 ')).to eq([1,2,3])
|
32
|
+
end
|
15
33
|
end
|
@@ -13,4 +13,14 @@ RSpec.describe Necromancer::BooleanConverters::BooleanToIntegerConverter, '.call
|
|
13
13
|
it "converts false to 0 value" do
|
14
14
|
expect(converter.call(false)).to eq(0)
|
15
15
|
end
|
16
|
+
|
17
|
+
it "fails to convert in strict mode" do
|
18
|
+
expect {
|
19
|
+
converter.call('unknown', strict: true)
|
20
|
+
}.to raise_error(Necromancer::ConversionTypeError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns value in non-strict mode" do
|
24
|
+
expect(converter.call('unknown', strict: false)).to eq('unknown')
|
25
|
+
end
|
16
26
|
end
|
@@ -13,4 +13,10 @@ RSpec.describe Necromancer::BooleanConverters::IntegerToBooleanConverter, '.call
|
|
13
13
|
it "converts 0 to false value" do
|
14
14
|
expect(converter.call(0)).to eq(false)
|
15
15
|
end
|
16
|
+
|
17
|
+
it "fails to convert in strict mode" do
|
18
|
+
expect {
|
19
|
+
converter.call('1', strict: true)
|
20
|
+
}.to raise_error(Necromancer::ConversionTypeError)
|
21
|
+
end
|
16
22
|
end
|
@@ -4,93 +4,33 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
RSpec.describe Necromancer::BooleanConverters::StringToBooleanConverter, '.call' do
|
6
6
|
|
7
|
-
subject(:converter) { described_class.new }
|
7
|
+
subject(:converter) { described_class.new(:string, :boolean) }
|
8
8
|
|
9
|
-
it "raises error for empty string" do
|
10
|
-
expect {
|
9
|
+
it "raises error for empty string strict mode" do
|
10
|
+
expect {
|
11
|
+
converter.call('', strict: true)
|
12
|
+
}.to raise_error(Necromancer::ConversionTypeError)
|
11
13
|
end
|
12
14
|
|
13
|
-
it "
|
14
|
-
expect
|
15
|
-
|
16
|
-
|
17
|
-
it "converts 'true' to true value" do
|
18
|
-
expect(converter.call('true')).to eq(true)
|
19
|
-
end
|
20
|
-
|
21
|
-
it "converts 'TRUE' to true value" do
|
22
|
-
expect(converter.call('TRUE')).to eq(true)
|
23
|
-
end
|
24
|
-
|
25
|
-
it "converts TRUE to true value" do
|
26
|
-
expect(converter.call(TRUE)).to eq(true)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "converts 't' to true value" do
|
30
|
-
expect(converter.call('t')).to eq(true)
|
31
|
-
end
|
32
|
-
|
33
|
-
it "converts 'T' to true value" do
|
34
|
-
expect(converter.call('T')).to eq(true)
|
35
|
-
end
|
36
|
-
|
37
|
-
it "converts 1 to true value" do
|
38
|
-
expect(converter.call(1)).to eq(true)
|
39
|
-
end
|
40
|
-
|
41
|
-
it "converts '1' to true value" do
|
42
|
-
expect(converter.call('1')).to eq(true)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "converts 'y' to true value" do
|
46
|
-
expect(converter.call('y')).to eq(true)
|
47
|
-
end
|
48
|
-
|
49
|
-
it "converts 'yes' to true value" do
|
50
|
-
expect(converter.call('yes')).to eq(true)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "converts 'on' to true value" do
|
54
|
-
expect(converter.call('on')).to eq(true)
|
55
|
-
end
|
56
|
-
|
57
|
-
it "converts 'false' to false value" do
|
58
|
-
expect(converter.call('false')).to eq(false)
|
59
|
-
end
|
60
|
-
|
61
|
-
it "converts FALSE to false value" do
|
62
|
-
expect(converter.call(FALSE)).to eq(false)
|
63
|
-
end
|
64
|
-
|
65
|
-
it "converts 'FALSE' to false value" do
|
66
|
-
expect(converter.call('FALSE')).to eq(false)
|
67
|
-
end
|
68
|
-
|
69
|
-
it "converts 'f' to false value" do
|
70
|
-
expect(converter.call('f')).to eq(false)
|
71
|
-
end
|
72
|
-
|
73
|
-
it "converts 'F' to false value" do
|
74
|
-
expect(converter.call('F')).to eq(false)
|
75
|
-
end
|
76
|
-
|
77
|
-
it "converts '0' to false value" do
|
78
|
-
expect(converter.call('0')).to eq(false)
|
79
|
-
end
|
80
|
-
|
81
|
-
it "converts 'n' to false value" do
|
82
|
-
expect(converter.call('n')).to eq(false)
|
15
|
+
it "fails to convert unkonwn value FOO" do
|
16
|
+
expect {
|
17
|
+
converter.call('FOO', strict: true)
|
18
|
+
}.to raise_error(Necromancer::ConversionTypeError)
|
83
19
|
end
|
84
20
|
|
85
|
-
it "
|
86
|
-
expect(converter.call(
|
21
|
+
it "passes through boolean value" do
|
22
|
+
expect(converter.call(true)).to eq(true)
|
87
23
|
end
|
88
24
|
|
89
|
-
|
90
|
-
|
25
|
+
%w[true TRUE t T 1 y Y YES yes on ON].each do |value|
|
26
|
+
it "converts '#{value}' to true value" do
|
27
|
+
expect(converter.call(value)).to eq(true)
|
28
|
+
end
|
91
29
|
end
|
92
30
|
|
93
|
-
|
94
|
-
|
31
|
+
%w[false FALSE f F 0 n N NO No no off OFF].each do |value|
|
32
|
+
it "converts '#{value}' to false value" do
|
33
|
+
expect(converter.call(value)).to eq(false)
|
34
|
+
end
|
95
35
|
end
|
96
36
|
end
|