Skip to content

Instantly share code, notes, and snippets.

@nicholasjhenry
Created October 22, 2011 04:56
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save nicholasjhenry/1305650 to your computer and use it in GitHub Desktop.
Save nicholasjhenry/1305650 to your computer and use it in GitHub Desktop.
PayRoll application, embedded in Rails, borrowing from Use Case Driven Architecture and DCI
## PayRoll gem
# lib/pay_roll.rb
module PayRoll
class << self
attr_accessor :employee_directory
def config
yield self
end
end
end
end
# lib/pay_roll/services/pay_day_service.rb
class PayRoll::PayDayService
def initialize(date=Date.now)
@date = date
@employees = PayRoll.employee_directory.active
@employees.each { |e| e.extend(Payable) }
end
def execute
@employees.each do |e|
if e.pay_date?(@date)
pc = PayCheck.new(e.calculate_pay(@date))
e.send_pay(pc)
end
end
end
end
# lib/pay_roll/roles/payable.rb
module PayRoll::Payable
def pay_date?(date)
# ...
end
def send_pay(pay_check)
# ...
end
end
## Rails application
# app/controllers/pay_day_controller.rb
# Yes, this would make more sense to be run in a scheduled job, but wanted to show
# an example of services used in a Rails controlle
class PayDayController < ApplicationController
def create
PayRoll::PayDayService.new.execute
redirect_to :back, :notice => "Pay day has been successfully completed"
end
end
# config/initializers/pay_roll.rb
PayRoll.config do |config|
employee_directory = Employee
end
# models/employee.rb
class Employee < ActiveRecord::Base
scope :active, where(:active => true)
end
@judofyr
Copy link

judofyr commented Oct 23, 2011

Dude…

Let's look at this line: options[:employees] ||= PayRoll.employee_directory.active and see how many levels there are to actually understand the code:

Level 1:

module PayRoll
  class << self
    attr_accessor :employee_directory
  end
end

Level 2:

PayRoll.config do |config|
  employee_directory = EmployeeDirectory.new # alternatively just use Employee model
end

Level 3:

class EmployeeDirectory
  def active
    Employee.active
  end
end

Level 4:

class Employee < ActiveRecord::Base
  scope :active, where(:active => true)
end

Four levels?! What have you really gained by all these levels?

@josegonzalez
Copy link

He's gained Indirection.

@judofyr
Copy link

judofyr commented Oct 23, 2011

I N D I R E C T I O N

@nicholasjhenry
Copy link
Author

@judofyr Yes, your quite right. In this example, a database gateway class is unnecessary. In Martin's example from Agile Software Development it makes sense when creating Entity objects, but with my DCI interpretation, it does not.

@codesnik
Copy link

how do you PayRoll::PayDayService.new if it's a module?

@nicholasjhenry
Copy link
Author

@codesnik good catch, thank you. Fixed.

@nicholasjhenry
Copy link
Author

@mankind Not sure where you're comment went, but I'm a big fan of using engines to break up a complex rails application. Pivotal has a good blog post on this approach which I'm currently using:

http://pivotallabs.com/users/mbarinek/blog/articles/2022-unbuilt-rails-dependencies-how-to-design-for-loosely-coupled-highly-cohesive-components-within-a-rails-application

@elgalu
Copy link

elgalu commented Jul 12, 2013

👍

@shime
Copy link

shime commented Sep 2, 2013

🍻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment