📜 ⬆️ ⬇️

Get rid of code repetition with DRY CRUD

The Ruby on Rails framework simply fascinates me, but until recently some difficulty was the generation of CRUD controllers.

Almost always I needed to implement lists with sorting, filtering and pagination, and I did not find a standard way to achieve this in the rails. I tried several options and none satisfied me:

For a long time, I couldn’t find anything similar to the CGridView widget from my Yii framework. Already almost put up with the need to write my bike, but I came across DRY CRUD and I want to share my experience of using it. Maybe it will be useful to someone, and maybe someone will tell even more suitable tool.

Installation


DRY CRUD is a gem that, using a generator, creates its own controller class in the project, after this generation, the gems can be turned off, and the code generated by them can be changed if necessary.
To install, just enter the gem in the gemfile
gem 'dry_crud', '= 1.7.0' 

run
 bundle install 

and generator
 rails generate dry_crud --templates haml --tests rspec 


Demonstration of opportunities


As a demonstration, I created an application from two User and Post models, connected by a one-to-many relationship.
 rails generate model User name:string email:string pass:string rails generate model Post author_id:integer title:string content:text is_draft:boolean 

For a more comprehensible for a person mapping, I defined the method to_s and I got this model code:
 #user.rb class User < ActiveRecord::Base attr_accessible :email, :name, :pass has_many :posts def to_s "#{name}" end end #post.rb class Post < ActiveRecord::Base attr_accessible :user_id, :content, :is_draft, :title belongs_to :user def to_s "#{title}" end end 

then in the / app / controllers folder it is enough to create a controller file and inherit it from CrudController . You can also specify which columns provide filtering.
 #users_controller.rb class UsersController < CrudController self.search_columns = [:name, :email] end 

That's all, it is not necessary even to create a folder for views, until more complex functionality is required. We start the application and get a working CRUD with sorting (clickable columns) and filtering (if self.search_columns is set in the controller), and pagination can be screwed with minimal effort and it will be compatible with the filter and with sorting.

')
The editing form also did not disappoint, it defines the type of each field and uses the corresponding control for the boolean, string and text fields, as well as for the “belongs_to” connection:

and of course all this can be customized!

Twitter bootstrap connection

Many of my projects use Twitter bootstrap and the DRY CRUD adaptation is very simple, just connect the twitter-bootstrap-rails gems , set with a generator
 rails generate bootstrap:install less 

remove app / views / layouts / crud.html.haml and /app/assets/stylesheets/sample.scss and rewrite /app/views/layouts/application.html . Who cares, can see the source of the project.
As a result, we get a recognizable design:


Hiding columns

If for example you need to hide the created_at and updated_at columns in all controllers, it is enough to change the helper of the base class.
 #/app/helpers/list_helper.rb def default_attrs attrs = model_class.column_names.collect(&:to_sym) attrs - [:id, :position, :password, :created_at, :updated_at] #<   end 


Change column set

Now in Users we hide the pass column and add the created_at . To do this, you need to create a folder / app / views / users copy the common partial into it /app/views/crud/_form.html.haml
In the first and only row = crud_table add a list of columns that we want to see:
 = crud_table :name, :email, :created_at 

Result:


Formatting cell contents

By default, the content of posts is displayed in full:

To make it trim up to 100 characters, it’s enough to create the format_ method in the helper.
 #/app/helpers/post_helper.rb module PostHelper def format_content(post) truncate(post.content, :length => 100, :omission => '...') end end 

result:


Localization

You can change the inscriptions for the fields without changing the presentation using the capabilities of the rail L18n. To do this, you must set the default language:
 #/config/application.rb .... module DryCrudSample class Application < Rails::Application ... I18n.default_locale = :ru end end 


I used the l18n_generator gem to generate language files
 #Gemfile group :development do gem 'i18n_generators' end 


download the locale file from the rails-i18n repository
 rails generate i18n_locale ru 


we copy the /config/locales/en_crud.yml translation file generated by installing DRY CRUD into ru_crud.yml and translate.
Generating a YAML file for our models.
 rails generate i18n_translation ru 

get the file /config/locales/translation_ru.yml which I prefer to rename to ru_models.yml
It remains only to write the desired names for the fields of our models.

After restarting the application, we get the localized interface:


Change behavior after editing

Pictures are over, a deeper adjustment begins.
By default, after successfully creating or editing a record, redirect occurs to view this record (the “show” action) I was more accustomed to, in such a case, the redirect would occur to the “index” solution:
 #/app/controllers/crud_controller.rb def update(options = {}, &block) assign_attributes updated = with_callbacks(:update, :save) { entry.save } #respond_with(entry, options.reverse_merge(:success => updated), &block) #<-  respond_with(entry, options.reverse_merge(:success => updated, :location=> index_url), &block) # end 

A similar replacement was needed in the create method.

Then in the project, after editing the record, it was necessary to return to its own editing form. To implement this in our Posts controller, all you need to do is override the update method in which you pass the desired URL:
 #/app/controllers/posts_controller.rb def update super location: edit_post_path(params[:id]) end 


Adding buttons to the action column

On one of the projects, it was necessary to implement the record cloning function. This functionality, as far as I understand, is missing by default, so I implemented it as follows. Using the Post example, first defined a new action in the resource route:
 #routes.rb resources :posts do get :clone, :on => :member end 

This action should receive the id copied record and display the creation form with the fields already filled in. Submit the form to the create action.
I placed the model cloning code in the base class:
 #/app/controllers/crud_controller.rb def duplicate_entry() set_model_ivar( find_entry.dup() ) end 


The button generation code is located in the helper:
 #/app/helpers/crud_helper.rb def action_col_clone(table, &block) action_col(table) do |e| link_table_action('list-alt', action_path(e, &block)) #       end end 


The code for adding a new button to a column is a place in the view:
 #/app/views/posts/_list.html.haml = crud_table :title, :content, :is_draft do |t| - action_col_clone t do |e| - clone_post_path e #     

It all worked. Only two functions have been added, and now for the whole project you can add the “clone” button with just one line!

Conclusion


In fact, using the “deep_cloneable” heme, you can duplicate ActiveRecord along with its dependencies. And in the form, you can edit not only the record itself, but also its related via has_many. And to make the filter more complicated, not one textfield but several selects. And all this is quite simple, partially described in the documentation, partially comprehended by studying the source code.

I came to the conclusion that DRY CRUD is very flexible and powerful.

It is a little embarrassing that in order to make some changes, you have to edit the “source code”, even though it is inside the project, perhaps this may complicate the use of updates from the author. But so far I have not come across such a need, and the volume of changes is small, so that it would be a real problem.

Links


DRY CRUD heme instructions
GitHub example source

Source: https://habr.com/ru/post/175887/


All Articles