power_converter 0.0.1 → 0.0.2
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 +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
|
+
[](http://badge.fury.io/rb/power_converter)
|
3
4
|
[](https://travis-ci.org/jeremyf/power_converter)
|
4
5
|
[](http://inch-ci.org/github/jeremyf/power_converter)
|
5
6
|
[](./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
|