DRY CRUD generates simple and extendable controller, views and helpers that support you to DRY up the CRUD code in your Rails project. Start with these elements and build a clean base to efficiently develop your application upon.
Create your Rails application directly with the DRY CRUD application template:
rails new APP_NAME -m https://raw.github.com/codez/dry_crud/master/template.rb
If your application already exists or you prefer the DIY way, then install
the Gem (gem install dry_crud), add it to your Gemfile and run
the generator. You may remove the Gemfile entry again afterwards, it is not
required anymore.
rails generate dry_crud [--templates haml] [--tests rspec]
By default, DRY CRUD generates ERB templates and Test::Unit tests. Pass the options above to generate HAML templates and/or RSpec examples instead.
To integrate DRY CRUD into your code, only a few additions are required:
For uniform CRUD functionality, just subclass your controllers from
CrudController.
Overwrite the :to_s method of your models for a human-friendly
representation in captions.
Version 1.5 and higher are built for Rails 3.2. As of Rails 3.1, views are inheritable as well, so a core functionality of DRY CRUD got into the Rails core itself. For Rails 3.0, use version 1.3.1 or refer to the rails-3.0 branch. If you need a version for Rails 2.3, please get version 0.6.0 of the gem or go to the rails-2.3 branch on Github. DRY CRUD 1.3 and above are fully compatible with Ruby 1.8.7, Ruby 1.9.2 and JRuby.
In most Rails applications, you have some models that require basic CRUD (create, read, update, delete) functionality. There are various possibilities like Rails scaffolding, Inherited Resources or Dry Scaffold. Still, various parts in your application remain duplicated. While you might pull up common methods into a common superclass controller, most views still contain very similar code. And then you also have to remember the entire API of these frameworks.
Enter DRY CRUD.
The main idea of DRY CRUD is to concentrate basic functionality of your application, like CRUD actions, uniform formatting, forms and tables into specifically extendable units. DRY CRUD generates various foundation classes that you may browse easily and adapt freely to your application’s needs. For each model, you may transparently customize arbitrary parts or just fallback to the general behavior. This applies not only for controllers, but also for view templates and helpers. There is no black box your code depends on. You lay the foundation that fits your application best.
DRY CRUD is a Rails generator. All code resides in your application and is open for you to inspect and to extend. You may pick whatever you consider useful or adapt what is not sufficient. Even if you do not require any CRUD functionality, you might find some helpers simplifying your work. There are no runtime dependencies to the dry_crud gem. Having said this, DRY CRUD does not want to provide a maximum of functionality that requires a lot of configuration, but rather a clean and lightweight foundation to build your application’s requirements upon. This is why DRY CRUD comes as a generator and not as a Rails plugin.
DRY CRUD does not depend on any other plugins, but easily allows you to
integrate them in order to unify the behavior of your CRUD controllers. You
might even use the plugins mentioned above to adapt your generated
CrudController base class. All classes come with thorough
tests that provide you with a solid foundation for implementing your own
adaptions.
A basic CSS gets you started with your application’s layout. For advanced needs, DRY CRUD supports the styles and classes used in Bootstrap. A great design never was so close.
If you find yourself adapting the same parts of DRY CRUD for your applications over and over, please feel free to fork me on Github.
See the Examples section for some use cases and the Generated Files section below for details on the single classes and templates.
Say you want to manage a Person model. Create the following
controller and overwrite the :to_s method of your model for a
human-friendly representation used in page titles.
app/controllers/people_controller.rb:
class PeopleController < CrudController end
app/models/person.rb:
class Person def to_s "#{lastname} #{firstname}" end end
That’s it. You have a sortable overview of all people, detail pages and forms to edit and create persons. Oh, and of course, you may delete persons as well. By default, all attributes are displayed and formatted according to their column type wherever they appear. This holds for the input fields as well.
Well, maybe there are certain attributes you do not want to display in the
people list, or others that are not editable. No problem, simply create a
_list partial in app/views/people/_list.html.erb
to customize this:
<%= crud_table :lastname, :firstname, :city, :sex %>
This only displays these three attributes in the table. All other
templates, as well as the main index view, fallback to the ones in
app/views/crud.
Next, let’s adapt a part of the general behavior used in all CRUD controllers. As an example, we include pagination with kaminari in all our overview tables:
In app/controllers/list_controller.rb, change the list_entries
method to
def list_entries model_scope.page(params[:page]) end
In app/views/list/index.html.erb, add the following line for
the pagination links:
<%= paginate entries %>
And we are done again. All our controllers inheriting from
ListController, including above PeopleController,
now have paginated index views. Because our customization for the people
table is in the separate _list partial, no further
modifications are required.
Sometimes, the default formatting provided by :format_attr
will not be sufficient. We have a boolean column sex in our
model, but would like to display ‘male’ or ‘female’ for it (instead
of ‘no’ or ‘yes’, which is a bit cryptic). Just define a method in
your view helper starting with format_, followed by the class
and attribute name:
In app/helpers/people.rb:
def format_person_sex(person) person.sex ? 'female' : 'male' end
Should you have attributes with the same name for multiple models that you
want to be formatted the same way, you may define a helper method
format_{attr} for these attributes.
By the way: The method :f in StandardHelper
uniformly formats arbitrary values according to their class.
There is a simple search functionality (based on SQL LIKE queries)
implemented in the CrudController. Define an array of columns
in your controller’s search_columns class variable to make
the entries searchable by these fields:
In app/controllers/people_controller.rb:
self.search_columns = [:firstname, :lastname]
If you have columns defined, a search box will be displayed in the index view that let’s you filter the displayed entries.
As a last example, let’s say we have added a custom input field that must
specially processed. Instead of overwriting the entire update action, it is
possible to register callbacks for the create,
update, save (= create and
update) and destroy actions. They work very
similarliy like the callbacks on ActiveRecord. For each action, before and
after callbacks are run. Before callbacks may also prevent the action from
being executed when returning false. Here is some code:
In app/controllers/people_controller.rb:
after_save :upload_picture before_destroy :delete_picture def upload_picture store_file(params[:person][:picture]) if params[:person][:picture] end def delete_picture if !perform_delete_picture(entry.picture) flash.alert = 'Could not delete picture' false end end
Beside these “action” callbacks, there is also a set of “before
render” callbacks that are called whenever a certain view is rendered.
They are available for the index, show,
new, edit and form (=
new and edit) views. These callbacks are not only
called for the corresponding action, but, for example, also when the
new view is going to be rendered from an unsuccessfull
create action. Say you need to prepare additional variables
whenever the form is rendered:
In app/controllers/people_controller.rb:
before_render_form :set_hometowns def set_hometowns @hometowns = City.where(:country => entry.country) end
DRY CRUD also provides two builder classes for update/create forms and
tables for displaying entries of one model. They may be used all over your
application to DRY up the form and table code. Normally, they are used with
the corresponding methods from StandardHelper. When you define
a view for a subclass of CrudController, you may also use the
slightly enhanced crud_table and crud_form
methods from CrudHelper.
This is the code to define a table with some attribute columns for a list of same-type entries. Columns get a header corresponding to the attribute name:
<%= table(@people) do |t|
t.sortable_attrs :lastname, :firstname
end %>
If entries is empty, a basic ‘No entries found’ message is rendered instead of the table.
To render custom columns, use the :col method:
<%= table(@people) do |t|
t.sortable_attrs :lastname, :firstname
t.col('', :class => 'center') {|entry| image_tag(entry.picture) }
t.attr :street
t.col('Map') {|entry| link_to(entry.city, "http://maps.google.com/?q=#{entry.city}" }
end %>
Forms work very similar. In the most simple case, you just have to specify which attributes of a model to create input fields for, and you get a complete form with error messages, labeled input fields according the column types and a save button:
<%= crud_form(@person, :firstname, :lastname, :age, :city) -%>
Of course, custom input fields may be defined as well:
<%= crud_form(@person, :url => custom_update_person_path(@person.id)) do |f| %>
<%= f.labeled_input_fields :firstname, :lastname %>
<%= f.labeled(:sex) do %>
<%= f.radio_button :sex, true %> female
<%= f.radio_button :sex, false %> male
<% end %>
<%= f.labeled_integer_field :age %>
<%= f.labeled_file_field :picture %>
<% end %>
Even belongs_to associations are automatically rendered with a
select field. By default, all entries from the associated model are used as
options. To customize this, either define an instance variable with the
same name as the association in your controller, or pass a
:list option:
<%= f.belongs_to_field :hometown, :list => City.where(:country => @person.country) %>
Yes, it’s bad practice to use finder logic in your views! Define the
variable @hometowns in your controller instead (as shown in
the example above), and you do not even have to specify the
:list option.
Optionally, has_and_belongs_to_many and has_many
associations can be rendered with a multi-select field. Similar to a
belongs_to association, all entries from the associated model
are used, but can be overwritten using the :list option:
<%= f.has_many_field :visited_cities, :list => City.where(:is_touristic => true) %>
And yes again, the same advice for where to put finder logic applies here as well.
Note: has_and_belongs_to_many and
has_many associations are not automatically rendered in a
form, you have to explicitly include these attributes. You might also want
to stylize the multi-select widget, for example with a jQuery UI Multiselect.
In case you define nested resources, your CrudController
subclass should know. Listing and creating entries as well as displaying
links for these resources is dependent on the nesting hierarchy. This is
how you declare the namespaces and parent resources in your controller:
self.nesting = :my_namspace, ParentResource
This declaration is for a controller nested in
parent_resources within a :my_namespace scope.
ParentResource is the corresponding ActiveRecord
model. The request param :parent_resource_id is used to load
the parent entry, which in turn is used to filter the entries listed and
created in your controller. For all parent resources, a corresponding
instance variable is created.
The ListController::Nesting module defines this basic
behaviour. For more complex setups, have a look there and adjust it to your
needs.
All text strings used are externalized to an english locale yaml. The keys are organized by controller and template name plus a generic global scope.
To represent your controller hierarchy, a special translation helper
:ti looks up keys along the hierarchy in the following order:
{controller_name}.{template_name}.{key}
{controller_name}.{action_name}.{key}
{controller_name}.global.{key}
{parent_controller_name}.{template_name}.{key}
{parent_controller_name}.{action_name}.{key}
{parent_controller_name}.global.{key}
...
global.{key}
In order to change the title for your PeopleController‘s
index action, you do not need to override the entire template,
but simply define the following key:
people.index.title = "The People"
Otherwise, the lookup for the title would fallback on the
ListController‘s key list.index.title.
This lookup mechanism also allows you to easily define per-controller overridable text snippets in your views.
All generated files are supposed to provide a reasonable foundation for the CRUD functionality. You are encouraged to adapt them to fit the needs of your application. They’re yours!
Abstract controller providing basic CRUD actions. This implementation
mainly follows the one of the Rails scaffolding controller and responses to
HTML and XML requests. Some enhancements were made to ease extendability.
Several protected helper methods are there to be (optionally) overriden by
subclasses. With the help of additional callbacks, it is possible to hook
into the action procedures without overriding the entire method. This class
is based on ListController.
Abstract controller providing a basic list action. Use this controller if you require read-only functionality. There are two sub-modules that provide search and sort functionality for the table displayed in the list action. A third sub-module remembers the list parameters in order to return to an identical list.
A view helper to standardize often used functions like formatting, tables,
forms or action links. This helper is ideally defined in the
ApplicationController. It is required to use the
StandardTableBuilder and the StandardFormBuilder.
A small helper for CrudController to render tables and forms
with a default set of attributes.
A small helper for ListController to render the list table
with a default set of attributes.
A simple helper object to easily define tables listing several rows of the same data type.
A form builder that automatically selects the corresponding input type for ActiveRecord columns. Input elements are rendered together with a label by default.
All templates in the list and crud folders may be
‘overriden’ individually in a respective view folder. Define the basic
structure of your CRUD views here and adapt it as required for each single
model. Actually, the _list.html.erb partial from the
list folder gets overriden in the crud folder
already.
The index view displaying a sortable table with all entries. If you have
search_columns defined for your controller, then a search box
is rendered as well.
A partial defining the table in the index view. To change the displayed
attributes for your list model, just create an own
_list.html.erb in your controller’s view directory.
A partial defining a simple search form that is displayed when
search_columns are defined in a subclassing controller.
The action links available in the index view. None by default.
The show view displaying all the attributes of one entry and the various actions to perform on it.
A partial defining the attributes to be displayed in the show view.
A partial defining the table in the index view with various links to manipulate the entries.
The view to create a new entry.
The view to edit an existing entry.
The form used to create and edit entries. If you would like to customize
this form for various models, just create an own
_form.html.erb in your controller’s view directory.
The action links available in the index view.
The action links available in the show view.
The action links available in the edit view.
Partial to define the layout for an arbitrary content with a label.
Partial to display the validation errors in Rails 2 style.
An example layout showing how to use the @title and
flash. Most probably you want to merge this with your
application.html.erb or adapt the main CRUD templates, so you
wont need this file.
An simple partial to display the various flash messages. Included from
crud.html.erb.
An empty file to put your navigation into. Included from
crud.html.erb.
A simple SCSS with all the classes and ids used in the CRUD code.
Some sample action icons from the Open Icon Library.
A dummy model to run CRUD tests against.
A handful of convenient assertions. Include this module into your
test_helper.rb file.
A module to include into the functional tests for your
CrudController subclasses. Contains a handful of CRUD
functionality tests for the provided implementation. So for each new CRUD
controller, you get 20 tests for free.
Functional tests for the basic CrudController functionality.
Testing the provided implementation and a great base to test your adaptions of the CRUD code.
A whole set of shared exampled to include into your controller specs. See
spec/controllers/crud_test_models_controller_spec.rb for
usage. So for each new CRUD controller, you get all the basic specs for
free.
Convenience methods used by the crud controller examples.
A dummy model to run CRUD tests against.
Controller specs to test the basic CrudController
functionality.
The specs for all the helpers included in DRY CRUD.