PowerConverter
About
PowerConverter exposes a means for defining a named conversion method.
Brief Example
PowerConverter.define_conversion_for(:boolean) do |input|
case input
when false, 0, '0', /\A(false|no)\Z/i, nil then false
when String
input.empty? ? nil : true
else
true
end
end
expect(PowerConverter.convert('no', to: :boolean)).to eq(false)
expect(PowerConverter.convert('yes', to: :boolean)).to eq(true)
expect { PowerConverter.convert('', to: :boolean) }.to raise_error(PowerConverter::ConversionError)
Details
What is a conversion method?
A well-established Ruby idiom for methods which "do the right thing" to convery any reasonable input value into a desired class.
Why conversion methods?
Because software is all about addressing a mapping problem. In my experience using conversion methods has provided a means for easing the movement across application design boundaries.
Why use the PowerConverter gem?
Excellent question.
The short-answer is consistency. PowerConverter helps you compose conversions that have a common form.
The longer-answer is again related to consistency. By using a common mechanism for definition, I'm hoping to reduce the nuanced variations that come from crafting conversions. They all have a very similar shape, and I'd like to provide tooling to help keep that shape.
I would much rather focus on other concepts than "is this conversion method similar enough to its sibling conversion methods?"
In other words, relying on a common interface for defining a conversion method reduces the number surprises when interacting with conversion methods.
Usage
Name and define conversions via a block.
PowerConverter.define_conversion_for(:always_true) { true }
PowerConverter.define_conversion_for(:always_nil) { nil }
PowerConverter.define_conversion_for(:boolean) { |value| !!value }
Call the conversions via a couple of methods:
PowerConverter.convert(nil, to: :always_true)
# or
PowerConverter.convert_to_always_true(nil)
When you call a conversion, if the conversion block evaluates to nil
, PowerConverter will fail with a PowerConverter::ConversionError
exception.
assert_raises(PowerConverter::ConversionError) { PowerConverter.convert(true, to: :always_nil) }
If you call the conversion and pass a block, the block will be the fallback for an evaluated nil
.
assert_equal('FALLBACK', PowerConverter.convert(true, to: :always_nil) { 'FALLBACK'} )
If the object you are attempting to convert responds to the to_<conversion_name>
method, that method will be called.
Thing = Struct.new(:to_always_nil)
thing = Thing.new("result of to_always_nil")
assert_equal("result of to_always_nil", PowerConverter.convert(thing, to: :always_nil))
You can also declare an alias for a registered PowerConverter
.
PowerConverter.define_alias(:true_or_false, is_alias_of: :boolean)
PowerConverter.convert("Hello", to: :boolean)
At times, a conversion may need additional information to be successful.
PowerConverter.define_conversion_for(:user_action) do |input, user|
case input
when String, Symbol then user.actions.find_by(name: input)
when Model::Action
input.user == user ? input : nil
end
end
PowerConverter.convert('start', to: :user_action, scope: a_user)
TODO
- Write about module declaration