Skip to main content

Using Ruby Service Objects To Refactor Your Rails Code Discussion

General • Asked by Chris Oliver

Very nice video Chris! I really like the idea of breaking things out into POROs. I think the next refactoring I would do to this app is making a Subscription class to house the logic for adding and updating the mailchimp list. Since multiple parts of your app could end up needing to add someone to mailchimp. Also feels like its violating SRP there.


Can you use active record queries in PORO's i.e. Lead.find in UpdateLead class

Yep! All your classes are available inside your POROs like you would expect. The things you won't have access to are things like "params" that come from inheriting ApplicationController. You'll have to pass those in.


Arent you calling lead_params twice? First, when you call CreateLead.new(lead_params) and then when you call @lead = Lead.new lead_params inside the save method on the CreateLead class ? Couldn't you just do @lead = Lead.new(@params) because we pass lead_params into the @params variable when we instantiate the instance of the PORO class. Or, b/c params is just an attribute on the CreateLead Class, we can't do that.

Good catch! That should actually reference @params in the PORO because it doesn't have access to lead_params in there unless you move that code too.

P.S. on the index page, please keep the runtime length for each screencast next to it's release date. Every now and then I see you take it down! Is that a design decision?

I don't think I've touched that for a bit. If you see it broken, send me a screenshot and I'll make sure to correct it. They should definitely be on the index page.


Hey Chris! I've been trying to "dumb down" service objects with a basic association between blogs and posts. I want to organize my model folder exactly how you have all of your lead related objects under the model / folder but I'm stumped. Can you point me in the right direction?

http://stackoverflow.com/qu...

Hey James! You can actually generate these types of nested models with Rails:

rails g model Blog::Post title

That will create the Post model nested inside the blog folder. The table name will be "blog_posts" because database tables don't have nesting. They just scope it by prepending "blog_" at the beginning in the database table. I agree with the StackOverflow poster in that you shouldn't try to override the table name to be safe. You can still scope it in your code and so long as you don't also create a "BlogPost" model, you will be fine.

Ok, I guess that works! So does this guys' solution below also require prepending the table like you said?

http://stackoverflow.com/qu...

Yeah in the database I think you would have both "users" and "something_users" tables.


Very good video Chris. One question, do we have to put this:
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{*/}')]
in order to load the folder Leads inside the model?

No, you shouldn't need to.

I've tried without and I'm getting uninitialized constant ReviewsController::CreateReview . I have a reviews folder inside the models with createReview.

The class has to match the filename exactly. If you have app/models/reviews/create_review.rb you need to have a class Reviews::CreateReview defined in it. It must match the folder and filename for autoloading to work automatically. Folders equate to namespaces, and underscored filenames are converted to cased words.

Ok, thank you Chris.

What is the advantage of using that way ( autoloading ) over writing something like this config.autoload_paths += Dir[Rails.root.join('app', 'models', '{*/}')] ?

Mainly it's good convention to keep one class per file, and have the class inside the file named exactly the same.

Rails already does autoloads that path, it's just that it requires the class inside to match the folder+filename. So since it already does that, you might as well just follow the convention.


If you would like to find out more on Service Objects, check out this blog post: https://selleo.com/blog/essential-rubyonrails-patterns-part-1-service-objects
Hope it helps!


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 24,647+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.