Module: Pod4::TypeCasting

Defined in:
lib/pod4/typecasting.rb

Overview

A mixin to give you some more options to control how Pod4 deals with data types.

Example


class Foo < Pod4::Model
  include Pod4::TypeCasting

  class Interface < Pod4::SomeInterface
    # blah blah blah
  end
  set_interface Interface.new($stuff)

  attr_columns :name, :issue, :created, :due, :last_update, :completed, :thing

  # Now the meat
  force_encoding Encoding::UTF-8
  typecast :issue,         as: Integer, strict: true
  typecast :created, :due, as: Date
  typecast :last_update,   as: Time
  typecast :completed,     as: BigDecimal, ot_as: Float
  typecast :thing,         use: mymethod
end

So this adds two commands to the model DSL: force_encoding, and typecast. Both are optional.

Force Encoding


Pass this a Ruby encoding, and it will call force the encoding of each incoming value from the database to match. It is to work around problems with some data sources like MSSQL, which may deal with encoding poorly.

Typecasting


This has the syntax: ‘typecast <attr> [,…], <options>`.

Options are ‘as:`, `ot_as:`, `strict:` and `use:`. You must specify either `as:` or `use:`.

Valid types are BigDecimal, Float, Integer, Date, Time, and :boolean.

Changes to Behaviour of Model


General: Any attributes named using ‘typecast` are set `attr_reader` if they are not already so.

‘map_to_model`: incoming data from the data source is coerced to the given encoding if `force_encoding` has been used. Typecast attributes are cast as per their settings, or if they cannot be cast, are left alone. (Unless you have specified strict: true, in which case they are set to nil.)

‘set()`: typecast attributes are cast as per their settings, or if they cannot be cast, are left alone. (Unless you have specified `strict: true`, in which case they are set to nil.)

‘to_ot()`: any typecast attributes with `ot_as` are cast that way in the outgoing OT, and set guard that way too (see Octothorpe#guard) to give a reasonable default value instead of nil.

‘map_to_interface()`: typecast attributes are cast as per their settings, or if they cannot be cast, are set to nil.

Note: Typecasting does not prevent you from setting any value you please on a model attibute Additional methods


The following are provided:

  • ‘typecast?(:columnname, value)` returns true if the value can be cast; value defaults to the column value if not given.

  • ‘typecast(type, value, options)` returns a typecast value, or either the original value, or

nil if options is true.

  • ‘guard(octothorpe)` sets guard conditions on the given octothorpe, based on the attributes typecast knows about. If the value was nil, it will be a reasonable default for the type instead.

Custom Typecasting Methods


By specifying ‘use: my_method` you are telling Pod4 that you have a method that will return the typecast value for the type. This method will be called as `my_method(value, options)`, where value is the value to be typecast, and options is the hash of options you specified for that column. Pod4 will set the column to whatever your method returns.

The options hash will have an additional key :mode in case you need to cast differently in different circumstances. Mode will be one of :set, :map_to_interface, :map_to_model, or :typecast? (if you call ‘typecast?` yourself).

What you don’t get


None of this has any direct effect on validation, although of course we do provide methods such as ‘typecast?()` to specifically help you with validation.

Naming an attribute using ‘typecast` does not automatically make it a Pod4 column; you need to use `attr_column`, just as in plain Pod4. Furthermore, only Pod4 columns can be named in the typecast command, although you can use the `typecast` instance method, etc., to help you roll your own typecasting for non-column attributes.

Loss of information. If your column is typecast to Integer, then setting it to 12.34 will not round it to 12. Likewise, I know that Time.to_date is a thing, but we don’t support it.

Protection from nil, except when using ‘ot_as:`. A column is always allowed to be nil, regardless of how it is typecast. (On the contrary: by forcing strict columns to nil if they fail typecasting, we help you validate.)

It’s theoretically possible that you could typecast a column into something that the interface cannot cast back onto the database. We don’t cover you in that case. If it happens, you will have to deal with it yourself in ‘map_to_interface`.

Defined Under Namespace

Modules: ClassMethods, InstanceMethods

Constant Summary collapse

TYPES =
[ Date, Time, Integer, Float, BigDecimal, :boolean ]

Class Method Summary collapse

Class Method Details

.included(base) ⇒ Object

A little bit of magic, for which I apologise.

When you include this module it actually adds the methods in ClassMethods to the class as if you had called ‘extend TypeCasting:ClassMethds` AND adds the methods in InstanceMethods as if you had written `prepend TypeCasting::InstanceMethods`.

In my defence: I didn’t want to have to make you remember to do that…



138
139
140
141
# File 'lib/pod4/typecasting.rb', line 138

def self.included(base)
  base.extend  ClassMethods
  base.send(:prepend, InstanceMethods)
end