necromancer 0.3.0 → 0.7.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.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +70 -3
  3. data/README.md +204 -86
  4. data/lib/necromancer.rb +17 -18
  5. data/lib/necromancer/configuration.rb +1 -1
  6. data/lib/necromancer/context.rb +16 -3
  7. data/lib/necromancer/conversion_target.rb +31 -14
  8. data/lib/necromancer/conversions.rb +39 -16
  9. data/lib/necromancer/converter.rb +10 -8
  10. data/lib/necromancer/converters/array.rb +143 -45
  11. data/lib/necromancer/converters/boolean.rb +21 -19
  12. data/lib/necromancer/converters/date_time.rb +58 -13
  13. data/lib/necromancer/converters/hash.rb +119 -0
  14. data/lib/necromancer/converters/numeric.rb +32 -28
  15. data/lib/necromancer/converters/range.rb +44 -18
  16. data/lib/necromancer/null_converter.rb +4 -2
  17. data/lib/necromancer/version.rb +2 -2
  18. metadata +39 -72
  19. data/.gitignore +0 -14
  20. data/.rspec +0 -3
  21. data/.ruby-version +0 -1
  22. data/.travis.yml +0 -19
  23. data/Gemfile +0 -16
  24. data/Rakefile +0 -8
  25. data/necromancer.gemspec +0 -21
  26. data/spec/spec_helper.rb +0 -53
  27. data/spec/unit/can_spec.rb +0 -11
  28. data/spec/unit/config_spec.rb +0 -32
  29. data/spec/unit/configuration/new_spec.rb +0 -30
  30. data/spec/unit/conversions/register_spec.rb +0 -49
  31. data/spec/unit/convert_spec.rb +0 -104
  32. data/spec/unit/converters/array/array_to_boolean_spec.rb +0 -22
  33. data/spec/unit/converters/array/array_to_numeric_spec.rb +0 -22
  34. data/spec/unit/converters/array/array_to_set_spec.rb +0 -18
  35. data/spec/unit/converters/array/object_to_array_spec.rb +0 -21
  36. data/spec/unit/converters/array/string_to_array_spec.rb +0 -33
  37. data/spec/unit/converters/boolean/boolean_to_integer_spec.rb +0 -26
  38. data/spec/unit/converters/boolean/integer_to_boolean_spec.rb +0 -22
  39. data/spec/unit/converters/boolean/string_to_boolean_spec.rb +0 -36
  40. data/spec/unit/converters/date_time/string_to_date_spec.rb +0 -22
  41. data/spec/unit/converters/date_time/string_to_datetime_spec.rb +0 -32
  42. data/spec/unit/converters/numeric/string_to_float_spec.rb +0 -48
  43. data/spec/unit/converters/numeric/string_to_integer_spec.rb +0 -62
  44. data/spec/unit/converters/numeric/string_to_numeric_spec.rb +0 -32
  45. data/spec/unit/converters/range/string_to_range_spec.rb +0 -35
  46. data/spec/unit/new_spec.rb +0 -12
  47. data/spec/unit/register_spec.rb +0 -17
  48. data/tasks/console.rake +0 -10
  49. data/tasks/coverage.rake +0 -11
  50. data/tasks/spec.rake +0 -29
@@ -1,4 +1,7 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../converter"
4
+ require_relative "../null_converter"
2
5
 
3
6
  module Necromancer
4
7
  # Container for Boolean converter classes
@@ -26,12 +29,11 @@ module Necromancer
26
29
  # 0, f, F, FALSE, false, False, n, N, NO, no, No, off, OFF
27
30
  #
28
31
  # @api public
29
- def call(value, options = {})
30
- strict = options.fetch(:strict, config.strict)
32
+ def call(value, strict: config.strict)
31
33
  case value.to_s
32
34
  when TRUE_MATCHER then true
33
35
  when FALSE_MATCHER then false
34
- else strict ? fail_conversion_type(value) : value
36
+ else strict ? raise_conversion_type(value) : value
35
37
  end
36
38
  end
37
39
  end
@@ -47,13 +49,10 @@ module Necromancer
47
49
  # converter.call(0) # => false
48
50
  #
49
51
  # @api public
50
- def call(value, options = {})
51
- strict = options.fetch(:strict, config.strict)
52
- begin
53
- !value.zero?
54
- rescue
55
- strict ? fail_conversion_type(value) : value
56
- end
52
+ def call(value, strict: config.strict)
53
+ !value.zero?
54
+ rescue StandardError
55
+ strict ? raise_conversion_type(value) : value
57
56
  end
58
57
  end
59
58
 
@@ -68,21 +67,24 @@ module Necromancer
68
67
  # converter.call(false) # => 0
69
68
  #
70
69
  # @api public
71
- def call(value, options = {})
72
- strict = options.fetch(:strict, config.strict)
73
- if ['TrueClass', 'FalseClass'].include?(value.class.name)
70
+ def call(value, strict: config.strict)
71
+ if %w[TrueClass FalseClass].include?(value.class.name)
74
72
  value ? 1 : 0
75
73
  else
76
- strict ? fail_conversion_type(value) : value
74
+ strict ? raise_conversion_type(value) : value
77
75
  end
78
76
  end
79
77
  end
80
78
 
81
79
  def self.load(conversions)
82
- conversions.register StringToBooleanConverter.new(:string, :boolean)
83
- conversions.register IntegerToBooleanConverter.new(:integer, :boolean)
84
- conversions.register BooleanToIntegerConverter.new(:boolean, :integer)
85
- conversions.register NullConverter.new(:boolean, :boolean)
80
+ [
81
+ StringToBooleanConverter.new(:string, :boolean),
82
+ IntegerToBooleanConverter.new(:integer, :boolean),
83
+ BooleanToIntegerConverter.new(:boolean, :integer),
84
+ NullConverter.new(:boolean, :boolean)
85
+ ].each do |converter|
86
+ conversions.register converter
87
+ end
86
88
  end
87
89
  end # BooleanConverters
88
90
  end # Necromancer
@@ -1,33 +1,78 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "time"
5
+
6
+ require_relative "../converter"
7
+ require_relative "../null_converter"
2
8
 
3
9
  module Necromancer
4
10
  # Container for Date converter classes
5
11
  module DateTimeConverters
6
12
  # An object that converts a String to a Date
7
13
  class StringToDateConverter < Converter
8
- def call(value, options = {})
9
- strict = options.fetch(:strict, config.strict)
14
+ # Convert a string value to a Date
15
+ #
16
+ # @example
17
+ # converter.call("1-1-2015") # => "2015-01-01"
18
+ # converter.call("01/01/2015") # => "2015-01-01"
19
+ # converter.call("2015-11-12") # => "2015-11-12"
20
+ # converter.call("12/11/2015") # => "2015-11-12"
21
+ #
22
+ # @api public
23
+ def call(value, strict: config.strict)
10
24
  Date.parse(value)
11
- rescue
12
- strict ? fail_conversion_type(value) : value
25
+ rescue StandardError
26
+ strict ? raise_conversion_type(value) : value
13
27
  end
14
28
  end
15
29
 
16
30
  # An object that converts a String to a DateTime
17
31
  class StringToDateTimeConverter < Converter
18
- def call(value, options = {})
19
- strict = options.fetch(:strict, config.strict)
32
+ # Convert a string value to a DateTime
33
+ #
34
+ # @example
35
+ # converer.call("1-1-2015") # => "2015-01-01T00:00:00+00:00"
36
+ # converer.call("1-1-2015 15:12:44") # => "2015-01-01T15:12:44+00:00"
37
+ #
38
+ # @api public
39
+ def call(value, strict: config.strict)
20
40
  DateTime.parse(value)
21
- rescue
22
- strict ? fail_conversion_type(value) : value
41
+ rescue StandardError
42
+ strict ? raise_conversion_type(value) : value
43
+ end
44
+ end
45
+
46
+ class StringToTimeConverter < Converter
47
+ # Convert a String value to a Time value
48
+ #
49
+ # @param [String] value
50
+ # the value to convert
51
+ #
52
+ # @example
53
+ # converter.call("01-01-2015") # => 2015-01-01 00:00:00 +0100
54
+ # converter.call("01-01-2015 08:35") # => 2015-01-01 08:35:00 +0100
55
+ # converter.call("12:35") # => 2015-01-04 12:35:00 +0100
56
+ #
57
+ # @api public
58
+ def call(value, strict: config.strict)
59
+ Time.parse(value)
60
+ rescue StandardError
61
+ strict ? raise_conversion_type(value) : value
23
62
  end
24
63
  end
25
64
 
26
65
  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)
66
+ [
67
+ StringToDateConverter.new(:string, :date),
68
+ NullConverter.new(:date, :date),
69
+ StringToDateTimeConverter.new(:string, :datetime),
70
+ NullConverter.new(:datetime, :datetime),
71
+ StringToTimeConverter.new(:string, :time),
72
+ NullConverter.new(:time, :time)
73
+ ].each do |converter|
74
+ conversions.register converter
75
+ end
31
76
  end
32
77
  end # DateTimeConverters
33
78
  end # Necromancer
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../converter"
4
+ require_relative "boolean"
5
+ require_relative "numeric"
6
+
7
+ module Necromancer
8
+ module HashConverters
9
+ # An object that converts a String to a Hash
10
+ class StringToHashConverter < Converter
11
+ DEFAULT_CONVERSION = ->(val, *_rest) { val }
12
+
13
+ # Convert string value to hash
14
+ #
15
+ # @example
16
+ # converter.call("a:1 b:2 c:3")
17
+ # # => {a: "1", b: "2", c: "3"}
18
+ #
19
+ # @example
20
+ # converter.call("a=1 & b=3 & c=3")
21
+ # # => {a: "1", b: "2", c: "3"}
22
+ #
23
+ # @api public
24
+ def call(value, strict: config.strict, value_converter: DEFAULT_CONVERSION)
25
+ values = value.split(/\s*[& ]\s*/)
26
+ values.each_with_object({}) do |pair, pairs|
27
+ key, value = pair.split(/[=:]/, 2)
28
+ value_converted = value_converter.(value, strict: strict)
29
+ if current = pairs[key.to_sym]
30
+ pairs[key.to_sym] = Array(current) << value_converted
31
+ else
32
+ pairs[key.to_sym] = value_converted
33
+ end
34
+ pairs
35
+ end
36
+ end
37
+ end
38
+
39
+ class StringToIntegerHashConverter < Converter
40
+ # Convert string value to hash with integer values
41
+ #
42
+ # @example
43
+ # converter.call("a:1 b:2 c:3")
44
+ # # => {a: 1, b: 2, c: 3}
45
+ #
46
+ # @api public
47
+ def call(value, strict: config.strict)
48
+ int_converter = NumericConverters::StringToIntegerConverter.new(:string,
49
+ :integer)
50
+ hash_converter = StringToHashConverter.new(:string, :hash)
51
+ hash_converter.(value, strict: strict, value_converter: int_converter)
52
+ end
53
+ end
54
+
55
+ class StringToFloatHashConverter < Converter
56
+ # Convert string value to hash with float values
57
+ #
58
+ # @example
59
+ # converter.call("a:1 b:2 c:3")
60
+ # # => {a: 1.0, b: 2.0, c: 3.0}
61
+ #
62
+ # @api public
63
+ def call(value, strict: config.strict)
64
+ float_converter = NumericConverters::StringToFloatConverter.new(:string,
65
+ :float)
66
+ hash_converter = StringToHashConverter.new(:string, :hash)
67
+ hash_converter.(value, strict: strict, value_converter: float_converter)
68
+ end
69
+ end
70
+
71
+ class StringToNumericHashConverter < Converter
72
+ # Convert string value to hash with numeric values
73
+ #
74
+ # @example
75
+ # converter.call("a:1 b:2.0 c:3")
76
+ # # => {a: 1, b: 2.0, c: 3}
77
+ #
78
+ # @api public
79
+ def call(value, strict: config.strict)
80
+ num_converter = NumericConverters::StringToNumericConverter.new(:string,
81
+ :numeric)
82
+ hash_converter = StringToHashConverter.new(:string, :hash)
83
+ hash_converter.(value, strict: strict, value_converter: num_converter)
84
+ end
85
+ end
86
+
87
+ class StringToBooleanHashConverter < Converter
88
+ # Convert string value to hash with boolean values
89
+ #
90
+ # @example
91
+ # converter.call("a:yes b:no c:t")
92
+ # # => {a: true, b: false, c: true}
93
+ #
94
+ # @api public
95
+ def call(value, strict: config.strict)
96
+ bool_converter = BooleanConverters::StringToBooleanConverter.new(:string,
97
+ :boolean)
98
+ hash_converter = StringToHashConverter.new(:string, :hash)
99
+ hash_converter.(value, strict: strict, value_converter: bool_converter)
100
+ end
101
+ end
102
+
103
+ def self.load(conversions)
104
+ [
105
+ NullConverter.new(:hash, :hash),
106
+ StringToHashConverter.new(:string, :hash),
107
+ StringToIntegerHashConverter.new(:string, :int_hash),
108
+ StringToIntegerHashConverter.new(:string, :integer_hash),
109
+ StringToFloatHashConverter.new(:string, :float_hash),
110
+ StringToNumericHashConverter.new(:string, :num_hash),
111
+ StringToNumericHashConverter.new(:string, :numeric_hash),
112
+ StringToBooleanHashConverter.new(:string, :boolean_hash),
113
+ StringToBooleanHashConverter.new(:string, :bool_hash)
114
+ ].each do |converter|
115
+ conversions.register(converter)
116
+ end
117
+ end
118
+ end # HashConverters
119
+ end # Necromancer
@@ -1,25 +1,27 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../converter"
4
+ require_relative "../null_converter"
2
5
 
3
6
  module Necromancer
4
7
  # Container for Numeric converter classes
5
8
  module NumericConverters
6
- INTEGER_MATCHER = /^[-+]?(\d+)$/.freeze
9
+ INTEGER_MATCHER = /^\s*[-+]?\s*(\d[\d\s]*)?$/.freeze
7
10
 
8
- FLOAT_MATCHER = /^[-+]?(\d*)(\.\d+)?([eE]?[-+]?\d+)?$/.freeze
11
+ FLOAT_MATCHER = /^\s*[-+]?([\d\s]*)(\.[\d\s]+)?([eE]?[-+]?[\d\s]+)?$/.freeze
9
12
 
10
13
  # An object that converts a String to an Integer
11
14
  class StringToIntegerConverter < Converter
12
15
  # Convert string value to integer
13
16
  #
14
17
  # @example
15
- # converter.call('1abc') # => 1
18
+ # converter.call("1abc") # => 1
16
19
  #
17
20
  # @api public
18
- def call(value, options = {})
19
- strict = options.fetch(:strict, config.strict)
21
+ def call(value, strict: config.strict)
20
22
  Integer(value)
21
- rescue
22
- strict ? fail_conversion_type(value) : value.to_i
23
+ rescue StandardError
24
+ strict ? raise_conversion_type(value) : value.to_i
23
25
  end
24
26
  end
25
27
 
@@ -28,10 +30,10 @@ module Necromancer
28
30
  # Convert integer value to string
29
31
  #
30
32
  # @example
31
- # converter.call(1) # => '1'
33
+ # converter.call(1) # => "1"
32
34
  #
33
35
  # @api public
34
- def call(value, _)
36
+ def call(value, **_)
35
37
  value.to_s
36
38
  end
37
39
  end
@@ -41,14 +43,13 @@ module Necromancer
41
43
  # Convert string to float value
42
44
  #
43
45
  # @example
44
- # converter.call('1.2') # => 1.2
46
+ # converter.call("1.2") # => 1.2
45
47
  #
46
48
  # @api public
47
- def call(value, options = {})
48
- strict = options.fetch(:strict, config.strict)
49
+ def call(value, strict: config.strict)
49
50
  Float(value)
50
- rescue
51
- strict ? fail_conversion_type(value) : value.to_f
51
+ rescue StandardError
52
+ strict ? raise_conversion_type(value) : value.to_f
52
53
  end
53
54
  end
54
55
 
@@ -57,32 +58,35 @@ module Necromancer
57
58
  # Convert string to numeric value
58
59
  #
59
60
  # @example
60
- # converter.call('1.0') # => 1.0
61
+ # converter.call("1.0") # => 1.0
61
62
  #
62
63
  # @example
63
- # converter.call('1') # => 1
64
+ # converter.call("1") # => 1
64
65
  #
65
66
  # @api public
66
- def call(value, options = {})
67
- strict = options.fetch(:strict, config.strict)
67
+ def call(value, strict: config.strict)
68
68
  case value
69
69
  when INTEGER_MATCHER
70
- StringToIntegerConverter.new(:string, :integer).call(value, options)
70
+ StringToIntegerConverter.new(:string, :integer).(value, strict: strict)
71
71
  when FLOAT_MATCHER
72
- StringToFloatConverter.new(:string, :float).call(value, options)
72
+ StringToFloatConverter.new(:string, :float).(value, strict: strict)
73
73
  else
74
- strict ? fail_conversion_type(value) : value
74
+ strict ? raise_conversion_type(value) : value
75
75
  end
76
76
  end
77
77
  end
78
78
 
79
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)
80
+ [
81
+ StringToIntegerConverter.new(:string, :integer),
82
+ IntegerToStringConverter.new(:integer, :string),
83
+ NullConverter.new(:integer, :integer),
84
+ StringToFloatConverter.new(:string, :float),
85
+ NullConverter.new(:float, :float),
86
+ StringToNumericConverter.new(:string, :numeric)
87
+ ].each do |converter|
88
+ conversions.register converter
89
+ end
86
90
  end
87
91
  end # Conversion
88
92
  end # Necromancer
@@ -1,13 +1,22 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../converter"
4
+ require_relative "../null_converter"
2
5
 
3
6
  module Necromancer
4
7
  # Container for Range converter classes
5
8
  module RangeConverters
6
- SINGLE_DIGIT_MATCHER = /^(\-?\d+)$/.freeze
9
+ SINGLE_DIGIT_MATCHER = /^(?<digit>-?\d+(\.\d+)?)$/.freeze
7
10
 
8
- DIGIT_MATCHER = /^(-?\d+?)(\.{2}\.?|-|,)(-?\d+)$/.freeze
11
+ DIGIT_MATCHER = /^(?<open>-?\d+(\.\d+)?)
12
+ \s*(?<sep>(\.\s*){2,3}|-|,)\s*
13
+ (?<close>-?\d+(\.\d+)?)$
14
+ /x.freeze
9
15
 
10
- LETTER_MATCHER = /^(\w)(\.{2}\.?|-|,)(\w)$/.freeze
16
+ LETTER_MATCHER = /^(?<open>\w)
17
+ \s*(?<sep>(\.\s*){2,3}|-|,)\s*
18
+ (?<close>\w)$
19
+ /x.freeze
11
20
 
12
21
  # An object that converts a String to a Range
13
22
  class StringToRangeConverter < Converter
@@ -16,30 +25,47 @@ module Necromancer
16
25
  # @param [Object] value
17
26
  #
18
27
  # @example
19
- # converter.call('0,9') # => (0..9)
28
+ # converter.call("0,9") # => (0..9)
20
29
  #
21
30
  # @example
22
- # converter.call('0-9') # => (0..9)
31
+ # converter.call("0-9") # => (0..9)
23
32
  #
24
33
  # @api public
25
- def call(value, options = {})
26
- strict = options.fetch(:strict, config.strict)
27
- case value
28
- when SINGLE_DIGIT_MATCHER
29
- ::Range.new($1.to_i, $1.to_i)
30
- when DIGIT_MATCHER
31
- ::Range.new($1.to_i, $3.to_i, $2 == '...')
32
- when LETTER_MATCHER
33
- ::Range.new($1.to_s, $3.to_s, $2 == '...')
34
+ def call(value, strict: config.strict)
35
+ if match = value.match(SINGLE_DIGIT_MATCHER)
36
+ digit = cast_to_num(match[:digit])
37
+ ::Range.new(digit, digit)
38
+ elsif match = value.match(DIGIT_MATCHER)
39
+ open = cast_to_num(match[:open])
40
+ close = cast_to_num(match[:close])
41
+ ::Range.new(open, close, match[:sep].gsub(/\s*/, "") == "...")
42
+ elsif match = value.match(LETTER_MATCHER)
43
+ ::Range.new(match[:open], match[:close],
44
+ match[:sep].gsub(/\s*/, "") == "...")
34
45
  else
35
- strict ? fail_conversion_type(value) : value
46
+ strict ? raise_conversion_type(value) : value
36
47
  end
37
48
  end
49
+
50
+ # Convert range end to numeric value
51
+ #
52
+ # @api private
53
+ def cast_to_num(str)
54
+ Integer(str)
55
+ rescue ArgumentError
56
+ Float(str)
57
+ rescue ArgumentError
58
+ nil
59
+ end
38
60
  end
39
61
 
40
62
  def self.load(conversions)
41
- conversions.register StringToRangeConverter.new(:string, :range)
42
- conversions.register NullConverter.new(:range, :range)
63
+ [
64
+ StringToRangeConverter.new(:string, :range),
65
+ NullConverter.new(:range, :range)
66
+ ].each do |converter|
67
+ conversions.register converter
68
+ end
43
69
  end
44
70
  end # RangeConverters
45
71
  end # Necromancer