necromancer 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +229 -19
  4. data/lib/necromancer.rb +13 -2
  5. data/lib/necromancer/context.rb +4 -0
  6. data/lib/necromancer/conversions.rb +2 -1
  7. data/lib/necromancer/converter.rb +11 -0
  8. data/lib/necromancer/converters/array.rb +29 -21
  9. data/lib/necromancer/converters/boolean.rb +40 -10
  10. data/lib/necromancer/converters/date_time.rb +33 -0
  11. data/lib/necromancer/converters/numeric.rb +88 -0
  12. data/lib/necromancer/converters/range.rb +13 -6
  13. data/lib/necromancer/version.rb +1 -1
  14. data/spec/spec_helper.rb +8 -0
  15. data/spec/unit/convert_spec.rb +31 -1
  16. data/spec/unit/converters/array/string_to_array_spec.rb +19 -1
  17. data/spec/unit/converters/boolean/boolean_to_integer_spec.rb +10 -0
  18. data/spec/unit/converters/boolean/integer_to_boolean_spec.rb +6 -0
  19. data/spec/unit/converters/boolean/string_to_boolean_spec.rb +19 -79
  20. data/spec/unit/converters/date_time/string_to_date_spec.rb +22 -0
  21. data/spec/unit/converters/date_time/string_to_datetime_spec.rb +32 -0
  22. data/spec/unit/converters/numeric/string_to_float_spec.rb +48 -0
  23. data/spec/unit/converters/{integer → numeric}/string_to_integer_spec.rb +28 -4
  24. data/spec/unit/converters/numeric/string_to_numeric_spec.rb +32 -0
  25. data/spec/unit/converters/range/string_to_range_spec.rb +25 -42
  26. data/spec/unit/register_spec.rb +17 -0
  27. metadata +17 -8
  28. data/lib/necromancer/converters/float.rb +0 -28
  29. data/lib/necromancer/converters/integer.rb +0 -48
  30. 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, on, ON
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 /^(yes|y|on|t(rue)?|1)$/i
28
- return true
29
- when /^(no|n|off|f(alse)?|0)$/i
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
- !value.zero?
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
- value ? 1 : 0
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
- case value.to_s
21
- when /\A(\-?\d+)\Z/
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 /\A(-?\d+?)(\.{2}\.?|-|,)(-?\d+)\Z/
30
+ when DIGIT_MATCHER
24
31
  ::Range.new($1.to_i, $3.to_i, $2 == '...')
25
- when /\A(\w)(\.{2}\.?|-|,)(\w)\Z/
32
+ when LETTER_MATCHER
26
33
  ::Range.new($1.to_s, $3.to_s, $2 == '...')
27
34
  else
28
- fail ConversionTypeError, "#{value} could not be converted " \
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
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
3
  module Necromancer
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end # Necromancer
@@ -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
@@ -6,7 +6,7 @@ RSpec.describe Necromancer, '.convert' do
6
6
 
7
7
  subject(:converter) { described_class.new }
8
8
 
9
- context 'when integer' do
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 { converter.call('') }.to raise_error(ArgumentError)
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 "passes through boolean value" do
14
- expect(converter.call(true)).to eq(true)
15
- end
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 "converts 'no' to false value" do
86
- expect(converter.call('no')).to eq(false)
21
+ it "passes through boolean value" do
22
+ expect(converter.call(true)).to eq(true)
87
23
  end
88
24
 
89
- it "converts 'off' to false value" do
90
- expect(converter.call('off')).to eq(false)
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
- it "fails to convert unkonwn value FOO" do
94
- expect { converter.call('FOO') }.to raise_error(ArgumentError)
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