necromancer 0.1.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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.travis.yml +19 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +22 -0
- data/README.md +141 -0
- data/Rakefile +8 -0
- data/lib/necromancer.rb +27 -0
- data/lib/necromancer/context.rb +45 -0
- data/lib/necromancer/conversion_target.rb +82 -0
- data/lib/necromancer/conversions.rb +75 -0
- data/lib/necromancer/converter.rb +37 -0
- data/lib/necromancer/converters/array.rb +50 -0
- data/lib/necromancer/converters/boolean.rb +58 -0
- data/lib/necromancer/converters/float.rb +28 -0
- data/lib/necromancer/converters/integer.rb +48 -0
- data/lib/necromancer/converters/range.rb +38 -0
- data/lib/necromancer/null_converter.rb +10 -0
- data/lib/necromancer/version.rb +5 -0
- data/necromancer.gemspec +21 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/unit/can_spec.rb +11 -0
- data/spec/unit/conversions/register_spec.rb +50 -0
- data/spec/unit/convert_spec.rb +66 -0
- data/spec/unit/converters/array/array_to_numeric_spec.rb +22 -0
- data/spec/unit/converters/array/string_to_array_spec.rb +15 -0
- data/spec/unit/converters/boolean/boolean_to_integer_spec.rb +16 -0
- data/spec/unit/converters/boolean/integer_to_boolean_spec.rb +16 -0
- data/spec/unit/converters/boolean/string_to_boolean_spec.rb +96 -0
- data/spec/unit/converters/float/string_to_float_spec.rb +28 -0
- data/spec/unit/converters/integer/string_to_integer_spec.rb +38 -0
- data/spec/unit/converters/range/string_to_range_spec.rb +52 -0
- data/spec/unit/new_spec.rb +12 -0
- data/tasks/console.rake +10 -0
- data/tasks/coverage.rake +11 -0
- data/tasks/spec.rake +29 -0
- 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
data/.rspec
ADDED
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]
|
3
|
+
[][travis]
|
4
|
+
[][codeclimate]
|
5
|
+
[][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
data/lib/necromancer.rb
ADDED
@@ -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
|