necromancer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +19 -0
  6. data/Gemfile +16 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +141 -0
  9. data/Rakefile +8 -0
  10. data/lib/necromancer.rb +27 -0
  11. data/lib/necromancer/context.rb +45 -0
  12. data/lib/necromancer/conversion_target.rb +82 -0
  13. data/lib/necromancer/conversions.rb +75 -0
  14. data/lib/necromancer/converter.rb +37 -0
  15. data/lib/necromancer/converters/array.rb +50 -0
  16. data/lib/necromancer/converters/boolean.rb +58 -0
  17. data/lib/necromancer/converters/float.rb +28 -0
  18. data/lib/necromancer/converters/integer.rb +48 -0
  19. data/lib/necromancer/converters/range.rb +38 -0
  20. data/lib/necromancer/null_converter.rb +10 -0
  21. data/lib/necromancer/version.rb +5 -0
  22. data/necromancer.gemspec +21 -0
  23. data/spec/spec_helper.rb +45 -0
  24. data/spec/unit/can_spec.rb +11 -0
  25. data/spec/unit/conversions/register_spec.rb +50 -0
  26. data/spec/unit/convert_spec.rb +66 -0
  27. data/spec/unit/converters/array/array_to_numeric_spec.rb +22 -0
  28. data/spec/unit/converters/array/string_to_array_spec.rb +15 -0
  29. data/spec/unit/converters/boolean/boolean_to_integer_spec.rb +16 -0
  30. data/spec/unit/converters/boolean/integer_to_boolean_spec.rb +16 -0
  31. data/spec/unit/converters/boolean/string_to_boolean_spec.rb +96 -0
  32. data/spec/unit/converters/float/string_to_float_spec.rb +28 -0
  33. data/spec/unit/converters/integer/string_to_integer_spec.rb +38 -0
  34. data/spec/unit/converters/range/string_to_range_spec.rb +52 -0
  35. data/spec/unit/new_spec.rb +12 -0
  36. data/tasks/console.rake +10 -0
  37. data/tasks/coverage.rake +11 -0
  38. data/tasks/spec.rake +29 -0
  39. metadata +109 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f9c37da594bf2020bd875ad6a46514c1d20b7d23
4
+ data.tar.gz: f351c2bbbd62d9f4780306897ea5f09983607af7
5
+ SHA512:
6
+ metadata.gz: 71b242fd171277c210324744d09d80702af4f718c675f747b997bfb8ec1bb9525c2f9a4bb4de5c6a5de6a2605248e02b576956a31cfd3cc7fb51ea2bb6bf1988
7
+ data.tar.gz: 2a0cfcb08fb44936ad433903a2d12024d31f615b8fafcbc3100c2a0ab56c253e6d96c9432373ce1c9cd74a47bab2ab948bcae886874824936885641fbeeb54f8
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --warnings
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/.travis.yml ADDED
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ bundler_args: --without yard benchmarks
3
+ script: "bundle exec rake ci"
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.0
8
+ - ruby-head
9
+ - jruby-19mode
10
+ - rbx-2
11
+ matrix:
12
+ include:
13
+ - rvm: jruby-head
14
+ allow_failures:
15
+ - rvm: ruby-head
16
+ - rvm: jruby-head
17
+ fast_finish: true
18
+ branches:
19
+ only: master
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rake', '~> 10.3.2'
7
+ gem 'rspec', '~> 3.1.0'
8
+ gem 'yard', '~> 0.8.7'
9
+ gem 'benchmark-ips', '~> 2.0.0'
10
+ end
11
+
12
+ group :metrics do
13
+ gem 'coveralls', '~> 0.7.0'
14
+ gem 'simplecov', '~> 0.8.2'
15
+ gem 'yardstick', '~> 0.9.9'
16
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Piotr Murach
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # Necromancer
2
+ [![Gem Version](https://badge.fury.io/rb/necromancer.png)][gem]
3
+ [![Build Status](https://secure.travis-ci.org/peter-murach/necromancer.png?branch=master)][travis]
4
+ [![Code Climate](https://codeclimate.com/github/peter-murach/necromancer.png)][codeclimate]
5
+ [![Coverage Status](https://coveralls.io/repos/peter-murach/necromancer/badge.png)][coverage]
6
+
7
+ [gem]: http://badge.fury.io/rb/necromancer
8
+ [travis]: http://travis-ci.org/peter-murach/necromancer
9
+ [codeclimate]: https://codeclimate.com/github/peter-murach/necromancer
10
+ [coverage]: https://coveralls.io/r/peter-murach/necromancer
11
+
12
+ > Conversion from one object type to another with a bit of black magic.
13
+
14
+ **Necromancer** provides independent type conversion component for [TTY](https://github.com/peter-murach/tty) toolkit.
15
+
16
+ ## Features
17
+
18
+ * Simple and expressive API
19
+ * Ability to specify own converters
20
+ * Support conversion of custom defined types
21
+
22
+ ## Installation
23
+
24
+ Add this line to your application's Gemfile:
25
+
26
+ ```ruby
27
+ gem 'necromancer'
28
+ ```
29
+
30
+ And then execute:
31
+
32
+ $ bundle
33
+
34
+ Or install it yourself as:
35
+
36
+ $ gem install necromancer
37
+
38
+ ## Contents
39
+
40
+ * [1. Usage](#1-usage)
41
+ * [2. Interface](#2-interface)
42
+ * [2.1 convert](#21-convert)
43
+ * [2.1 can?](#22-can)
44
+ * [3. Converters](#3-converters)
45
+
46
+ ## 1. Usage
47
+
48
+ **Necromancer** requires you to instatiate it like so:
49
+
50
+ ```ruby
51
+ converter = Necromancer.new
52
+ ```
53
+
54
+ Once initialize **Necromancer** knows how to handle numerous conversions.
55
+
56
+ For example, to convert a string to a range type:
57
+
58
+ ```ruby
59
+ converter.convert('1-10').to(:range) # => 1..10
60
+ ```
61
+
62
+ In order to handle boolean conversions:
63
+
64
+ ```ruby
65
+ converter.convert('t').to(:boolean) # => true
66
+ ```
67
+
68
+ or get array elements into numeric type:
69
+
70
+ ```ruby
71
+ converter.convert(['1', '2.3', '3.0']).to(:numeric) # => [1, 2.3, 3.0]
72
+ ```
73
+
74
+ However, if you want to tell **Necromancer** about source type use `from`:
75
+
76
+ ```ruby
77
+ converter.convert(['1', '2.3', '3.0']).from(:array).to(:numeric) # => [1, 2.3, 3.0]
78
+ ```
79
+
80
+ ## 2. Interaface
81
+
82
+ ### 2.1 convert
83
+
84
+ ### 2.2 can?
85
+
86
+ To verify that a a given conversion can be handled by **Necormancer** call `can?` with the `source` and `target` of the desired conversion.
87
+
88
+ ```ruby
89
+ converter = Necromancer.new
90
+ converter.can?(:string, :integer) # => true
91
+ converter.can?(:unknown, :integer) # => false
92
+ ```
93
+
94
+ ## 3. Converters
95
+
96
+ ### 3.1 Custom
97
+
98
+ ### 3.2 Array
99
+
100
+ ```ruby
101
+ converter.convert(['1', '2.3', '3.0']).to(:numeric)
102
+ ```
103
+
104
+ ```ruby
105
+ converter.convert(['1', '2.3', '3.0']).from(:array).to(:numeric)
106
+ ```
107
+
108
+ Raises error when in strict mode
109
+
110
+ ```ruby
111
+ converter.convert(['1', '2.3', false]).from(:array).to(:numeric)
112
+ # => Necromancer::ConversionError: false cannot be converter to numeric value
113
+ ```
114
+
115
+ Returns value when in non strict mode
116
+
117
+ ```ruby
118
+ converter.convert(['1', '2.3', false]).from(:array).to(:numeric)
119
+ # => [1, 2.3, false]
120
+ ```
121
+
122
+ ### 3.3 Range
123
+
124
+ ### 3.4 Hash
125
+
126
+ ```ruby
127
+ converter.convert({ x: '27.5', y: '4', z: '11'}).to(:numeric)
128
+ # => { x: 27.5, y: 4, z: 11}
129
+ ```
130
+
131
+ ## Contributing
132
+
133
+ 1. Fork it ( https://github.com/peter-murach/necromancer/fork )
134
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
135
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
136
+ 4. Push to the branch (`git push origin my-new-feature`)
137
+ 5. Create a new Pull Request
138
+
139
+ ## Copyright
140
+
141
+ Copyright (c) 2014 Piotr Murach. See LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # coding: utf-8
2
+
3
+ require 'bundler/gem_tasks'
4
+
5
+ FileList['tasks/**/*.rake'].each(&method(:import))
6
+
7
+ desc 'Run all specs'
8
+ task ci: %w[ spec ]
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+
3
+ require 'necromancer/conversions'
4
+ require 'necromancer/context'
5
+ require 'necromancer/converter'
6
+ require 'necromancer/null_converter'
7
+ require 'necromancer/converters/array'
8
+ require 'necromancer/converters/boolean'
9
+ require 'necromancer/converters/float'
10
+ require 'necromancer/converters/integer'
11
+ require 'necromancer/converters/range'
12
+ require 'necromancer/conversion_target'
13
+ require 'necromancer/version'
14
+
15
+ module Necromancer
16
+ # Raised when cannot conver to a given type
17
+ ConversionTypeError = Class.new(StandardError)
18
+
19
+ # Raised when conversion type is not available
20
+ NoTypeConversionAvailableError = Class.new(StandardError)
21
+
22
+ def new
23
+ Context.new
24
+ end
25
+
26
+ module_function :new
27
+ end # Necromancer
@@ -0,0 +1,45 @@
1
+ # coding: utf-8
2
+
3
+ module Necromancer
4
+ # A class used by Necromancer to provide user interace
5
+ #
6
+ # @api public
7
+ class Context
8
+ # Create a context
9
+ #
10
+ # @api private
11
+ def initialize
12
+ @conversions = Conversions.new
13
+ @conversions.load
14
+ end
15
+
16
+ # Converts the object
17
+ # @param [Object] value
18
+ # any object to be converted
19
+ #
20
+ # @api public
21
+ def convert(object = ConversionTarget::UndefinedValue, &block)
22
+ ConversionTarget.for(conversions, object, block)
23
+ end
24
+
25
+ # Check if this converter can convert source to target
26
+ #
27
+ # @param [Object] source
28
+ # the source class
29
+ # @param [Object] target
30
+ # the target class
31
+ #
32
+ # @return [Boolean]
33
+ #
34
+ # @api public
35
+ def can?(source, target)
36
+ !conversions[source, target].nil?
37
+ rescue NoTypeConversionAvailableError
38
+ false
39
+ end
40
+
41
+ protected
42
+
43
+ attr_reader :conversions
44
+ end # Context
45
+ end # Necromancer
@@ -0,0 +1,82 @@
1
+ # coding: utf-8
2
+
3
+ module Necromancer
4
+ # A class responsible for wrapping conversion target
5
+ #
6
+ # @api private
7
+ class ConversionTarget
8
+ # Used as a stand in for lack of value
9
+ # @api private
10
+ UndefinedValue = Module.new
11
+
12
+ # @api private
13
+ def initialize(conversions, object)
14
+ @object = object
15
+ @conversions = conversions
16
+ end
17
+
18
+ # @api private
19
+ def self.for(context, value, block)
20
+ if UndefinedValue.equal?(value)
21
+ unless block
22
+ fail ArgumentError,
23
+ 'You need to pass either argument or a block to `convert`.'
24
+ end
25
+ new(context, block.call)
26
+ elsif block
27
+ fail ArgumentError,
28
+ 'You cannot pass both an argument and a block to `convert`.'
29
+ else
30
+ new(context, value)
31
+ end
32
+ end
33
+
34
+ # Allows to specify conversion source type
35
+ #
36
+ # @example
37
+ # converter.convert('1').from(:string).to(:numeric) # => 1
38
+ #
39
+ # @return [ConversionType]
40
+ #
41
+ # @api public
42
+ def from(source)
43
+ @source = source
44
+ self
45
+ end
46
+
47
+ # Runs a given conversion
48
+ #
49
+ # @example
50
+ # converter.convert('1').to(:numeric) # => 1
51
+ #
52
+ # @return [Object]
53
+ # the converted target type
54
+ #
55
+ # @api public
56
+ def to(target, options = {})
57
+ conversion = conversions[source || detect(object), detect(target)]
58
+ conversion.call(object, options)
59
+ end
60
+
61
+ protected
62
+
63
+ # Detect object type
64
+ #
65
+ # @api private
66
+ def detect(object)
67
+ case object
68
+ when TrueClass, FalseClass then :boolean
69
+ when Fixnum, Bignum then :integer
70
+ when Symbol then object
71
+ else
72
+ object.class.name.downcase
73
+ end
74
+ end
75
+
76
+ attr_reader :object
77
+
78
+ attr_reader :conversions
79
+
80
+ attr_reader :source
81
+ end # ConversionTarget
82
+ end # Necromancer
@@ -0,0 +1,75 @@
1
+ # coding: utf-8
2
+
3
+ module Necromancer
4
+ # Represents the context used to configure various converters
5
+ # and facilitate type conversion
6
+ #
7
+ # @api public
8
+ class Conversions
9
+ DELIMITER = '->'.freeze
10
+
11
+ # TODO: allow to register converters as just simple proc objects
12
+ #
13
+ # register "integer->string", { |value| value.to_s }
14
+ # if block present then take it as converter class
15
+ #
16
+
17
+ # Creates a new conversions map
18
+ #
19
+ # @example
20
+ # conversion = Necromancer::Conversions.new
21
+ #
22
+ # @api public
23
+ def initialize
24
+ @converter_map = {}
25
+ end
26
+
27
+ # Load converters
28
+ #
29
+ # @api private
30
+ def load
31
+ ArrayConverters.load(self)
32
+ BooleanConverters.load(self)
33
+ IntegerConverters.load(self)
34
+ RangeConverters.load(self)
35
+ end
36
+
37
+ def [](source, target)
38
+ key = "#{source}#{DELIMITER}#{target}"
39
+ converter_map[key] || fail(NoTypeConversionAvailableError)
40
+ end
41
+
42
+ # @example with simple object
43
+ #
44
+ #
45
+ # @example with block
46
+ #
47
+ #
48
+ # @api public
49
+ def register(converter = nil, &block)
50
+ converter ||= Converter.create(&block)
51
+ key = generate_key(converter)
52
+ return false if converter_map.key?(key)
53
+ converter_map[key] = converter
54
+ true
55
+ end
56
+
57
+ def to_hash
58
+ converter_map.dup
59
+ end
60
+
61
+ protected
62
+
63
+ def generate_key(converter)
64
+ parts = []
65
+ parts << (converter.source ? converter.source.to_s : 'none')
66
+ parts << (converter.target ? converter.target.to_s : 'none')
67
+ parts.join(DELIMITER)
68
+ end
69
+
70
+ # Map of type and conversion
71
+ #
72
+ # @api private
73
+ attr_reader :converter_map
74
+ end # Conversions
75
+ end # Necromancer