1 Ubiquo scaffolding
1.1 Creating an Ubiquo Resource
Similarly to the Rails scaffold, ubiquo_scaffold creates code to handle resources on an application. It creates a model (with tests), migration, route, ubiquo controller (with tests) and ubiquo views. As an example, let’s create a scaffold for a Book model:
script/generate ubiquo_scaffold book title:string published_at:datetime author_id:integer
REST resources for this model will be created automatically. Check it out:
map.namespace :ubiquo do |ubiquo|
ubiquo.resources :books
...
end
If we have in our application a ubiquo_i18n plugin, we can generate a ubiquo_scaffold with —translatable param. Learn more about this in the Ubiquo i18n guide.
The ubiquo scaffold will generate the following files in your application, along with some folders and edit one more. Here’s a quick overview of what it creates:
| File | Purpose |
|---|---|
| app/views/ubiquo/books/index.html.erb | A view to display an index of all books |
| app/views/ubiquo/books/new.html.erb | A view to create a new book |
| app/views/ubiquo/books/edit.html.erb | A view to edit an existing book |
| app/views/ubiquo/books/show.html.erb | A view to display a single book |
| app/views/ubiquo/books/_form.html.erb | A partial to display a form. Used by new and edit views |
| app/views/ubiquo/books/_submenu.html.erb | A partial to display a navigation submenu. It calls books_navlinks partial. Used by all views |
| app/views/ubiquo/books/_title.html.erb | A partial to display a section title. Used by all views |
| app/views/ubiquo/books/_book.html.erb | A partial to display a single book. Used by preview |
| app/views/navigators/_books_navlinks.html.erb | A partial to display a navigation submenu. Called by submenu partial |
| app/models/book.rb | The Book model |
| test/unit/book_test.rb | Unit testing harness for the book model |
| test/fixtures/books.yml | Dummy books for use in testing |
| config/locales/ca/models/book.yml | Catalan book model locales |
| config/locales/es/models/book.yml | Spanish book model locales |
| db/migrate/20090529095904_create_books.rb | Migration to create a books table in your database (your name will include a different timestamp) |
| app/controllers/ubiquo/books_controller.rb | The books controller |
| test/functional/ubiquo/books_controller_test.rb | Functional testing harness for the books controller |
| app/helpers/ubiquo/books_helper.rb | Helper functions to be used from the books ubiquo views |
In addition it will also update the following existings files:
| File | Purpose |
|---|---|
| config/locales/ca/ubiquo.yml | Catalan book ubiquo scaffold locales |
| config/locales/es/ubiquo.yml | Spanish book ubiquo scaffold locales |
| config/locales/en/ubiquo.yml | English book ubiquo scaffold locales |
| config/locales/ca/models.yml | Catalan book model and attributes translations |
| config/locales/es/models.yml | Spanish book model and attributes translations |
| config/locales/en/models.yml | English book model and attributes translations |
Keep in mind that due to yaml limitations with UTF-8 output it is useful to have the ya2yaml gem installed.
1.2 Running a Migration
One of the products of the script/generate scaffold command is a database migration.
If you look in the db/migrate/20090529095904_create_books.rb file (remember, your file name will contain a different timestamp), here’s what you’ll find:
class CreateBooks < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.string :title
t.datetime :published_at
t.integer :author_id
t.timestamps
end
end
def self.down
drop_table :books
end
end
At this point, you can use a rake command to run the migration:
$ rake db:migrate
Because you’re working in the development environment by default, this command will apply to the database defined in the development section of your config/database.yml file.
It is possible to run the migration automatically if you pass the -m flag to the ubiquo_scaffold command.
1.3 Configuring menus and navigation tabs
A tab should be automatically added on the main left navigation bar, you can review the file app/views/navigators/_main_navtabs.html.erb to customize it:
<%
navigator_left = create_tab_navigator(:tab_options => {}) do |navigator|
...
navigator.add_tab do |tab|
tab.text = t("application.books")
tab.title = t("application.goto", :place => "books")
tab.link = ubiquo_books_path
tab.highlights_on({:controller => "ubiquo/books"})
tab.highlighted_class = "active"
end if permit?("books_management")
...
end
%>
<%= render_tab_navigator(navigator_left) %>
Note that the books_management permission is not automatically created. You can learn the detailed way to manage permissions in the Ubiquo authentication guide.
In app/views/ubiquo/books/_submenu.html.erb is default helper call used to display a navigation ubiquo submenu.
<%= render_navigation_links_section :books %>
At this moment the navigation menu is read from app/views/navigators/_books_navlinks.html.erb. This is generated automatically, check it out:
<%
navigator_section = create_link_navigator(:class => 'submenu') do |navigator|
...
navigator.add_link do |link|
link.text = t("ubiquo.book.index.title")
link.url = ubiquo_books_path
link.highlights_on({:controller => "ubiquo/books"})
end
...
end
%>
<%= render_link_navigator(navigator_section) %>
Imagine now that you want to add an authors ubiquo resource. If you need it to appear on the same tab that books_, edit the books navigation file app/views/navigators/_booksnavlinks.html.erb and add an authors link:
<%
navigator_section = create_link_navigator(:class => 'submenu') do |navigator|
...
navigator.add_link do |link|
link.text = t("ubiquo.book.index.title")
link.url = ubiquo_books_path
link.highlights_on({:controller => "ubiquo/books"})
end
navigator.add_link do |link|
link.text = t("ubiquo.author.index.title")
link.url = ubiquo_authors_path
link.highlights_on({:controller => "ubiquo/authors"})
end
end
%>
<%= render_link_navigator(navigator_section) %>
You also have to indicate on the main tab navigation app/views/navigators/_main_tabnavs.html.erb to select the Books tab when the item authors is selected:
#app/views/navigators/_main_navtabs.html.erb
<% navigator_left = create_tab_navigator(:tab_options => {}) do |navigator|
...
navigator.add_tab do |tab|
tab.text = t("Books")
tab.title = t("application.goto", :place => "books"")
tab.link = ubiquo_books_path
tab.highlights_on({:controller => "ubiquo/books"})
tab.highlights_on({:controller => "ubiquo/authors"})
tab.highlighted_class = "active"
end if permit?("books_management")
...
end
%>
<%= render_tab_navigator(navigator_left) %>
1.4 Views, Helpers, and Controllers Overview
To understand and learn how use and extend all generated files with ubiquo scaffold, we’ll review action by action trying cover all cases.
1.4.1 Index
To start, check out index action in app/controllers/ubiquo/books_controller.rb, we can view that this action only contains a call to filtered_search Book class method. The results are used for paginate method, implemented in ubiquo_core plugin.
The params that paginated_filtered_search method needs are explained with more details in the filters section
class Ubiquo::BooksController < UbiquoController
# GET /books
# GET /books.xml
def index
@books_pages, @books = Book.paginated_filtered_search(params)
respond_to do |format|
format.html # index.html.erb
format.xml {
render :xml => @books
}
end
end
...
end
With new ubiquo version, we have reduced the code generated in index view and moved it to the helper. With this form, if we need do any change, we work with helper usually instead of view.
<h1>
<%= render :partial => 'title' %>
</h1>
<h2>
<%= render :partial => 'submenu' %>
</h2>
<%= render :partial => "shared/ubiquo/feedback" %>
<%= show_filter_info %>
<%= book_list(@books, @books_pages) %>
<% content_for :sidebar do %>
<%= show_filters %>
<%= help_block_sidebar(t("ubiquo.book.index.help_message")) %>
<% end %>
The books_list method renders standard partial located in ubiquo_core plugin. Let’s see its parameters:
Required locals:
- name – The name of the model listed.
- headers – An array with columns headers. If the element is a symbol, it’s considerated how a model attribute and list can be sorted by this field. If element is a string, it insn’t sortable.
- rows – An array with each row of the list. Each row is a hash:
- id – The id of this element
- columns – An array with the columns values of this element
- actions – A result of actions helper method
- pages – Pagination for this list
- link_to_new – Link to be used as a button for creating a new model
Optional locals:
- actions_width – Width (in pixels) of the Actions column (default is 100)
- hide_actions – Set to true to hide actions (default is false)
The books_actions method returns an array containing all the actions that we want to perform on the instances of the models. By default those actions are edit and delete.
And an example of books ubiquo helper:
module Ubiquo::BooksHelper
... filters helpers ...
def book_list(collection, pages, options = {})
render(:partial => "shared/ubiquo/lists/standard", :locals => {
:name => 'book',
:headers => [:title, :published_at, :author_id],
:rows => collection.collect do |book|
{
:id => book.id,
:columns => [
book.title,
book.published_at,
book.author_id,
],
:actions => book_actions(book)
}
end,
:pages => pages,
:link_to_new => link_to(t("ubiquo.book.index.new"),
new_ubiquo_book_path, :class => 'new')
})
end
private
def book_actions(book, options = {})
actions = []
actions << link_to(t("ubiquo.edit"), [:edit, :ubiquo, book])
actions << link_to(t("ubiquo.remove"), [:ubiquo, book],
:confirm => t("ubiquo.book.index.confirm_removal"), :method => :delete
)
actions
end
end
You can also use related fields as columns (ubiquo_categories should work too), an example:
def article_list(collection, pages, options = {})
render(:partial => "shared/ubiquo/lists/standard", :locals => {
:name => 'article',
:headers => [:title, :description, :"author.name", :"section.name", :publish_date],
:rows => collection.collect do |article|
{
:id => article.id,
:columns => [
article.title,
article.description,
article.author.try(:name),
article.section.try(:name),
article.publish_date,
],
:actions => article_actions(article)
}
end,
:pages => pages,
:link_to_new => link_to(t("ubiquo.article.index.new"),
new_ubiquo_article_path, :class => 'new')
})
end
2 Adding extra data to scaffold initialization
By now, we have seen how to create a scaffold for a given model. It creates physical files and some code to get it working. This section is focused on expanding the previous behaviour and introducing new features.
2.1 Adding media
Now it is possible to add media from scaffold. Media resources will appear in model, as a media_attachment directive, and in form, with a media selector attached to a new form box.
script/generate ubiquo_scaffold Book title:string published_at:datetime author_id:integer --media media_field_1,media_field_2
The previous directive will generate two media relations through media_field_1 and media_field_2 names.
2.2 Adding categories
Similarly as adding media, we can categorize our model from scaffold. It will generate a categorization in model and a category selector in form.
script/generate ubiquo_scaffold book title:string published_at:datetime author_id:integer --categorized category_set_1,category_set_2
This directive will generate two category relations from our model to category_set_1 and category_set_2.
2.3 Adding model relations
Finally, and in the same way as the previous sections, we will be able to generate relations with other models. All relations created by scaffold will benefit of relation selector helper on forms and will generate a properly relation on model (has_many or belongs_to, depending on the params supplied).
script/generate ubiquo_scaffold book title:string published_at:datetime author_id:integer --belongs_to author --has_many pages
The previous directive will generate a has_many relation to Page model and a belongs_to relation to Author through author_id field. Both relations will be seen in form as relation selector helpers.
This guide is not finished yet.