In Rubyroid Labs we are very passionate about application architecture. Most projects we work here are long-term projects, so if you are not being careful about your application design at some point you will find yourself in a position where in order to add a new feature it’s just easier to rebuild the whole project from the scratch. And it’s definitely not something you want to face up with.
One of the bad signs that your project is getting sick is that new team members spend a significant amount of time just reading through the source code in order to understand the logic. Today we want share a list of different gems, which in our opinion can help you to organize your code and make your team members smile.
1. interactor
This library is absolutely fabulous and has always been in our shortlist when talking about writing some complex business logic. What is an interactor? As gem readme says – “an interactor is a simple, single-purpose object, used to encapsulate your application’s business logic”. You can think of that as a service-objects we all love, but it’s a way more than that. Let’s take a look on the example:
# app/interactors/create_order.rb class CreateOrder include Interactor def call order = Order.create(order_params) if order.persisted? context.order = order else context.fail! end end def rollback context.order.destroy end end # app/interactors/place_order.rb class PlaceOrder include Interactor::Organizer organize CreateOrder, ChargeCard, SendThankYou end
In this example you probably noticed couple absolutely great features of this gem.
The first thing is that you are able to organize your simple interactors to an executable chain, which will be executed in proper order. Special variable context is being used to share states between different interactors.
The second thing is that if one of the interactors fails for some reason, all previous will be rolled-back. You could see that rollback on CreateOrder, which will drop an order if ChargeCard or SendThankYou fails.
It is so cool, isn’t it?
2. draper
If you have ever used custom Rails-helpers, you know how messy they become overtime. And in most cases we use them to display some data in a more fancy way. That’s where decorator design pattern can help us. Let’s take a look at a draper syntax, which is pretty self-explanatory:
# app/controllers/articles_controller.rb def show @article = Article.find(params[:id]).decorate end # app/decorators/article_decorator.rb class ArticleDecorator < Draper::Decorator delegate_all def publication_status if published? "Published at #{published_at}" else "Unpublished" end end def published_at object.published_at.strftime("%A, %B %e") end end # app/views/articles/show.html.erb <%= @article.publication_status %>
In the code above you can see that our goal was to show published_at attribute in specific format. In classical rails-way we have 2 options of how to do that.
First one is just to write a special helper. The problem with this is that all helpers live under the namespace and as project longs you can face up with some weird name collision, which is extremely hard to debug.
The second one is to create a new method inside the model and use that method instead. This solution also feels wrong since it breaks model class responsibility. By default models are responsible for interaction with data, other than representation of that data.
That’s why using draper in this scenario is a more elegant way to achieve our goal.
Be sure that you check another stunning article:
19 Ruby on Rails Gems which Can Amaze
3. virtus
Sometimes using a simple ruby object is not enough for you. Imagine you have some complex form on a page, where different pieces form should be saved as different models in the database. That’s where virtus can help you. Let’s take a look at the example:
class User include Virtus.model attribute :name, String attribute :age, Integer attribute :birthday, DateTime end user = User.new(:name => 'Piotr', :age => 31) user.attributes # => { :name => "Piotr", :age => 31, :birthday => nil } user.name # => "Piotr" user.age = '31' # => 31 user.age.class # => Fixnum user.birthday = 'November 18th, 1983' # => #<DateTime: 1983-11-18T00:00:00+00:00 (4891313/2,0/1,2299161)> # mass-assignment user.attributes = { :name => 'Jane', :age => 21 } user.name # => "Jane" user.age # => 21
As you can see, virtus looks similar to standard {OpenStruct} class, but gives you many more features. You should definitely play around to explore them all.
This gem could be a good start for you, but if you are looking for more advanced techniques – definitely go and check dry-types, dry-struct and dry-validation.
4. cells
If you are not familiar with Nick Sutterer Ruby on Rails advanced architecture, you should definitely check that out. Not all of us ready to apply this whole concept to their existing applications. But sometimes your views become really complicated, with different conditions applied for different types of user and etc. That’s where cells gem could help. It allows moving part of you views to isolated components, which are just regular ruby-classes. Let’s take a look at code sample:
# app/cells/comment_cell.rb class CommentCell < Cell::ViewModel property :body property :author def show render end private def author_link link_to "#{author.email}", author end end # app/cells/comment/show.html.erb <h3>New Comment</h3> <%= body %> By <%= author_link %> # app/controllers/dashboard_controller.rb class DashboardController < ApplicationController def index @comments = Comment.recent end end # app/controllers/dashboard/index.html.erb <% @comments.each do |comment| %> <%= cell(:comment, comment) %> <% end %>
In this example we want to display recent comments on our dashboard. Imagine all comments should be displayed identical on our application. Rails will use some shared partial for rendering. But instead of doing that we use CommentCell object. You can think of that object as a combination of draper we talked about before with ability to render views. But of course it has many more features. Check their README to learn more about all options.
How to Clear Out Your Controllers and Models With Waterfall Gem
5. retryable
All modern web application do have different types of integrations. Sometimes it’s made through solid API calls, sometimes you have to upload a file to FTP or even use some binary protocol. Problem with all integrations is that sometimes their calls just fail. In some cases – it fails without any reasons. And the best thing you can do is just to try again. Thumbs up if you have ever had to do something like this:
begin result = YetAnotherApi::Client.get_info(params) rescue YetAnotherApi::Exception => e retries ||= 0 retries += 1 raise e if retries > 5 retry end
Here is where retryable can help you. Let’s take a look at how we can rewrite the example above using that gem:
Retryable.retryable(tries: 5, on: => YetAnotherApi::Exception) do result = YetAnotherApi::Client.get_info(params) end
It looks much nicer, doesn’t it? Check that gem out for other scenarios it supports.
6. decent_exposure
If you are not a big fan of using magic – this library is not for you. But in some applications we definitely have a lot of duplications for very simple and standard CRUD actions. That’s where decent_exposure could help you. Let’s imagine that we are creating new controllers to manage things. That’s how scaffold will look for us:
class ThingsController < ApplicationController before_action :set_thing, only: [:show, :edit, :update, :destroy] def index @things = Thing.all end def show end def new @thing = Thing.new end def edit end def create @thing = Thing.new(thing_params) respond_to do |format| if @thing.save format.html { redirect_to @thing, notice: 'Thing was successfully created.' } else format.html { render :new } end end end def update respond_to do |format| if @thing.update(thing_params) format.html { redirect_to @thing, notice: 'Thing was successfully updated.' } else format.html { render :edit } end end end def destroy @thing.destroy respond_to do |format| format.html { redirect_to things_url, notice: 'Thing was successfully destroyed.' } end end private def set_thing @thing = Thing.find(params[:id]) end def thing_params params.require(:thing).permit(:for, :bar) end end
We can’t say that 60 lines of code is not too much. But as rubyists we always want it to be as minimalistic as possible. Let’s take a look at how it could be transformed using decent_exposure:
class ThingsController < ApplicationController expose :things, ->{ Thing.all } expose :thing def create if thing.save redirect_to thing_path(thing) else render :new end end def update if thing.update(thing_params) redirect_to thing_path(thing) else render :edit end end def destroy thing.destroy redirect_to things_path end private def thing_params params.require(:thing).permit(:foo, :bar) end end
Yakes! Now it’s a little more than 30 lines of code without losing any functionality. As you could notice – all magic is brought by expose method. Check this gem documentation for a better understanding of how things work under the hood.
7. groupdate
Every developer knows that dealing with different time zones is a hard thing. Especially when you are trying to write some aggregation on your database. It always gave me hard time when someone asked: “Could I get how many users we are getting every day this month excluding free users” or similar. Now you can stop worrying about such requests and just use gem. Here is an example:
User.paid.group_by_week(:created_at, time_zone: "Pacific Time (US & Canada)").count # { # Sun, 06 Mar 2016 => 70, # Sun, 13 Mar 2016 => 54, # Sun, 20 Mar 2016 => 80 # }
Hope you enjoyed some of these libraries. Let us know if you have any other tools in your mind which can help to write more expressive and awesome code.
2 Comments
Thanks! Great article!
Thanks, Radoslav!