Necromancer
Conversion from one object type to another with a bit of black magic.
Install / Use
/learn @piotrmurach/NecromancerREADME
Necromancer
Conversion from one object type to another with a bit of black magic.
Necromancer provides independent type conversion component for TTY toolkit.
Motivation
Conversion between Ruby core types frequently comes up in projects but is solved by half-baked solutions. This library aims to provide an independent and extensible API to support a robust and generic way to convert between core Ruby types.
Features
- Simple and expressive API
- Ability to specify own converters
- Ability to compose conversions out of simpler ones
- Support conversion of custom defined types
- Ability to specify strict conversion mode
Installation
Add this line to your application's Gemfile:
gem "necromancer"
And then execute:
$ bundle
Or install it yourself as:
$ gem install necromancer
Contents
1. Usage
Necromancer knows how to handle conversions between various types using the convert method. The convert method takes as an argument the value to convert from. Then to perform actual coercion use the to or more functional style >> method that accepts the type for the returned value which can be :symbol, object or ClassName.
For example, to convert a string to a range type:
Necromancer.convert("1-10").to(:range) # => 1..10
Necromancer.convert("1-10") >> :range # => 1..10
Necromancer.convert("1-10") >> Range # => 1..10
In order to handle boolean conversions:
Necromancer.convert("t").to(:boolean) # => true
Necromancer.convert("t") >> true # => true
To convert string to numeric value:
Necromancer.convert("10e1").to(:numeric) # => 100
You can convert string to array of values like boolean, integer or float:
Necromancer.convert("t,f,t"]).to(:booleans) # => [true, false, true]
Necromancer.convert("1,2.3,3.0"]).to(:integers) # => [1, 2, 3]
Necromancer.convert("1,2.3,3.0"]).to(:floats) # => [1.0, 2.3, 3.0]
To convert string to hash value:
Necromancer.convert("a:1 b:2 c:3").to(:hash) # => {a: "1", b: "2", c: "3"}
Necromancer.convert("a=1 b=2 c=3").to(:hash) # => {a: "1", b: "2", c: "3"}
To provide extra information about the conversion value type use the from:
Necromancer.convert(["1", "2.3", "3.0"]).from(:array).to(:numeric) # => [1, 2.3, 3.0]
Necromancer also allows you to add custom conversions.
When conversion isn't possible, a Necromancer::NoTypeConversionAvailableError is thrown indicating that convert doesn't know how to perform the requested conversion:
Necromancer.convert(:foo).to(:float)
# => Necromancer::NoTypeConversionAvailableError: Conversion 'foo->float' unavailable.
2. Interface
Necromancer will perform conversions on the supplied object through use of convert, from and to methods.
2.1 convert
For the purpose of divination, Necromancer uses convert method to turn source type into target type. For example, in order to convert a string into a range type do:
Necromancer.convert("1,10").to(:range) # => 1..10
Alternatively, you can use block:
Necromancer.convert { "1,10" }.to(:range) # => 1..10
Conversion isn't always possible, in which case a Necromancer::NoTypeConversionAvailableError is thrown indicating that convert doesn't know how to perform the requested conversion:
Necromancer.convert(:foo).to(:float)
# => Necromancer::NoTypeConversionAvailableError: Conversion 'foo->float' unavailable.
2.2 from
To specify conversion source type use from method:
Necromancer.convert("1.0").from(:string).to(:numeric)
In majority of cases you do not need to specify from as the type will be inferred from the convert method argument and then appropriate conversion will be applied to result in target type such as :numeric. However, if you do not control the input to convert and want to ensure consistent behaviour please use from.
The source parameters are:
:array:boolean:date:datetime:float:integer:numeric:range:string:time
2.3 to
To convert objects between types, Necromancer provides several target types. The to or functional style >> method allows you to pass target as an argument to perform actual conversion. The target can be one of :symbol, object or ClassName:
Necromancer.convert("yes").to(:boolean) # => true
Necromancer.convert("yes") >> :boolean # => true
Necromancer.convert("yes") >> true # => true
Necromancer.convert("yes") >> TrueClass # => true
By default, when target conversion fails the original value is returned. However, you can pass strict as an additional argument to ensure failure when conversion cannot be performed:
Necromancer.convert("1a").to(:integer, strict: true)
# => raises Necromancer::ConversionTypeError
The target parameters are:
:array:boolean,:booleans,:bools,:boolean_hash,:bool_hash:date:datetime,:float,:floats,:float_hash:integer,:integers,:ints,:integer_hash,:int_hash:numeric,:numerics,:nums,:numeric_hash,:num_hash:range:string:time
2.4 can?
To verify that a given conversion can be handled by Necromancer call can? with the source and target of the desired conversion.
converter = Necromancer.new
converter.can?(:string, :integer) # => true
converter.can?(:unknown, :integer) # => false
2.5 configure
You may set global configuration options on Necromancer instance by passing a block like so:
Necromancer.new do |config|
config.strict true
end
Or calling configure method:
converter = Necromancer.new
converter.configure do |config|
config.copy false
end
Available configuration options are:
strict- ensures correct types for conversion, by defaultfalsecopy- ensures only copy is modified, by defaulttrue
3. Converters
Necromancer flexibility means you can register your own converters or use the already defined converters for such types as Array, Boolean, Date, DateTime, Hash, Numeric, Range and Time.
3.1 Array
The Necromancer allows you to transform arbitrary object into array:
Necromancer.convert(nil).to(:array) # => []
Necromancer.convert({x: 1}).to(:array) # => [[:x, 1]]
In addition, Necromancer excels at converting , or - delimited string into an array object:
Necromancer.convert("a, b, c").to(:array) # => ["a", "b", "c"]
If the string is a list of - or , separated numbers, they will be converted to their respective numeric types:
Necromancer.convert("1 - 2 - 3").to(:array) # => [1, 2, 3]
It handles conversion of string into an array of boolean values as well:
Necromancer.convert("yes,no,t").to(:booleans) # => [true, false, true]
Necromancer.convert("1 - f - FALSE").to(:bools) # => [true, false, false]
You can also convert array containing string objects to array containing numeric values:
Necromancer.convert(["1", "2.3", "3.0"]).to(:numerics) # => [1, 2.3, 3.0]
Necromancer.convert(["1", "2.3", "3.0"]).to(:nums) # => [1, 2.3, 3.0]
Or you can be more specific by using :integers and :floats as the resulting type:
Necromancer.convert(["1", "2.3", "3.0"]).to(:integers) # => [1, 2, 3]
When in strict mode the conversion will raise a Necromancer::ConversionTypeError error like so:
Necromancer.convert(["1", "2.3", false]).to(:numerics, strict: true)
# => Necromancer::ConversionTypeError: false cannot be converted from `array` to `numerics`
However, in non-strict mode the value will be simply returned unchanged:
Necromancer.convert(["1", "2.3", false]).to(:numerics, strict: false)
# => [1, 2.3, false]
3.2 Boolean
The Necromancer allows you to convert a string object to boolean object. The 1, "1", "t", "T", "true", "TRUE", "y", "Y", "yes", "Yes", "on", "ON" values are converted to TrueClass.
Necromancer.convert("yes").to(:boolean) # => true
Similarly, the 0, "0", "f", "F", "false", "FALSE", "n", "N", "no", "No", "off", "OFF" values are converted to `Fa
