Foraneus

Foraneus allows to:

  • parse data coming from external sources (like an HTTP request).
  • convert data back to a raw representation suitable for being used at the outside, like an HTML form.

No matter which source of data is fed into Foraneus (external or internal), any instance can return raw and parsed data.

Basic usage

  • Declaration:
  class MyForm < Foraneus
    integer :delay
    float :duration
  end
  • From the outside:
  form = MyForm.parse(:delay => '5', :duration => '2.14')
  form.delay    # => 5
  form[:delay]  # => '5'
  form.data   # => { :delay => 5, :duration => 2.14 }
  form[]      # => { :delay => '5', :duration => '2.14' }
  • From the inside:
  form = MyForm.raw(:delay => 5, :duration => 2.14)
  form.delay    # => 5
  form[:delay]  # => '5'
  form.data   # => { :delay => 5, :duration => 2.14 }
  form[]      # => { :delay => '5', :duration => '2.14' }

Declaration

Declare source classes by inheriting from Foraneus base class.

  class MyForm < Foraneus
    field :delay, SomeCustomConverter.new
    float :duration
  end

Fields are declared by two ways:

  • calling .field
  • calling a shortcut method, like .float

There are shortcut methods for any of the built-in converters: boolean, date, decimal, float, integer, noop, and string.

When no converter is passed to .field, Foraneus::Converters::Noop is assigned to the declared field.

Instantiation

Foraneus instances can be obtained by calling two methods: parse and raw.

Use .parse when:

  • data is coming from outside of the system, like an HTTP request.

Use .raw when:

  • data is coming from the inside of the system, like a business layer.

Converters

Converters have two interrelated responsibilities:

  • Parse data, like the string "3,000", into an object, like 3_000.
  • Serialize data, like integer 3_000, into string "3,000"

A converter is an object that responds to #parse(s), #raw(v), and #opts methods.

When #parse(s) raises a StandardError exception, or any of its descendants, the exception is rescued and a Foraneus::Error instance is added to Foraneus#errors map.

#opts should return the opts hash used to instantiate the converter.

Built-in converters:

  • Boolean
  • Date
  • Decimal
  • Float
  • Integer
  • Noop
  • String

Validations

Foraneus only validates that external data can be converted to the specified types. Smart validations, like date range inclusion, are out of the scope of this gem.

#valid? and #errors are handy methods that tell whether a Foraneus instance is valid or not.

Valid instance:

  form.valid?     # => true
  form.errors   # => {}

Invalid one:

  form = MyForm.parse(:delay => 'INVALID')

  form.valid?                     # => false

  form.errors[:delay].key       # => 'ArgumentError'
  form.errors[:delay].message   # => 'invalid value for Integer(): "INVALID"'

#errors is a map in which keys correspond to field names, and values are instances of Foraneus::Error.

The name of the exception raised by #parse is the error's key attribute, and the exception's message is set to the error's message attribute.

Data coming from the inside is assumed to be valid, so .raw won't return an instance having errors neither being invalid.

Required fields

Fields can be declared as required.

  class MyForm < Foraneus
    integer :delay, :required => true
  end

If an external value is not fed into a required field, an error with key KeyError will be assigned.

  form = MyForm.parse

  form.valid?                       # => false

  form.errors[:delay].key         # => 'KeyError'
  form.errors[:delay].message     # => 'required parameter not found: "delay"'

Blank values

By default, any blank value is treated as nil.

  MyForm = Class.new(Foraneus) { string :name }

  MyForm.parse(:name => '').name
  # => nil

This behaviour can be modified by setting opt blanks_as_nil to false.

  MyForm = Class.new(Foraneus) { string :name, :blanks_as_nil => false }

  MyForm.parse(:name => '').name
  # => ''

Default values

Define fields with default values:

  MyForm = Class.new(Foraneus) { string :name, :default => 'Alice' }

Parse data from the ouside:

  form = MyForm.parse

  form.name             # => 'Alice'
  form[:name]           # => nil, because data from the outside
                        #    don't include any value

Convert values back from the inside:

  form = MyForm.raw

  form[:name]           # => 'Alice'
  form.name             # => nil, because data from the inside
                        #    don't include any value

Prevent name clashes

It is possible to rename methods #errors and #data so it will not conflict with defined fields.

  MyForm = Class.new(Foraneus) {
    field :errors
    field :data

    accessors[:errors] = :non_clashing_errors
    accessors[:data] = :non_clashing_data
  }
  form = MyForm.parse(:errors => 'some errors', :data => 'some data')

  form.errors                 # => 'some errors'
  form.data                   # => 'some data'

  form.non_clashing_errors    # []
  form.non_clashing_data      # { :errors => 'some errors', :data => 'some data' }

Installation

  • Install foraneus as a gem.

    gem install foraneus
    

Running tests

Tests are written in MiniTest. To run them all just execute the following from your command line:

  ruby spec/runner.rb

Execute the following when ruby 1.8.7:

  ruby -rubygems spec/runner.rb

To run a specific test case:

    ruby -Ispec -Ilib spec/lib/foraneus_spec.rb

When running under ruby 1.8.7:

    ruby -rubygems -Ispec -Ilib spec/lib/foraneus_spec.rb

Code documentation

Documentation is written in Yard. To see it in a browser, execute this command:

  yard server --reload

Then point the browser to http://localhost:8808/.

Badges

Build Status Code Climate

License

This software is licensed under the LGPL license.