necromancer 0.3.0 → 0.4.0

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