Question

Using rails generate model, I created two models/tables, policeman and policewoman. They are lists of officers with many attributes (age, time in the field, case solved, etc.). If I wanted to do a computation that determines who should get the next promotion to Sargent what is the conventional way to do it in rails?

I would want to create a new class just to deal with this situation, and hide all the complex computation (compare attributes between both lists) away from the caller. So maybe in a controller, say Captain, under the method show I would do

class Captain < ApplicationController
  def show
    promotion = Promotion.new
    @ideal_sargent = promotion.sargent(Policeman.find(:all),Policewoman.find(:all))
  end
end

Where do I create this Promotion class? Do I use rails generate controller to make it? Or maybe make a gem for it? Or even put it all in a model (I hear thin controllers fat models)?

EDIT: Maybe this? If so, how do I create a model without a migration file being automatically made?

Was it helpful?

Solution

Your general idea of decoupling it from models and from controllers is a good one. Although you confuse it a bit with controllers, generators and gems...

What you want to do is:

  • introduce a service object which is plain ruby object
  • put the logic for calculating promotion order inside it
  • have it completely decoupled from controller and loosely coupled to police officers models

The interface to use it would be basically as you have already described:

# prepare some officers to choose from
officers = [PoliceWoman.find(1)] + PoliceMan.all
# returns the first ranking officer for promotion
to_be_promoted = SargentPromotionService.new.find_top_candidate(*officers)

Where to put this service model? I suppose it contains application specific logic, that isn't really useful outside of application. Therefore we put it in the app folder. But where in the app?

A good practice is to setup the app/domain folder. There you can put all the app specific domain models (service, policy, value objects, etc...). All you need to setup this folder is add it to the autoload paths inside the config/application.rb:

config.autoload_paths += %W(#{config.root}/app/domain)

This way you have a clear separation of rails models (take care of persistence) and domain models - where you should put most of the application specific code. If your app is really simple and small you could also skip the app/domain folder and just use the app/models. And if you follow the approach to use plain ruby objects with loose coupling, you will get easily testable, maintainable, flexible and reusable code. =)

OTHER TIPS

For this purpose, I wouldn't create two tables to modeling the data. I would use a single table named polices, and keep a column as gender and another one as rank to differ policeman and policewoman, and different rank. Then I would put promote function as a class method inside the Police modal.

class Police < ActiveRecord::Base
  def self.promote(param1, param2)
     ....
  end
end

In this way you can incapsulate the business logic inside the promote function, the caller can invoke it without knowing any complex computation in it. Police.promote(a,b)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top