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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +14 -6
  3. data/CHANGELOG.md +35 -3
  4. data/CODE_OF_CONDUCT.md +49 -0
  5. data/Gemfile +3 -4
  6. data/README.md +116 -61
  7. data/lib/necromancer.rb +16 -18
  8. data/lib/necromancer/context.rb +14 -1
  9. data/lib/necromancer/conversion_target.rb +27 -10
  10. data/lib/necromancer/conversions.rb +33 -13
  11. data/lib/necromancer/converter.rb +3 -1
  12. data/lib/necromancer/converters/array.rb +5 -1
  13. data/lib/necromancer/converters/boolean.rb +4 -1
  14. data/lib/necromancer/converters/date_time.rb +45 -1
  15. data/lib/necromancer/converters/numeric.rb +4 -1
  16. data/lib/necromancer/converters/range.rb +4 -1
  17. data/lib/necromancer/null_converter.rb +3 -1
  18. data/lib/necromancer/version.rb +2 -2
  19. data/necromancer.gemspec +3 -1
  20. data/spec/unit/can_spec.rb +1 -3
  21. data/spec/unit/config_spec.rb +1 -3
  22. data/spec/unit/configuration/new_spec.rb +1 -3
  23. data/spec/unit/conversions/fetch_spec.rb +16 -0
  24. data/spec/unit/conversions/register_spec.rb +12 -3
  25. data/spec/unit/conversions/to_hash_spec.rb +37 -0
  26. data/spec/unit/convert_spec.rb +29 -3
  27. data/spec/unit/converters/array/array_to_boolean_spec.rb +1 -3
  28. data/spec/unit/converters/array/array_to_numeric_spec.rb +1 -3
  29. data/spec/unit/converters/array/array_to_set_spec.rb +1 -3
  30. data/spec/unit/converters/array/object_to_array_spec.rb +1 -3
  31. data/spec/unit/converters/array/string_to_array_spec.rb +1 -3
  32. data/spec/unit/converters/boolean/boolean_to_integer_spec.rb +1 -3
  33. data/spec/unit/converters/boolean/integer_to_boolean_spec.rb +1 -3
  34. data/spec/unit/converters/boolean/string_to_boolean_spec.rb +1 -3
  35. data/spec/unit/converters/date_time/string_to_date_spec.rb +5 -3
  36. data/spec/unit/converters/date_time/string_to_datetime_spec.rb +1 -3
  37. data/spec/unit/converters/date_time/string_to_time_spec.rb +28 -0
  38. data/spec/unit/converters/numeric/string_to_float_spec.rb +1 -3
  39. data/spec/unit/converters/numeric/string_to_integer_spec.rb +1 -3
  40. data/spec/unit/converters/numeric/string_to_numeric_spec.rb +1 -3
  41. data/spec/unit/converters/range/string_to_range_spec.rb +1 -3
  42. data/spec/unit/inspect_spec.rb +14 -0
  43. data/spec/unit/new_spec.rb +1 -3
  44. data/spec/unit/register_spec.rb +1 -3
  45. metadata +48 -12
  46. data/.ruby-version +0 -1
@@ -1,21 +1,7 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
2
 
3
- require 'forwardable'
4
- require 'date'
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
@@ -1,4 +1,10 @@
1
- # coding: utf-8
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
- fail ArgumentError,
23
- 'You need to pass either argument or a block to `convert`.'
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
- fail ArgumentError,
28
- 'You cannot pass both an argument and a block to `convert`.'
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 Fixnum, Bignum then :integer
70
- when Symbol then object
82
+ when Integer then :integer
83
+ when Class then object.name.downcase
71
84
  else
72
- object.class.name.downcase
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
- # coding: utf-8
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
- converter_map["object->#{target}"] ||
49
- fail_no_type_conversion_available(key)
56
+ converter_map["object#{DELIMITER}#{target}"] ||
57
+ raise_no_type_conversion_available(key)
50
58
  end
59
+ alias fetch []
51
60
 
52
- # Fail with conversion error
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,4 +1,6 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+
3
+ require_relative 'configuration'
2
4
 
3
5
  module Necromancer
4
6
  # Abstract converter used internally as a base for other converters
@@ -1,4 +1,8 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+
3
+ require 'set'
4
+
5
+ require_relative '../converter'
2
6
 
3
7
  module Necromancer
4
8
  # Container for Array converter classes
@@ -1,4 +1,7 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+
3
+ require_relative '../converter'
4
+ require_relative '../null_converter'
2
5
 
3
6
  module Necromancer
4
7
  # Container for Boolean converter classes
@@ -1,10 +1,25 @@
1
- # coding: utf-8
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
@@ -1,4 +1,7 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+
3
+ require_relative '../converter'
4
+ require_relative '../null_converter'
2
5
 
3
6
  module Necromancer
4
7
  # Container for Numeric converter classes
@@ -1,4 +1,7 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+
3
+ require_relative '../converter'
4
+ require_relative '../null_converter'
2
5
 
3
6
  module Necromancer
4
7
  # Container for Range converter classes
@@ -1,4 +1,6 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
+
3
+ require_relative 'converter'
2
4
 
3
5
  module Necromancer
4
6
  # A pass through converter
@@ -1,5 +1,5 @@
1
- # coding: utf-8
1
+ # encoding: utf-8
2
2
 
3
3
  module Necromancer
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end # Necromancer
@@ -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/peter-murach/necromancer'
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
@@ -1,6 +1,4 @@
1
- # coding: utf-8
2
-
3
- require 'spec_helper'
1
+ # encoding: utf-8
4
2
 
5
3
  RSpec.describe Necromancer, 'can?' do
6
4
  it "checks if conversion is possible" do
@@ -1,6 +1,4 @@
1
- # coding: utf-8
2
-
3
- require 'spec_helper'
1
+ # encoding: utf-8
4
2
 
5
3
  RSpec.describe Necromancer, 'config' do
6
4
  it "configures global settings per instance" do
@@ -1,6 +1,4 @@
1
- # coding: utf-8
2
-
3
- require 'spec_helper'
1
+ # encoding: utf-8
4
2
 
5
3
  RSpec.describe Necromancer::Configuration, '.new' do
6
4
 
@@ -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
- # coding: utf-8
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