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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ffe8e4ed7f1e0bdafe8e06f34abca7936df05eb
4
- data.tar.gz: 7be13e121f7797f707da2627fa048a7117d2888c
3
+ metadata.gz: 1bd5e2306a30c044d98fadc15a1a330cd485f34f
4
+ data.tar.gz: ebc9c3bed3649bd24f11fa6564e52a088a766d5a
5
5
  SHA512:
6
- metadata.gz: 6b61649071f7d32ed30c86ce4f4dc09a1a442f0d49c6ac8d255a7ab03b1e0c2b1326557e559a5e90a5d4b79fadca90c0e3f9628cd10ed05f6e46095cea677b2f
7
- data.tar.gz: a25a41c2d57a8241a307905ba9d9e6264d0133888bc56b181b52bddbc3beb0583b4816c7093234499bcd827b21be73c435a583e3c8353786832f76ca312aea33
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
- A placeholder for implementing a conversion module pattern.
8
+ ## About
8
9
 
9
- Exposing a means of registering conversions. These can be accessed at the module
10
- level:
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
  ```
@@ -1,5 +1,5 @@
1
1
  # :nodoc:
2
2
  module PowerConverter
3
3
  # :nodoc:
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
@@ -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
- # @todo I want to:
89
- # * raise an exception if I'm unable to convert an object
90
- # * auto-handle :to_<named_conversion> so I don't need to worry about
91
- # registering that.
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
- converter_for(options.fetch(:to)).call(value)
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
- define_method("convert_to_#{named_conversion}", &converter)
120
- private "convert_to_#{named_conversion}"
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: power_converter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Friesen