Need help with necromancer?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

piotrmurach
137 Stars 6 Forks MIT License 197 Commits 0 Opened issues

Description

Conversion from one object type to another with a bit of black magic.

Services available

!
?

Need anything else?

Contributors list

# 21,943
Ruby
Markdow...
Shell
ruby-cl...
188 commits
# 9,897
sidekiq
Koa
opam
Markdow...
2 commits
# 24,819
Ruby
Rails
inspec
tdd-uti...
1 commit
# 597,427
Elixir
Shell
coercio...
transfo...
1 commit

Necromancer

Gem Version Actions CI Build status Code Climate Coverage Status Inline docs

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 default
    false
  • copy
    - ensures only copy is modified, by default
    true

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
FalseClass
.
Necromancer.convert("no").to(:boolean) # => false

You can also convert an integer object to boolean:

Necromancer.convert(1).to(:boolean)  # => true
Necromancer.convert(0).to(:boolean)  # => false

3.3 DateTime

Necromancer knows how to convert string to

date
object:
Necromancer.convert("1-1-2015").to(:date)    # => "2015-01-01"
Necromancer.convert("01/01/2015").to(:date)  # => "2015-01-01"

You can also convert string to

datetime
:
Necromancer.convert("1-1-2015").to(:datetime)          # => "2015-01-01T00:00:00+00:00"
Necromancer.convert("1-1-2015 15:12:44").to(:datetime) # => "2015-01-01T15:12:44+00:00"

To convert a string to a time instance do:

Necromancer.convert("01-01-2015").to(:time)       # => 2015-01-01 00:00:00 +0100
Necromancer.convert("01-01-2015 08:35").to(:time) # => 2015-01-01 08:35:00 +0100
Necromancer.convert("12:35").to(:time)            # => 2015-01-04 12:35:00 +0100

3.4 Hash

With Necromancer you can convert a string with pairs delimited by

=
or
:
characters into a hash:
Necromancer.convert("a:1 b:2 c:3").to(:hash)
Necromancer.convert("a=1 b=2 c=3").to(:hash)
# => {a: "1", b: "2", c: "3"}

The pairs can be separated by

&
symbols and mix
=
and
:
pair delimiters:
Necromancer.convert("a:1 & b=2 & c:3").to(:hash)
# => {a: "1", b: "2", c: "3"}

You can also convert string to hash with integer values using

:int_hash
type:
Necromancer.convert("a:1 b:2 c:3").to(:int_hash)     # => {a: 1, b: 2, c: 3}
Necromancer.convert("a:1 b:2 c:3").to(:integer_hash) # => {a: 1, b: 2, c: 3}

Similarly you can convert string to hash with

float
or
numeric
values using
:float_hash
and
numeric_hash
types:
Necromancer.convert("a:1 b:2 c:3").to(:float_hash)    # => {a: 1.0, b: 2.0, c: 3.0}
Necromancer.convert("a:1 b:2.0 c:3").to(:num_hash)    # => {a: 1, b:2.0, c: 3}

String can also be converted to hash with boolean values using

:boolean_hash
or
:bool_hash
:
Necromancer.convert("a:yes b:no c:t").to(:bool_hash)  # => {a: true, b: false, c: true}

3.5 Numeric

Necromancer comes ready to convert all the primitive numeric values.

To convert a string to a float do:

Necromancer.convert("1.2a").to(:float)  #  => 1.2

Conversion to numeric in strict mode raises

Necromancer::ConversionTypeError
:
Necromancer.convert("1.2a").to(:float, strict: true) # => raises error

To convert a string to an integer do:

Necromancer.convert("1a").to(:integer)  #  => 1

However, if you want to convert string to an appropriate matching numeric type do:

Necromancer.convert("1e1").to(:numeric)   # => 10

3.6 Range

Necromancer is no stranger to figuring out ranges from strings. You can pass

,
,
-
,
..
,
...
characters to denote ranges:
Necromancer.convert("1,10").to(:range)  # => 1..10

Or to create a range of letters:

Necromancer.convert("a-z").to(:range)   # => "a".."z"

It will handle space characters:

Necromancer.convert("1 . . 10") >> :range   # => 1..10
Necromancer.convert("a . . . z") >> :range  # =>  "a"..."z"

3.7 Custom

In case where provided conversions do not match your needs you can create your own and

register
with Necromancer by using an
Object
or a
Proc
.

3.7.1 Using an Object

Firstly, you need to create a converter that at minimum requires to specify

call
method that will be invoked during conversion:
UpcaseConverter = Struct.new(:source, :target) do
  def call(value, options = {})
    value.upcase
  end
end

Inside the

UpcaseConverter
you have access to global configuration options by directly calling
config
method.

Then you need to specify what type conversions this converter will support. For example,

UpcaseConverter
will allow a string object to be converted to a new string object with content upper cased. This can be done:
upcase_converter = UpcaseConverter.new(:string, :upcase)

Necromancer provides the

register
method to add converter:
converter = Necromancer.new
converter.register(upcase_converter)   # => true if successfully registered

Finally, by invoking

convert
method and specifying
:upcase
as the target for the conversion we achieve the result:
converter.convert("magic").to(:upcase)   # => "MAGIC"

3.7.2 Using a Proc

Using a Proc object you can create and immediately register a converter. You need to pass

source
and
target
of the conversion that will be used later on to match the conversion. The
convert
allows you to specify the actual conversion in block form. For example:
converter = Necromancer.new

converter.register do |c| c.source= :string c.target= :upcase c.convert = proc { |value, options| value.upcase } end

Then by invoking the

convert
method and passing the
:upcase
conversion type you can transform the string like so:
converter.convert("magic").to(:upcase)   # => "MAGIC"

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/piotrmurach/necromancer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

  1. Fork it ( https://github.com/piotrmurach/necromancer/fork )
  2. Create your feature branch (
    git checkout -b my-new-feature
    )
  3. Commit your changes (
    git commit -am 'Add some feature'
    )
  4. Push to the branch (
    git push origin my-new-feature
    )
  5. Create a new Pull Request

License

The gem is available as open source under the terms of the MIT License.

Copyright

Copyright (c) 2014 Piotr Murach. See LICENSE for further details.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.