necromancer 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|