necromancer 0.3.0 → 0.7.0

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