Services, What I Wish I learned As A New Rails Developer

Services are what gives rails app functionality in clear, clean control patterns. We use services at Redrooffs for structuring functionality in the apps we build with plain ruby objects. Services act as “action objects”, with methods that perform, as they’re named actions. Think of actions like notifying a slack channel, sending an email, subscribing to an email list. Services can also be used for triggering a larger, more complex series of actions in your app such as a registration process, or payment flow. As a new developer I was taught to build RESTful routes, and to push methods down the stack, resulting in most of my app logic living in the model layer. As you become more comfortable with the rails development pattern you’ll start to notice a consistent theme of “where should I put this code?”. Let me answer that question for you… make it into a service.
How to Get Started Using services
As I mentioned before services are simple Ruby Objects. This means that they are independant of the rails cycle. You don’t need to follow the standard rails inheritance pattern or use any external libraries to make services work (although they certainly can and often do use them). Services can be as simple as separating the sending of an email from a controller or as complicated as a payment, slack notification, mobile notification system. Rails is wonderful because it automatically detects the new Ruby objects created in the App folder. This means, there’s no need for require statements to include your services into the rails pipeline. To add and organize your services simply create a services folder in the App directory of your system and restart your rails server. Boom they’re ready to go.
App/
- Assets
- Channels
- Controllers
- Helpers
- Jobs
- Mailers
- Models
- Services
- ViewsActually using a service
Okay, your service folder is ready to go. Now it’s time to set up and use your services. For this example, we’ll be using the Mailchimp API wrapped by the Gibbon gem to add a person to a mailing list. Here is our service, what we’re going to do, it create the service object, a method to initiate the API call and a public method to initiate the API call from our controller.
require "gibbon" Class SubscriptionService
def initialize(params)
@user_to_subscribe = params[:new_subscription]
end def perform
subscribe_to_list
end private def subscribe_to_list
gibbon.lists(ENV["MAILCHIMP_USER_LIST"]).members.create(body: {email_address: @user_to_subscribe.email, status: "Subscribed", merge_fields: {FNAME: @user_to_subscribe.first_name, LNAME: @user_to_subscribe.last_name}})
end
end
As you can see above, we’ve created a simple ruby object calling it SubscriptionService that takes a new subscription object. A method that extracts the gibbon gem new subscriber method and lastly exposes a method to start the subscribe request. Now it’s as easy as creating the new service object in a controller and calling the perform method from your controller. In the example below we’re going to automatically sign up new users to our new user mailing list.
Class UserController > ApplicationController ... def create
@user = User.create(user_params)
if @user.save
...
SubscriptionService.new({new_subscription: @user}).perfom
...
else
...
end
...
end ... end
Do you see how simple that is, and how clean that controller code looks now?
Clean up and Summary
We’ve structured this service, with a terrible naming scheme. To clean it up we will namespace the service into a MailChimp specific name, we’ll call it MChimp (mostly because i’m pretty sure MailChimp is a reserved module name from the gibbon gem). To do this, we will create a MChimp folder within the Services folder.
App/
- Assets
- Channels
- Controllers
- Helpers
- Jobs
- Mailers
- Models
- Services/
- MChimp
- ViewsWe’ll update the SubscriptionService class to:
require "gibbon" module MChimp
class SubscriptionService
...
#same code as before
...
end end
We’ll update our User Controller to use the name spaced Subscription Service class.
Class UserController > ApplicationController ... def create
@user = User.create(user_params)
if @user.save
...
MChimp::SubscriptionService.new({new_subscription: @user}).perfom
...
else
...
end
...
end ...end
There we go, we’ve got a nicely cleaned up module we can add other service objects to, for maybe recalling the list and doing something with it. Service objects can be immensely powerful and once you’ve taken the time to learn their development partner you will fall in love as I have.
Happy coding.
Originally published at www.theredrooffs.com.
