power_converter 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +40 -3
- data/lib/power_converter/version.rb +1 -1
- data/lib/power_converter.rb +90 -10
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bd5e2306a30c044d98fadc15a1a330cd485f34f
|
4
|
+
data.tar.gz: ebc9c3bed3649bd24f11fa6564e52a088a766d5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53124e4dc414751702a99c3b8be7d61dc31cc275141ddc9a5a9fe0fa192a9890580c022e8190675932ea1406dbc362fa8b0877b8201f4b6f34f74aedb4b22a84
|
7
|
+
data.tar.gz: 93dae5155d1d25aaecac0c218d75a7fa9b734a3e7cf61663d4a427bf0e546d4a9d6a2e1b72c18dfa312debb6853a87a8c034c21582bf3503faf8628e57d19f0b
|
data/README.md
CHANGED
@@ -1,13 +1,46 @@
|
|
1
1
|
# PowerConverter
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/power_converter.svg)](http://badge.fury.io/rb/power_converter)
|
3
4
|
[![Build Status](https://travis-ci.org/jeremyf/power_converter.png?branch=master)](https://travis-ci.org/jeremyf/power_converter)
|
4
5
|
[![Documentation Status](http://inch-ci.org/github/jeremyf/power_converter.svg?branch=master)](http://inch-ci.org/github/jeremyf/power_converter)
|
5
6
|
[![APACHE 2 License](http://img.shields.io/badge/APACHE2-license-blue.svg)](./LICENSE)
|
6
7
|
|
7
|
-
|
8
|
+
## About
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
**PowerConverter** exposes a means for defining a named conversion method.
|
11
|
+
|
12
|
+
*What is a conversion method?*
|
13
|
+
|
14
|
+
> A well-established Ruby idiom for methods which "do the right thing" to
|
15
|
+
> convery any reasonable input value into a desired class.
|
16
|
+
>
|
17
|
+
> http://devblog.avdi.org/2012/05/07/a-ruby-conversion-idiom/
|
18
|
+
|
19
|
+
*Why conversion methods?*
|
20
|
+
|
21
|
+
Because software is all about addressing a mapping problem. In my experience
|
22
|
+
using conversion methods has provided a means for easing the movement across
|
23
|
+
application design boundaries.
|
24
|
+
|
25
|
+
*Why use the PowerConverter gem?*
|
26
|
+
|
27
|
+
Excellent question.
|
28
|
+
|
29
|
+
**The short-answer is consistency**. **PowerConverter** helps you compose
|
30
|
+
conversions that have a common form.
|
31
|
+
|
32
|
+
**The longer-answer** is again related to consistency. By using a common
|
33
|
+
mechanism for definition, I'm hoping to reduce the nuanced variations that come
|
34
|
+
from crafting conversions. They all have a very similar shape, and I'd like to
|
35
|
+
provide tooling to help keep that shape.
|
36
|
+
|
37
|
+
I would much rather focus on other concepts than "is this conversion method
|
38
|
+
similar enough to its sibling conversion methods?"
|
39
|
+
|
40
|
+
In other words, relying on a common interface for defining a conversion method
|
41
|
+
reduces the number surprises when interacting with conversion methods.
|
42
|
+
|
43
|
+
## Usage
|
11
44
|
|
12
45
|
```ruby
|
13
46
|
PowerConverter.define_conversion_for :boolean do |input|
|
@@ -19,4 +52,8 @@ PowerConverter.define_conversion_for :boolean do |input|
|
|
19
52
|
end
|
20
53
|
|
21
54
|
PowerConverter.convert(object, to: :boolean)
|
55
|
+
|
56
|
+
# OR
|
57
|
+
|
58
|
+
PowerConverter.convert_to_boolean(object)
|
22
59
|
```
|
data/lib/power_converter.rb
CHANGED
@@ -22,10 +22,27 @@ module PowerConverter
|
|
22
22
|
# @param named_converter [#to_s]
|
23
23
|
# @param defined_converter_names [Array]
|
24
24
|
#
|
25
|
+
# You had one job...to register a converter. Now you get an exception.
|
26
|
+
#
|
25
27
|
# @example
|
26
28
|
# raise ConverterNotFoundError.new(:boolean, [:hello, :world])
|
27
29
|
def initialize(named_converter, defined_converter_names)
|
28
|
-
super("Unable to find PowerConverter for #{named_converter} in #{defined_converter_names.inspect}")
|
30
|
+
super("Unable to find PowerConverter for #{named_converter} in #{defined_converter_names.inspect}.")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# When you tried to convert something and it just won't convert, this is a
|
35
|
+
# great exception to raise.
|
36
|
+
class ConversionError < RuntimeError
|
37
|
+
# @param value [Object]
|
38
|
+
# @param named_converter [#to_s]
|
39
|
+
#
|
40
|
+
# Do or do not. There is no try.
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# raise ConversionError.new(:boolean, [:hello, :world])
|
44
|
+
def initialize(value, named_converter)
|
45
|
+
super("Unable to convert #{value.inspect} to '#{named_converter}'.")
|
29
46
|
end
|
30
47
|
end
|
31
48
|
|
@@ -37,6 +54,10 @@ module PowerConverter
|
|
37
54
|
# Responsible for defining a conversion method and a "shovel-ready" conversion
|
38
55
|
# module; because maybe you want a mixin for convenience reasons.
|
39
56
|
#
|
57
|
+
# @note If your defined converter returns `nil`, it is assumed if the
|
58
|
+
# conversion failed and a [PowerConverter::ConversionError] exception should
|
59
|
+
# be thrown.
|
60
|
+
#
|
40
61
|
# @param named_conversion [String,Symbol] the name of the conversion that you
|
41
62
|
# are declaring.
|
42
63
|
# @param converter [#call] the callable object that will perform the
|
@@ -55,6 +76,10 @@ module PowerConverter
|
|
55
76
|
# true
|
56
77
|
# end
|
57
78
|
# end
|
79
|
+
#
|
80
|
+
# PowerConverter.convert(object, to: :boolean)
|
81
|
+
# PowerConverter.convert_to_boolean(object)
|
82
|
+
#
|
58
83
|
# @see http://devblog.avdi.org/2012/05/07/a-ruby-conversion-idiom/ Avdi
|
59
84
|
# Grimm's post on "A Ruby Conversion Idiom"
|
60
85
|
#
|
@@ -72,27 +97,45 @@ module PowerConverter
|
|
72
97
|
# @api public
|
73
98
|
# @since 0.0.1
|
74
99
|
#
|
75
|
-
# Convert the given `value` via the named `:to` converter.
|
100
|
+
# Convert the given `value` via the named `:to` converter. As a short-circuit
|
101
|
+
# if the given `value` publicly responds to a `to_<named_converter>` it will
|
102
|
+
# use that.
|
76
103
|
#
|
77
104
|
# @param value [Object] the thing that you will be converting
|
78
105
|
# @param [Hash] options the options used to perform the conversion
|
79
106
|
# @option options [Symbol] :to the named_conversion that has been registered
|
80
107
|
#
|
108
|
+
# @return [Object] the resulting converted object
|
109
|
+
#
|
81
110
|
# @raise [ConverterNotFoundError] if the named converter is not found
|
111
|
+
# @raise [ConversionError] if the named converter returned a nil value
|
82
112
|
#
|
83
113
|
# @see PowerConverter.define_conversion_for
|
84
114
|
#
|
85
115
|
# @example
|
86
116
|
# PowerConverter.convert('true', to: :boolean)
|
87
117
|
#
|
88
|
-
# @
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
118
|
+
# @example
|
119
|
+
# class Foo
|
120
|
+
# def to_bar
|
121
|
+
# :hello_world
|
122
|
+
# end
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# PowerConverter.convert(Foo.new, to: :bar)
|
126
|
+
# => :hello_world
|
127
|
+
#
|
92
128
|
def convert(value, options = {})
|
93
|
-
|
129
|
+
named_converter = options.fetch(:to)
|
130
|
+
return value.public_send("to_#{named_converter}") if value.respond_to?("to_#{named_converter}", false)
|
131
|
+
returning_value = converter_for(named_converter).call(value)
|
132
|
+
return returning_value unless returning_value.nil?
|
133
|
+
fail ConversionError.new(value, named_converter)
|
94
134
|
end
|
95
135
|
|
136
|
+
# :nodoc:
|
137
|
+
CONVERSION_METHOD_PREFIX = "convert_to_".freeze
|
138
|
+
|
96
139
|
# @api public
|
97
140
|
# @since 0.0.1
|
98
141
|
#
|
@@ -113,11 +156,16 @@ module PowerConverter
|
|
113
156
|
# convert_to_boolean(@bar)
|
114
157
|
# end
|
115
158
|
# end
|
159
|
+
#
|
160
|
+
# @todo Allow for the inclusion of multiple power converter named types.
|
116
161
|
def module_for(named_conversion)
|
117
|
-
converter = converter_for(named_conversion)
|
118
162
|
Module.new do
|
119
|
-
|
120
|
-
|
163
|
+
# HACK: I'd prefer to not lean on calling the underlying convert method
|
164
|
+
# which means I will likely need some converter builder behavior.
|
165
|
+
define_method("#{CONVERSION_METHOD_PREFIX}#{named_conversion}") do |value|
|
166
|
+
PowerConverter.convert(value, to: named_conversion)
|
167
|
+
end
|
168
|
+
private "#{CONVERSION_METHOD_PREFIX}#{named_conversion}"
|
121
169
|
end
|
122
170
|
end
|
123
171
|
|
@@ -152,4 +200,36 @@ module PowerConverter
|
|
152
200
|
def defined_converter_names
|
153
201
|
@defined_conversions.keys
|
154
202
|
end
|
203
|
+
|
204
|
+
# :nodoc:
|
205
|
+
CONVERSION_METHOD_REGEXP = /\A#{CONVERSION_METHOD_PREFIX}(.*)\Z/.freeze
|
206
|
+
|
207
|
+
# :nodoc:
|
208
|
+
def method_missing(method_name, *args, &block)
|
209
|
+
named_converter = extract_named_converter_from(method_name)
|
210
|
+
if named_converter
|
211
|
+
convert(*args, to: named_converter)
|
212
|
+
else
|
213
|
+
super
|
214
|
+
end
|
215
|
+
end
|
216
|
+
private_class_method :method_missing
|
217
|
+
|
218
|
+
# :nodoc:
|
219
|
+
def extract_named_converter_from(method_name)
|
220
|
+
match = method_name.to_s.match(CONVERSION_METHOD_REGEXP)
|
221
|
+
match.captures[0] if match
|
222
|
+
end
|
223
|
+
private_class_method :extract_named_converter_from
|
224
|
+
|
225
|
+
# :nodoc:
|
226
|
+
def respond_to_missing?(method_name, include_private = false)
|
227
|
+
named_converter = extract_named_converter_from(method_name)
|
228
|
+
if named_converter
|
229
|
+
@defined_conversions.key?(named_converter.to_s)
|
230
|
+
else
|
231
|
+
super
|
232
|
+
end
|
233
|
+
end
|
234
|
+
private_class_method :respond_to_missing?
|
155
235
|
end
|