Rails service objects and controller - ruby-on-rails

I have a colleague that likes to pass off the controller into a service object. For example a controller method might look the following way:
class FooController < ApplicationController
...
def show
Foo.new(self).call
end
...
end
the service object looks like this then:
class Foo
attr_reader :controller, :resource_id
delegate :render, :params, :head, to: :controller
def initialize(controller, resource_id)
#controller = controller
#resource_id = resource_id
end
def call
resource = SomeActiveRecordModel.find(resource_id)
if resource
render json: resource.to_json
else
head :not_found
end
end
end
Somehow I feel that this is counterproductive and an instance of cargo-cult software engineering.
I would prefer to keep the service object completely separate from the controller. Dependencies would be passed into the service object's constructor, parameters would be passed into the service object as method arguments. Any result is simply returned from the method.
Sadly my colleagues are not exactly thrilled by this whenever I bring it up in a code review, which I in turn find relatively frustrating.
What are the pros an cons of the respective approaches? How can I argue my case better? Am I missing something here?

I suspect the answer is "it depends".
In the exact example you gave, I see no particular advantage and it creates a degree of obfuscation. Also, in general, I agree with you on keeping the service object separate from the controller.
However, there are times when I find myself passing the controller into the service object. For instance, when I have a lot of complex work to do in dynamically constructing a view.

Related

Rails controller design pattern idea for decreasing it's size

The question is what do you think about this pattern?
Problem:
You've got controller with index action, and this action is huge.
Action is full of ActiveRecord chaining and maybe some computations with records.
When you are adding new methods controller is getting bigger.
I've heard about "skinny controller fat model", and I'm just what? My models are already fat, are you crazy?
I've heard about service objects, they are not very usable as for me.
result = SomeService.new(params, request, flash, current_user).call
After such service object you could try:
result = SomeService.new(controller).call
# or
result = SomeService.new.(controller)
And what to do with returning error statutes from that service? As answered below, exceptions.
So, you need to create exceptions class, throw it, catch it and only then render something or make redirect.
Here is the pattern from subject:
# controllers/some_controller.rb
class SomeController < OtherController
before_actions
include Index
include Show
def create_update_and_destroy
# small methods have no reason to leave controller
end
private
def common_private_method
end
end
# controllers/some_controller/index.rb
module SomeController::Index
def index
# code here
end
private
def index_do_some_stuff
# this method is prefixed by "index" to avoid name collision
end
end
Yes, there is some_controller.rb and some_controller directory with actions as files.
Nobody in OOP likes prefixes and if your method has well explaining not short name - prefix is not necessary.
In my opinion, this is the most simple and obvious way. Just take fat code and split to modules!
What do you think?
Explainations about why I have many code:
In one project I have view, it requires records from several models, some related, some not related.
So I had started to write many scopes in models, time passed, I realized that this is wrong approach.
Different actions required very specific records selections, only action should know about such specifics. And I had scopes named "for_index", "for_show".
Now I'm creating module Index with metod index, and all record fetching and computing code is splitted into private methods right in place.
In other project I have API. Specific endpoint is returning specific deep nested json, several models are fetching. I already know that creating scopes in model for one specific endpoint is bad idea, so I'm splitting code amoung private methods. One action and five private methods for it. And next five public methods and 25 private? In single controller?
Combining form objects with service objects and other patterns can make your model and controller thin. You can also add an ActiveResource::Errors object in your service object to collect errors.
Here's an example using user's input. Customize it according to your specification
class ProductForm
...
def save
if service_object.call
self
else
append_errors(service_object)
false
end
end
def service_object
#service_object ||= ProductCreationService.new(params)
end
def append_errors object
errors.append object.errors # just for simplicity
end
end
In your controller
def create
#product = ProductForm.new params
if #product.save
...
else
...
end
end
Controller actions should be straight forward and as short as possible. Complexities inside these actions can be lessen with other design patterns.
Actions that collects data can be abstracted by using query/finder objects.
Here's a rough example
class DashboardQuery
attr_reader :options
def initalize(options = {})
#options = options
end
def branches
#branches ||= Branch.all
end
def branch_count
#branch_count ||= branches.count
end
end
# Usage
#dashboard = DashboardQuery.new(params)
#dashboard.branch_count
#dashabord.branches

what is the best way to set author field on STI model

I have a basic model called Page and have many STI models based on Page such as
Drawing
Article
Story
etc...
I have separate controller and view for each of these STI models because I needed to customized the view layer based on the model type and have different logic for controller and hence separate controllers. However, I need the author field of all models to be set to current user. How do I do this in a single place?
For example, if I use before_action on Page controller and set the author, it affects the #page instance variable whereas my DrawingsController is using #drawing so it wont save the author of my #drawing unless I repeat the same code in DrawingsController.
Edit:
My controller hierarchy is
DrawingsController < PagesController
PagesController < ApplicationController
Both PagesController and DrawingsController have all the 7 restful actions. However, the actions on PagesController doesn't serve any purpose as I dont want any of my users to create Pages. I only want them to create the inherited STI classes like Drawings.
you could do this using some convention and meta programming in your controller hierarchy:
def add_author
model = instance_variable_get(:"##{controller_name.singularize}")
model.author = current_user
end
I'll be honest, I make no guarantees about the "best-practice"-ness of this answer, but I'll propose it anyways in case it helps to some degree. Also note that after rethinking the problem, I realized my first suggested solution in the comments was wrong, and the second is also not quite right either. So I'm posting a modified version of the second suggestion only:
Short answer: Let the PagesController handle most of the work, and delegate only to the subcontroller for model-specific things if needed. As phoet said, you can use a bit of meta programming (in a different way) to accomplish this.
class PagesController < ApplicationController
# pages controller stuff here
def create
#page = controller_name.classify.constantize.new(params[:page_params]) # I love Rails, don't you?
#page.author = current_user
handle_additional_create_actions
# For completeness of this example...
if #page.save
# render / redirect on success
else
# render errors
end
end
protected
# This method should be overwritten by sub controllers if needed
# Also, name this whatever you like, this is merely a verbose example for illustration purposes
def handle_additional_create_actions
# in the pages controller, this method does nothing
end
end
And, if there are additional things that need to be done by the model-specific controller:
class DrawingsController < PagesController
# drawing controller stuff here
protected
def handle_additional_create_actions
#page.some_other_field = some_other_data
end
end
A quick note: Note that in my suggestion, you're eliminating the model-specific variable names, meaning we don't have an #drawing and an #article, etc, anymore. Your models are all, essentially, types of Page objects, and so we're going to call it by its general name as a convention. That way, when you ask the DrawingsController to do something specific for the Drawing class, it knows that instance can be accessed via our generically named #page object.
So ultimately, the PagesController does the heavy lifting, regardless of which concrete model type you're dealing with. That way, only general page stuff is found in the pages controller, and drawing, article or story-specific stuff is found in their respective concrete controllers.

REST API parameters validation in Rails

I am writing a Rails application that has a REST API only (no web interface or such). I need to check if the requests are being made with the right parameters and return different error codes if not. For example, all my API endpoints require a user_access_token, and a client_id. Some other endpoints require other different parameters to be present.
In all the controllers, I have the code that does this checking, but the code is getting duplicated, and there are many if-conditions that can be extracted out and put somewhere else. So I thought of adding a before_filter in my ApplicationController that does this checking. I defined a hash that contains all the endpoint to required_params mapping, and this filter runs before the control passes to the actual controller in question.
But for some endpoints, it is getting a little complicated because some parameters are required if some other parameters are present, and in some cases one of two parameters is required. So now I am wondering if there is a better way of doing this.
Am I doing it right? Is there a better/standardized way of doing this? Or some gem that does this?
I would have to see some code to understand the context. But it essentiallys sounds like you have your base ApplicationController that all of your other controller's inherit from. The thing that varies is the parameters that you expect (except for user_access_token and client_id which always need to be supplied). Since you are using inheritance you could define a method in your ApplicationController that contains a list of which parameters you expect and then in your subclasses override the method to check add other params. Your base controller will be responsible for doing the actual validation but the subclasses will get a chance to override the required parameters.
class ApplicationController < ActionController::Base
before_filter :validate_params
protected
def required_params
[:user_access_token, :client_id]
end
def validate_params
unless (require_params - params.keys).count.zero?
# do something
end
end
end
class AnotherController < ApplicationController
protected
def required_params
p = super + [:email, :password]
p = p + [:another, :and_another] if some_condition?
p
end
end
Essentially you would let the subclass decide if it needed to add any additional required parameters. Of course I don't know what your exact context is, but hopefully this will help you in the right direction. Do some research on polymorphism when you get the chance :-)

Repository or Gateway pattern in Ruby

How can I implement the Repository or Gateway pattern in Ruby?
I come from a C# world and I usually abstract away my data access but with ActiveRecord as the default data access mechanism in Ruby, it's not obvious how to accomplish that.
What I usually would do in C# is work with abstract interfaces and then have a concrete implementation for EFCustomerRepository, NHibernateCustomerRepository and InMemoryCustomerRepository and depending on the situation I inject the matching concrete implementation.
So now, what’s the Ruby way?!
As far as I understand it, in dynamic languages you would not need something like DI (dependency injection).
And Ruby has powerful language features to allow things like mixins.
But you would define the mixin to use statically on class or module-level?
How do I write my business logic if I want to develop against an in-memory repository and in production I would switch to my ActiveRecord-Repository?
If might be on the wrong path here since I'm used to thinking in a statically typed language. How would someone tackle this task the Ruby way? Basically I want to make my persistence layer abstract and it's implementations interchangeable.
EDIT: I am referring to robert c. martins (unclebob) keynote about architecture
Thanks for any help...
I get what you are saying. I come from a .NET background as well. Abstracting away your business logic & persistance logic is imo a good idea. I haven't found a gem that does it for you yet. But you can easily roll something simple yourself. In the end a repository pattern is basically a class that delegates to your persistance layer.
Here is what I do:
require 'active_support/core_ext/module/attribute_accessors'
class GenericRepository
def initialize(options = {})
#scope = options[:scope]
#association_name = options[:association_name]
end
def self.set_model(model, options = {})
cattr_accessor :model
self.model = model
end
def update(record, attributes)
check_record_matches(record)
record.update_attributes!(attributes)
end
def save(record)
check_record_matches(record)
record.save
end
def destroy(record)
check_record_matches(record)
record.destroy
end
def find_by_id(id)
scoped_model.find(id)
end
def all
scoped_model.all
end
def create(attributes)
scoped_model.create!(attributes)
end
private
def check_record_matches(record)
raise(ArgumentError, "record model doesn't match the model of the repository") if not record.class == self.model
end
def scoped_model
if #scope
#scope.send(#association_name)
else
self.model
end
end
end
And then you could for example have a Post repository.
class PostRepository < GenericRepository
set_model Post
# override all because we also want to fetch the comments in 1 go.
def all
scoped_model.all(:include => :comments)
end
def count()
scoped_model.count
end
end
Just instantiate it in your controller in a before_filter or initialize or wherever. In this case I'm scoping it to the current_user so that it only fetches those records and automatically create posts only for the current user.
def initialize
#post_repository = PostRepository.new(:scope => #current_user, :association_name => 'posts')
end
def index
#posts = #post_repository.all
respond_with #posts, :status => :ok
end
I came across https://github.com/bkeepers/morphine which is a tiny DI framework. It could work for you :) But DI isn't a heavily used pattern in ruby. Also, I instantiate my repos in order to scope them to a current user or something else.
I'm on a quest to find the right way to do just what you ask and do a little write-up about it if I ever do find it. But for now it's already sufficient to make the clean cut between persistance & my controllers. If this is done properly it won't be a big hassle to switch to a different system later on. Or add caching etc.
Well, ActiveRecord already provides abstract persistence layer - it has several different adapters allowing it to use different database backends. Also, it's open-source so you are free to take a look at how it has been achieved.
Upon the first glance you can see that it also has an AbstractAdapter that all other adapters inherit, however, as Ruby is dynamic, duck-typing language, AbstractAdapter doesn't have to contain abstract methods which will be overridden in children classes, neither defines a "contract" that they should honour.
Edit:
Here's a simple sketch on how you could abstract away your storage in Ruby, not sure which pattern exactly it is:
# say you have an AR model of a person
class Person < ActiveRecord::Base
end
# and in-memory store of persons (simply, a hash)
IN_MEMORY_STORE = {
:Person => ['Tim', 'Tom', 'Tumb']
}
# this will abstract access
class MyAbstractModel
def initialize item, adapter
#item = item
#adapter = adapter
end
# get all elements from the store
def all
case #adapter
when :active_record
# pull from database:
Object.const_get(#item).all
when :in_memory_store
# get from in-memory store
IN_MEMORY_STORE[#item]
else
raise "Unknown adapter"
end
end
end
# get all Persons from in-memory storage...
p MyAbstractModel.new(:Person, :in_memory_store).all
# ...and from a database
p MyAbstractModel.new(:Person, :active_record).all
#serverinfo, I don't know much about C#. But when I came to Ruby from a Java/C background, I was blown away when I realized how flexible this language really is. You say that your real problem here is to "abstract away your persistence layer and make it exchangeable". You also asked "how will I write the business logic".
I suggest that you throw away your preconceptions and ask yourself: "how would I like to express data access/storage within my business logic layer"? Don't worry about what you think can or can't be done; if you can figure out how you would like the interface to work, there is probably a way it can be done in Ruby.
You will also have to decide how you want to specify the concrete implementation to be used. Is it possible you will want to use a different data store for different model objects? Might you want to switch at run-time? Would you like to specify the backend to be used in a configuration file, or in code? If you can decide what you want to do, there are lots of people on Stack Overflow who can help you figure out how to do it.

Tracking model changes in Rails, automatically

In my rails app I would like to track who changes my model and update a field on the model's table to reflect.
So, for example we have:
class Foo < ActiveRecord::Base
before_create :set_creator
belongs_to :creator, :class_name => "User"
protected
def set_creator
# no access to session[:user_id] here...
end
end
What's a good testable way for me to get at the user_id from my model? Should I be wacking this data in Thread.current ?
Is it a better practice to hand this information from the controller?
Best practice in MVC is to have your Models be stateless, the controller gets to handle state. If you want the information to get to your models, you need to pass it from the controller. Using a creation hook here isn't really the right way to go, because you are trying to add stateful data, and those hooks are really for stateless behavior.
You can pass the info in from the controller:
Foo.new(params[:foo].merge {:creator_id => current_user.id})
Or you can create methods on User to handle these operations:
class User
def create_foo(params)
Foo.new(params.merge! {:creator_id => self.id})
end
end
If you find yourself writing a lot of permissions code in the controller, I'd go with option 2, since it will let you refactor that code to the model. Otherwise option 1 is cleaner.
Omar points out that it's trickier to automate, but it can still be done. Here's one way, using the create_something instance method on user:
def method_missing(method_sym, *arguments, &block)
meth = method_sym.to_s
if meth[0..6] == "create_"
obj = meth[7..-1].classify.constantize.new(*arguments)
obj.creator_id = self.id
else
super
end
end
You could also override the constructor to require user_ids on construction, or create a method inside ApplicationController that wraps new.
There's probably a more elegant way to do things, but I definitely don't like trying to read state from inside Model code, it breaks MVC encapsulation. I much prefer to pass it in explicitly, one way or another.
Yeah, something like that would work, or having a class variable on your User model
cattr_accessor :current_user
Then in your controller you could have something like:
User.current_user = current_user
inside a before filter (assuming current_user is the logged in user).
You could then extend AR:Base's create/update methods to check for the existence of a created_by/updated_by field on models and set the value to User.current_user.
I'd create new save, update, etc methods that take the user_id from everything that calls them (mainly the controller).
I'd probably extend ActiveRecord:Base into a new class that handles this for all the models that need this behaviour.
I wouldn't trust Thread.current, seems a bit hackish. I would always call a custom method which takes an argument:
def create_with_creator(creator, attributes={})
r = new(attributes)
r.creator = creator
r.save
end
As it follows the MVC pattern. The obviously inherient problem with this is that you're going to be calling create_with_creator everywhere.
You might find PaperTrail useful.
Probably you could check out usertamp plugins, found two in github
http://github.com/delynn/userstamp/tree/master
http://github.com/jnunemaker/user_stamp/tree/master

Resources