test/hobofields_api.rdoctest in hobofields-0.8.5 vs test/hobofields_api.rdoctest in hobofields-0.8.6
- old
+ new
@@ -1,43 +1,63 @@
+
# HoboFields API
-## Connect to the Database
-
In order for the API examples to run we need a connection to a database. You can ignore this if you're just looking for documentation.
+{.hidden}
+Our test requires rails:
+{.hidden}
+
>> require 'rubygems'
>> require 'activesupport'
- >> Dependencies.load_paths << '.'
- >> Dependencies.mechanism = :require
>> require 'activerecord'
>> require 'action_controller'
- >> require 'hobofields'
+{.hidden}
+
+We need a database connection for this test:
+{.hidden}
+
>> mysql_database = "hobofields_doctest"
+ >> system "mysqladmin --force drop #{mysql_database} 2> /dev/null"
>> system("mysqladmin create #{mysql_database}") or raise "could not create database"
>> ActiveRecord::Base.establish_connection(:adapter => "mysql",
:database => mysql_database,
:host => "localhost")
+{.hidden}
+Some load path manipulation you shouldn't need:
+{.hidden}
+
+ >> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobofields/lib')
+ >> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobosupport/lib')
+{.hidden}
+
+Require the module we're testing:
+{.hidden}
+
+ >> require 'hobosupport'
+ >> require 'hobofields'
+
## Example Models
Let's define some example models that we can use to demonstrate the API. With HoboFields we define the model's fields, with their name and type, directly in the model like so:
- >>
- class Advert < ActiveRecord::Base
- fields do
- title :string
- body :text
- contact_address :email_address
- end
- end
+ >>
+ class Advert < ActiveRecord::Base
+ fields do
+ title :string
+ body :text
+ contact_address :email_address
+ end
+ end
(Note: `: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.
- >> up, down = HoboFields::MigrationGenerator.run
- >> ActiveRecord::Migration.class_eval up
+ >> up, down = HoboFields::MigrationGenerator.run
+ >> ActiveRecord::Migration.class_eval up
We're now ready to start demonstrating the API
## The Basics
@@ -45,63 +65,62 @@
### Field Types
Field values are returned as the type you specify.
- >> a = Advert.new :body => "This is the body"
- >> a.body.class
- => HoboFields::Text
+ >> a = Advert.new :body => "This is the body"
+ >> a.body.class
+ => HoboFields::Text
This also works after a round-trip to the database
- >> a.save
- >> b = Advert.find(a.id)
- >> b.body.class
- => HoboFields::Text
+ >> a.save
+ >> b = Advert.find(a.id)
+ >> b.body.class
+ => HoboFields::Text
HoboFields::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::Text.new("hello")
- => "hello"
- >> t.class
- => HoboFields::Text
+ >> t = HoboFields::Text.new("hello")
+ => "hello"
+ >> t.class
+ => HoboFields::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 type='text'>` in an Advert form.
## Names vs. Classes
In the `fields do ... end` block you can give the field-type either as a name (symbol) or a class. For example, we could have said
- body HoboFields::Text
+ body HoboFields::Text
Obviously the symbol form is a nicer:
- body :text
+ body :text
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::Text::COLUMN_TYPE
- => :text
+ >> HoboFields::Text::COLUMN_TYPE
+ => :text
The full set of available symbolic names is
* `:integer`
- * `:big_integer`
* `:float`
* `:string`
* `:text`
* `:boolean`
* `:date`
* `:datetime`
* `:html`
* `:textile`
* `:markdown`
* `:password`
- * `:email_addresss`
+ * `:email_address`
You can add your own types too. More on that later.
## Model extensions
@@ -110,139 +129,141 @@
### `Model.attr_type`
Returns the type (i.e. class) declared for a given field or attribute
- >> Advert.attr_type :title
- => String
- >> Advert.attr_type :body
- => HoboFields::Text
+ >> Advert.attr_type :title
+ => String
+ >> Advert.attr_type :body
+ => HoboFields::Text
### `Model.column`
A shorthand for accessing column metadata
- >> col = Advert.column :title
- >> col.name
- "title"
- >> col.klass
- >> String
+ >> 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::Text
+ >>
+ class Advert
+ attr_accessor :my_attr, :type => :text
+ end
+ >> a = Advert.new
+ >> a.my_attr = "hello"
+ >> a.my_attr.class
+ => HoboFields::Text
## Field validations
HoboFields gives you some shorthands for declaring some common validations right in the field declaration
### Required fields
The `:required` argument to a field gives a `validates_presence_of`:
- >>
- class Advert
- fields do
- title :string, :required
- end
- end
- >> a = Advert.new
- >> a.valid?
- => false
- >> a.errors.full_messages
- => ["Title can't be blank"]
- >> a.title = "Jimbo"
- >> a.save
- => true
+ >>
+ class Advert
+ fields do
+ title :string, :required
+ end
+ end
+ >> a = Advert.new
+ >> a.valid?
+ => false
+ >> a.errors.full_messages
+ => ["Title can't be blank"]
+ >> a.title = "Jimbo"
+ >> a.save
+ => true
### Unique fields
The `:unique` argument in a field declaration gives `validates_uniqueness_of`:
- >>
- class Advert < ActiveRecord::Base
- fields do
- title :string, :unique
- end
- end
- >> a = Advert.new :title => "Jimbo"
- >> a.valid?
- => false
- >> a.errors.full_messages
- => ["Title has already been taken"]
- >> a.title = "Sambo"
- >> a.save
- => true
+ >>
+ class Advert < ActiveRecord::Base
+ fields do
+ title :string, :unique
+ end
+ 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:
- >> Dependencies.remove_constant "Advert"
- >>
- class Advert < ActiveRecord::Base
- fields do
- title :string
- body :text
- contact_address :email_address
- end
- end
+ >> ActiveSupport::Dependencies.remove_constant "Advert"
+ >>
+ class Advert < ActiveRecord::Base
+ fields do
+ title :string
+ body :text
+ contact_address :email_address
+ end
+ 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::EmailAddress
- >> a.contact_address.validate
- => "is not valid"
+ >> a = Advert.new :contact_address => "not really an email address"
+ >> a.contact_address.class
+ => HoboFields::EmailAddress
+ >> a.contact_address.validate
+ => "is not valid"
But normally that method would be called for us during validation:
- >> a.valid?
- => false
- >> a.errors.full_messages
- => ["Contact address is not valid"]
- >> a.contact_address = "me@me.com"
- >> a.valid?
- => true
+ >> a.valid?
+ => false
+ >> a.errors.full_messages
+ => ["Contact address is not valid"]
+ >> a.contact_address = "me@me.com"
+ >> a.valid?
+ => true
You can add this capability to your own rich types just by defining `#validate`
### 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
- end
+ >>
+ class Advert
+ attr_accessor :alternative_email, :type => :email_address
+ end
By default, virtual fields are not subject to validation.
- >> a = Advert.new :alternative_email => "woot!"
- >> a.valid?
- => true
+ >> a = Advert.new :alternative_email => "woot!"
+ >> a.valid?
+ => true
To have them validated use `validate_virtual_field`:
- >>
- class Advert
- validate_virtual_field :alternative_email
- end
- >> a.valid?
- => false
+ >>
+ class Advert
+ validate_virtual_field :alternative_email
+ end
+ >> a.valid?
+ => false
-## Cleanup
+Cleanup
+{.hidden}
- >> system "mysqladmin --force drop #{mysql_database}"
+ >> system "mysqladmin --force drop #{mysql_database} 2> /dev/null"
+{.hidden}