PowerConverter

Gem Version Build Status Code Climate Test Coverage Documentation Status APACHE 2 License

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.

http://devblog.avdi.org/2012/05/07/a-ruby-conversion-idiom/

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