necromancer 0.3.0 → 0.4.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/.travis.yml +14 -6
- data/CHANGELOG.md +35 -3
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +3 -4
- data/README.md +116 -61
- data/lib/necromancer.rb +16 -18
- data/lib/necromancer/context.rb +14 -1
- data/lib/necromancer/conversion_target.rb +27 -10
- data/lib/necromancer/conversions.rb +33 -13
- data/lib/necromancer/converter.rb +3 -1
- data/lib/necromancer/converters/array.rb +5 -1
- data/lib/necromancer/converters/boolean.rb +4 -1
- data/lib/necromancer/converters/date_time.rb +45 -1
- data/lib/necromancer/converters/numeric.rb +4 -1
- data/lib/necromancer/converters/range.rb +4 -1
- data/lib/necromancer/null_converter.rb +3 -1
- data/lib/necromancer/version.rb +2 -2
- data/necromancer.gemspec +3 -1
- data/spec/unit/can_spec.rb +1 -3
- data/spec/unit/config_spec.rb +1 -3
- data/spec/unit/configuration/new_spec.rb +1 -3
- data/spec/unit/conversions/fetch_spec.rb +16 -0
- data/spec/unit/conversions/register_spec.rb +12 -3
- data/spec/unit/conversions/to_hash_spec.rb +37 -0
- data/spec/unit/convert_spec.rb +29 -3
- data/spec/unit/converters/array/array_to_boolean_spec.rb +1 -3
- data/spec/unit/converters/array/array_to_numeric_spec.rb +1 -3
- data/spec/unit/converters/array/array_to_set_spec.rb +1 -3
- data/spec/unit/converters/array/object_to_array_spec.rb +1 -3
- data/spec/unit/converters/array/string_to_array_spec.rb +1 -3
- data/spec/unit/converters/boolean/boolean_to_integer_spec.rb +1 -3
- data/spec/unit/converters/boolean/integer_to_boolean_spec.rb +1 -3
- data/spec/unit/converters/boolean/string_to_boolean_spec.rb +1 -3
- data/spec/unit/converters/date_time/string_to_date_spec.rb +5 -3
- data/spec/unit/converters/date_time/string_to_datetime_spec.rb +1 -3
- data/spec/unit/converters/date_time/string_to_time_spec.rb +28 -0
- data/spec/unit/converters/numeric/string_to_float_spec.rb +1 -3
- data/spec/unit/converters/numeric/string_to_integer_spec.rb +1 -3
- data/spec/unit/converters/numeric/string_to_numeric_spec.rb +1 -3
- data/spec/unit/converters/range/string_to_range_spec.rb +1 -3
- data/spec/unit/inspect_spec.rb +14 -0
- data/spec/unit/new_spec.rb +1 -3
- data/spec/unit/register_spec.rb +1 -3
- metadata +48 -12
- data/.ruby-version +0 -1
data/lib/necromancer.rb
CHANGED
@@ -1,21 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require 'set'
|
6
|
-
|
7
|
-
require 'necromancer/conversions'
|
8
|
-
require 'necromancer/configuration'
|
9
|
-
require 'necromancer/context'
|
10
|
-
require 'necromancer/converter'
|
11
|
-
require 'necromancer/null_converter'
|
12
|
-
require 'necromancer/converters/array'
|
13
|
-
require 'necromancer/converters/boolean'
|
14
|
-
require 'necromancer/converters/date_time'
|
15
|
-
require 'necromancer/converters/numeric'
|
16
|
-
require 'necromancer/converters/range'
|
17
|
-
require 'necromancer/conversion_target'
|
18
|
-
require 'necromancer/version'
|
3
|
+
require_relative 'necromancer/context'
|
4
|
+
require_relative 'necromancer/version'
|
19
5
|
|
20
6
|
module Necromancer
|
21
7
|
# Raised when cannot conver to a given type
|
@@ -35,6 +21,18 @@ module Necromancer
|
|
35
21
|
def new(&block)
|
36
22
|
Context.new(&block)
|
37
23
|
end
|
38
|
-
|
39
24
|
module_function :new
|
25
|
+
|
26
|
+
# Convenience to directly call conversion
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# Necromancer.convert('1').to(:integer)
|
30
|
+
#
|
31
|
+
# @return [ConversionTarget]
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def convert(*args, &block)
|
35
|
+
Context.new.convert(*args, &block)
|
36
|
+
end
|
37
|
+
module_function :convert
|
40
38
|
end # Necromancer
|
data/lib/necromancer/context.rb
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
require_relative 'configuration'
|
6
|
+
require_relative 'conversions'
|
7
|
+
require_relative 'conversion_target'
|
2
8
|
|
3
9
|
module Necromancer
|
4
10
|
# A class used by Necromancer to provide user interace
|
@@ -71,6 +77,13 @@ module Necromancer
|
|
71
77
|
false
|
72
78
|
end
|
73
79
|
|
80
|
+
# Inspect this context
|
81
|
+
#
|
82
|
+
# @api public
|
83
|
+
def inspect
|
84
|
+
%(#<#{self.class}@#{object_id} @config=#{configuration}>)
|
85
|
+
end
|
86
|
+
|
74
87
|
protected
|
75
88
|
|
76
89
|
attr_reader :conversions
|
@@ -19,13 +19,13 @@ module Necromancer
|
|
19
19
|
def self.for(context, value, block)
|
20
20
|
if UndefinedValue.equal?(value)
|
21
21
|
unless block
|
22
|
-
|
23
|
-
|
22
|
+
raise ArgumentError,
|
23
|
+
'You need to pass either argument or a block to `convert`.'
|
24
24
|
end
|
25
25
|
new(context, block.call)
|
26
26
|
elsif block
|
27
|
-
|
28
|
-
|
27
|
+
raise ArgumentError,
|
28
|
+
'You cannot pass both an argument and a block to `convert`.'
|
29
29
|
else
|
30
30
|
new(context, value)
|
31
31
|
end
|
@@ -49,27 +49,44 @@ module Necromancer
|
|
49
49
|
# @example
|
50
50
|
# converter.convert('1').to(:numeric) # => 1
|
51
51
|
#
|
52
|
+
# @example
|
53
|
+
# converter.convert('1') >> Integer # => 1
|
54
|
+
#
|
52
55
|
# @return [Object]
|
53
56
|
# the converted target type
|
54
57
|
#
|
55
58
|
# @api public
|
56
59
|
def to(target, options = {})
|
57
|
-
conversion = conversions[source || detect(object), detect(target)]
|
60
|
+
conversion = conversions[source || detect(object, false), detect(target)]
|
58
61
|
conversion.call(object, options)
|
59
62
|
end
|
63
|
+
alias >> to
|
64
|
+
|
65
|
+
# Inspect this conversion
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def inspect
|
69
|
+
%(#<#{self.class}@#{object_id} @object=#{object}, @source=#{detect(object)}>)
|
70
|
+
end
|
60
71
|
|
61
72
|
protected
|
62
73
|
|
63
|
-
# Detect object type
|
74
|
+
# Detect object type and coerce into known key type
|
75
|
+
#
|
76
|
+
# @param [Object] object
|
64
77
|
#
|
65
78
|
# @api private
|
66
|
-
def detect(object)
|
79
|
+
def detect(object, symbol_as_object = true)
|
67
80
|
case object
|
68
81
|
when TrueClass, FalseClass then :boolean
|
69
|
-
when
|
70
|
-
when
|
82
|
+
when Integer then :integer
|
83
|
+
when Class then object.name.downcase
|
71
84
|
else
|
72
|
-
object.
|
85
|
+
if object.is_a?(Symbol) && symbol_as_object
|
86
|
+
object
|
87
|
+
else
|
88
|
+
object.class.name.downcase
|
89
|
+
end
|
73
90
|
end
|
74
91
|
end
|
75
92
|
|
@@ -1,4 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative 'configuration'
|
4
|
+
require_relative 'converter'
|
5
|
+
require_relative 'converters/array'
|
6
|
+
require_relative 'converters/boolean'
|
7
|
+
require_relative 'converters/date_time'
|
8
|
+
require_relative 'converters/numeric'
|
9
|
+
require_relative 'converters/range'
|
2
10
|
|
3
11
|
module Necromancer
|
4
12
|
# Represents the context used to configure various converters
|
@@ -14,9 +22,9 @@ module Necromancer
|
|
14
22
|
# conversion = Necromancer::Conversions.new
|
15
23
|
#
|
16
24
|
# @api public
|
17
|
-
def initialize(configuration = Configuration.new)
|
25
|
+
def initialize(configuration = Configuration.new, map = {})
|
18
26
|
@configuration = configuration
|
19
|
-
@converter_map =
|
27
|
+
@converter_map = map.dup
|
20
28
|
end
|
21
29
|
|
22
30
|
# Load converters
|
@@ -45,22 +53,22 @@ module Necromancer
|
|
45
53
|
def [](source, target)
|
46
54
|
key = "#{source}#{DELIMITER}#{target}"
|
47
55
|
converter_map[key] ||
|
48
|
-
|
49
|
-
|
56
|
+
converter_map["object#{DELIMITER}#{target}"] ||
|
57
|
+
raise_no_type_conversion_available(key)
|
50
58
|
end
|
59
|
+
alias fetch []
|
51
60
|
|
52
|
-
#
|
61
|
+
# Register a converter
|
53
62
|
#
|
54
|
-
# @api private
|
55
|
-
def fail_no_type_conversion_available(key)
|
56
|
-
fail NoTypeConversionAvailableError, "Conversion '#{key}' unavailable."
|
57
|
-
end
|
58
|
-
|
59
63
|
# @example with simple object
|
60
|
-
#
|
64
|
+
# conversions.register NullConverter.new(:array, :array)
|
61
65
|
#
|
62
66
|
# @example with block
|
63
|
-
#
|
67
|
+
# conversions.register do |c|
|
68
|
+
# c.source = :array
|
69
|
+
# c.target = :array
|
70
|
+
# c.convert = -> { |val, options| val }
|
71
|
+
# end
|
64
72
|
#
|
65
73
|
# @api public
|
66
74
|
def register(converter = nil, &block)
|
@@ -72,12 +80,24 @@ module Necromancer
|
|
72
80
|
true
|
73
81
|
end
|
74
82
|
|
83
|
+
# Export all the conversions as hash
|
84
|
+
#
|
85
|
+
# @return [Hash[String, String]]
|
86
|
+
#
|
87
|
+
# @api public
|
75
88
|
def to_hash
|
76
89
|
converter_map.dup
|
77
90
|
end
|
78
91
|
|
79
92
|
protected
|
80
93
|
|
94
|
+
# Fail with conversion error
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
def raise_no_type_conversion_available(key)
|
98
|
+
raise NoTypeConversionAvailableError, "Conversion '#{key}' unavailable."
|
99
|
+
end
|
100
|
+
|
81
101
|
# @api private
|
82
102
|
def generate_key(converter)
|
83
103
|
parts = []
|
@@ -1,10 +1,25 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
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
|
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
|
8
23
|
def call(value, options = {})
|
9
24
|
strict = options.fetch(:strict, config.strict)
|
10
25
|
Date.parse(value)
|
@@ -15,6 +30,13 @@ module Necromancer
|
|
15
30
|
|
16
31
|
# An object that converts a String to a DateTime
|
17
32
|
class StringToDateTimeConverter < Converter
|
33
|
+
# Convert a string value to a DateTime
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# converer.call("1-1-2015") # => "2015-01-01T00:00:00+00:00"
|
37
|
+
# converer.call("1-1-2015 15:12:44") # => "2015-01-01T15:12:44+00:00"
|
38
|
+
#
|
39
|
+
# @api public
|
18
40
|
def call(value, options = {})
|
19
41
|
strict = options.fetch(:strict, config.strict)
|
20
42
|
DateTime.parse(value)
|
@@ -23,11 +45,33 @@ module Necromancer
|
|
23
45
|
end
|
24
46
|
end
|
25
47
|
|
48
|
+
class StringToTimeConverter < Converter
|
49
|
+
# Convert a String value to a Time value
|
50
|
+
#
|
51
|
+
# @param [String] value
|
52
|
+
# the value to convert
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# converter.call("01-01-2015") # => 2015-01-01 00:00:00 +0100
|
56
|
+
# converter.call("01-01-2015 08:35") # => 2015-01-01 08:35:00 +0100
|
57
|
+
# converter.call("12:35") # => 2015-01-04 12:35:00 +0100
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def call(value, options = {})
|
61
|
+
strict = options.fetch(:strict, config.strict)
|
62
|
+
Time.parse(value)
|
63
|
+
rescue
|
64
|
+
strict ? fail_conversion_type(value) : value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
26
68
|
def self.load(conversions)
|
27
69
|
conversions.register StringToDateConverter.new(:string, :date)
|
28
70
|
conversions.register NullConverter.new(:date, :date)
|
29
71
|
conversions.register StringToDateTimeConverter.new(:string, :datetime)
|
30
72
|
conversions.register NullConverter.new(:datetime, :datetime)
|
73
|
+
conversions.register StringToTimeConverter.new(:string, :time)
|
74
|
+
conversions.register NullConverter.new(:time, :time)
|
31
75
|
end
|
32
76
|
end # DateTimeConverters
|
33
77
|
end # Necromancer
|
data/lib/necromancer/version.rb
CHANGED
data/necromancer.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['']
|
11
11
|
spec.summary = %q{Conversion from one object type to another with a bit of black magic.}
|
12
12
|
spec.description = %q{Conversion from one object type to another with a bit of black magic.}
|
13
|
-
spec.homepage = 'https://github.com/
|
13
|
+
spec.homepage = 'https://github.com/piotrmurach/necromancer'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -18,4 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.6"
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'rspec', '~> 3.5.0'
|
21
23
|
end
|
data/spec/unit/can_spec.rb
CHANGED
data/spec/unit/config_spec.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
RSpec.describe Necromancer::Conversions, '#fetch' do
|
4
|
+
it "retrieves conversion given source & target" do
|
5
|
+
converter = double(:converter)
|
6
|
+
conversions = described_class.new nil, {'string->array' => converter}
|
7
|
+
expect(conversions['string', 'array']).to eq(converter)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "fails to find conversion" do
|
11
|
+
conversions = described_class.new
|
12
|
+
expect {
|
13
|
+
conversions['string', 'array']
|
14
|
+
}.to raise_error(Necromancer::NoTypeConversionAvailableError)
|
15
|
+
end
|
16
|
+
end
|
@@ -1,6 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
require 'spec_helper'
|
1
|
+
# encoding: utf-8
|
4
2
|
|
5
3
|
RSpec.describe Necromancer::Conversions, '.register' do
|
6
4
|
it "allows to register converter" do
|
@@ -35,6 +33,17 @@ RSpec.describe Necromancer::Conversions, '.register' do
|
|
35
33
|
expect(conversions[:string, :upcase].call('magic')).to eq('MAGIC')
|
36
34
|
end
|
37
35
|
|
36
|
+
it "allows to register anonymous converter with class names" do
|
37
|
+
conversions = described_class.new
|
38
|
+
|
39
|
+
conversions.register do |c|
|
40
|
+
c.source= String
|
41
|
+
c.target= Array
|
42
|
+
c.convert = proc { |value| Array(value) }
|
43
|
+
end
|
44
|
+
expect(conversions[String, Array].call('magic')).to eq(['magic'])
|
45
|
+
end
|
46
|
+
|
38
47
|
it "allows to register custom converter" do
|
39
48
|
conversions = described_class.new
|
40
49
|
UpcaseConverter = Struct.new(:source, :target) do
|