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