In order for the API examples to run we need to load the rails generators of our testapp:
doctest: prepare testapp environment doctest_require: 'prepare_testapp'
Let’s define some example models that we can use to demonstrate the API. With HoboFields we can use the ‘hobo:model’ generator like so:
$ rails generate hobo:model advert title:string body:text contact_address:email_address
This will generate the test, fixture and a model file like this:
>> Rails::Generators.invoke 'hobo:model', %w(advert title:string body:text contact_address:email_address)
class Advert < ActiveRecord::Base fields do title :string body :text contact_address :email_address end attr_accessible :title, :body, :contact_address end
:email_address is an example of a “Rich Type” provided by HoboFields – more on those later)
The migration generator uses this information to create a migration. The following creates and runs the migration so we’re ready to go.
$ rails generate hobo:migration -n -m
We’re now ready to start demonstrating the API
>> Rails::Generators.invoke 'hobo:migration', %w(-n -m) >> Rails::Generators.invoke 'hobo:migration', %w(-n -m)
The main feature of HoboFields, aside from the migration generator, is the ability to declare rich types for your fields. For example, you can declare that a field is an email address, and the field will be automatically validated for correct email address syntax.
Field values are returned as the type you specify.
>> a = Advert.new :body => "This is the body" >> a.body.class => HoboFields::Types::Text
This also works after a round-trip to the database
>> a.save >> b = Advert.find(a.id) >> b.body.class => HoboFields::Types::Text
HoboFields::Types::Text is a simple subclass of string. It’s a “wrapper type”, by which we mean you pass the underlying value to the constructor.
>> t = HoboFields::Types::Text.new("hello") => "hello" >> t.class => HoboFields::Types::Text
If you define your own rich types, they need to support a one argument constructor in the same way.
Although the body of our advert is really just a string, it’s very useful that it has a different type. For example, the view layer in Hobo Rapid would use this information to render a
<textarea> rather than an <input> in an Advert form.
Names vs. Classes
fields do ... end block you can give the field-type either as a name (symbol) or a class. For example, we could have said
Obviously the symbol form is a nicer:
If you provide a class it must define the
COLUMN_TYPE constant. This instructs the migration generator to create the appropriate underlying database column type. It should be a symbol that is a valid column type in a Rails migration.
>> HoboFields::Types::Text::COLUMN_TYPE => :text
The full set of available symbolic names is
You can add your own types too. More on that later.
HoboFields adds a few features to your models.
Returns the type (i.e. class) declared for a given field or attribute
>> Advert.connection.schema_cache.clear! >> Advert.reset_column_information >> Advert.attr_type :title => String >> Advert.attr_type :body => HoboFields::Types::Text
A shorthand for accessing column metadata
>> col = Advert.column :title >> col.name => "title" >> col.klass >> String
Model.attr_accessor with types
In your HoboFields models you can also give type information to “virtual fields” (i.e. regular Ruby attributes)
>> class Advert attr_accessor :my_attr, :type => :text end >> a = Advert.new >> a.my_attr = "hello" >> a.my_attr.class => HoboFields::Types::Text
HoboFields gives you some shorthands for declaring some common validations right in the field declaration
:required argument to a field gives a
>> class Advert fields do title :string, :required end attr_accessible :title end >> a = Advert.new >> a.valid? => false >> a.errors.full_messages => ["Title can't be blank"] >> a.title = "Jimbo" >> a.save => true
:unique argument in a field declaration gives
>> class Advert < ActiveRecord::Base fields do title :string, :unique end attr_accessible :title end >> a = Advert.new :title => "Jimbo" >> a.valid? => false >> a.errors.full_messages => ["Title has already been taken"] >> a.title = "Sambo" >> a.save => true
Let’s get back to the basic Advert class with no validations before we continue:
>> ActiveSupport::Dependencies.remove_constant "Advert" >> class Advert < ActiveRecord::Base fields do title :string body :text contact_address :email_address end attr_accessible :title, :body, :contact_address end
Type specific validations
Rich types can define there own validations by a
#validate method. It should return an error message if the value is invalid, otherwise nil. We can call that method directly to show how it works:
>> a = Advert.new :contact_address => "not really an email address" >> a.contact_address.class => HoboFields::Types::EmailAddress >> a.contact_address.validate => "is invalid"
But normally that method would be called for us during validation:
>> a.valid? => false >> a.errors.full_messages => ["Contact address is invalid"] >> a.contact_address = "email@example.com" >> a.valid? => true
You can add this capability to your own rich types just by defining
Validating virtual fields
You can set the type of a virtual field to a rich type, e.g.
>> class Advert attr_accessor :alternative_email, :type => :email_address attr_accessible :alternative_email end
By default, virtual fields are not subject to validation.
>> a = Advert.new :alternative_email => "woot!" >> a.valid? => true
To have them validated use
>> class Advert validate_virtual_field :alternative_email end >> a.valid? => false
Edit this page