Slide 1

Slide 1 text

Building Extractable Libraries in Rails Thursday, May 3, 12

Slide 2

Slide 2 text

HELLO THERE! I’ m @patricksroberts; I push bits at @iorahealth and herd cats at @bostonrb. Thursday, May 3, 12

Slide 3

Slide 3 text

Some Things I <3 About Rails Thursday, May 3, 12

Slide 4

Slide 4 text

Convention Configuration Thursday, May 3, 12

Slide 5

Slide 5 text

Transport Concerns Business Concerns Thursday, May 3, 12

Slide 6

Slide 6 text

Business Logic Persistence (One can Dream) Thursday, May 3, 12

Slide 7

Slide 7 text

Engines Finally* Fly First Class Thursday, May 3, 12

Slide 8

Slide 8 text

However... I don’ t :( Thursday, May 3, 12

Slide 9

Slide 9 text

We’re still left with many decisions. Thursday, May 3, 12

Slide 10

Slide 10 text

Let’s make some conventions for /lib Thursday, May 3, 12

Slide 11

Slide 11 text

Thursday, May 3, 12

Slide 12

Slide 12 text

Treat /lib as a temple. Thursday, May 3, 12

Slide 13

Slide 13 text

Treat /lib as a temple. Isolate interaction with your domain models. Thursday, May 3, 12

Slide 14

Slide 14 text

/lib is not your junk drawer. Thursday, May 3, 12

Slide 15

Slide 15 text

# config/application.rb # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths += %W(#{config.root}/lib) Thursday, May 3, 12

Slide 16

Slide 16 text

# config/application.rb # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths += %W(#{config.root}/lib) Thursday, May 3, 12

Slide 17

Slide 17 text

NO Thursday, May 3, 12

Slide 18

Slide 18 text

# config/initializers/twitter_wrangler.rb require_relative ‘../lib/twitter_wrangler’ Thursday, May 3, 12

Slide 19

Slide 19 text

# lib/twitter_wrangler.rb require ‘twitter_wrangler/authentication’ require ‘twitter_wrangler/authorization’ module TwitterWrangler end Thursday, May 3, 12

Slide 20

Slide 20 text

# lib/twitter_wrangler.rb module TwitterWrangler OAUTH_KEY = ‘ABCDEFG1234ZXYZB2324’ end Thursday, May 3, 12

Slide 21

Slide 21 text

NO Thursday, May 3, 12

Slide 22

Slide 22 text

# config/initializers/twitter_wrangler.rb require_relative ‘../lib/twitter_wrangler’ TwitterWrangler.configure do |config| config.oauth_key = ENV[:twitter_oauth_key] end Thursday, May 3, 12

Slide 23

Slide 23 text

# config/initializers/twitter_wrangler.rb require_relative ‘../lib/twitter_wrangler’ TwitterWrangler.configure do |config| config.oauth_key = ENV[:twitter_oauth_key] end Thursday, May 3, 12

Slide 24

Slide 24 text

# config/initializers/twitter_wrangler.rb require_relative ‘../lib/twitter_wrangler’ TwitterWrangler.configure do |config| config.oauth_key = ENV[:twitter_oauth_key] end Thursday, May 3, 12

Slide 25

Slide 25 text

# config/initializers/twitter_wrangler.rb require_relative ‘../lib/twitter_wrangler’ TwitterWrangler.configure do |config| config.oauth_key = ENV[:twitter_oauth_key] end Thursday, May 3, 12

Slide 26

Slide 26 text

# lib/twitter_wrangler/configuration.rb module TwitterWrangler class Configuration attr_accessor :oauth_key def initialize self.oauth_key = nil end end class << self attr_accessor :configuration end def self.configure self.configuration ||= Configuration.new yield(configuration) if block_given? end end Thursday, May 3, 12

Slide 27

Slide 27 text

# lib/twitter_wrangler/configuration.rb module TwitterWrangler class Configuration attr_accessor :oauth_key def initialize self.oauth_key = nil end end class << self attr_accessor :configuration end def self.configure self.configuration ||= Configuration.new yield(configuration) if block_given? end end Thursday, May 3, 12

Slide 28

Slide 28 text

# lib/twitter_wrangler/configuration.rb module TwitterWrangler class Configuration attr_accessor :oauth_key def initialize self.oauth_key = nil end end class << self attr_accessor :configuration end def self.configure self.configuration ||= Configuration.new yield(configuration) if block_given? end end Thursday, May 3, 12

Slide 29

Slide 29 text

# lib/twitter_wrangler/configuration.rb module TwitterWrangler class Configuration attr_accessor :oauth_key def initialize self.oauth_key = nil end end class << self attr_accessor :configuration end def self.configure self.configuration ||= Configuration.new yield(configuration) if block_given? end end Thursday, May 3, 12

Slide 30

Slide 30 text

# lib/twitter_wrangler/configuration.rb module TwitterWrangler class Configuration attr_accessor :oauth_key def initialize self.oauth_key = nil end end class << self attr_accessor :configuration end def self.configure self.configuration ||= Configuration.new yield(configuration) if block_given? end end Thursday, May 3, 12

Slide 31

Slide 31 text

# lib/twitter_wrangler/configuration.rb module TwitterWrangler class Configuration attr_accessor :oauth_key def initialize self.oauth_key = nil end end class << self attr_accessor :configuration end def self.configure self.configuration ||= Configuration.new yield(configuration) if block_given? end end Thursday, May 3, 12

Slide 32

Slide 32 text

Chaos is hard to undo on a large library; starting with order is easy. At any moment that code in /lib might need to be a gem. Thursday, May 3, 12

Slide 33

Slide 33 text

Keep your domain models focused on the problem domain. Thursday, May 3, 12

Slide 34

Slide 34 text

Authentication Authorization Queueing Timeouts Caching Prescriptions BMI Appointments Thursday, May 3, 12

Slide 35

Slide 35 text

Common Sense Composition (Also known as the DCI Pattern) Thursday, May 3, 12

Slide 36

Slide 36 text

# lib/twitter_wrangler/roles/tweeting_patient.rb module TwitterWrangler::Roles::TweetingPatient def bmi_twitter_message “Today my BMI is #{body_mass_index} and I’m #{percent_of_body_mass_index_goal} from my goal of #{body_mass_index_goal}!” end end Thursday, May 3, 12

Slide 37

Slide 37 text

# lib/twitter_wrangler/roles/tweeting_patient.rb module TwitterWrangler::Roles::TweetingPatient def bmi_twitter_message “Today my BMI is #{body_mass_index} and I’m #{percent_of_body_mass_index_goal} from my goal of #{body_mass_index_goal}!” end end Thursday, May 3, 12

Slide 38

Slide 38 text

# lib/twitter_wrangler/roles/tweeting_patient.rb module TwitterWrangler::Roles::TweetingPatient def bmi_twitter_message “Today my BMI is #{body_mass_index} and I’m #{percent_of_body_mass_index_goal} from my goal of #{body_mass_index_goal}!” end end Thursday, May 3, 12

Slide 39

Slide 39 text

# lib/twitter_wrangler/support/fake_patient.rb class FakePatient def body_mass_index;27;end def percent_of_body_mass_index_goal;”95%”;end def body_mass_index_goal;25;end end # spec/twitter_wrangler/roles/tweeting_patient_spec.rb describe TweetingPatient do let(:patient) { FakePatient.new } before { FakePatient.extend TweetingPatient } it ‘asserts some fantastic things’ do #GLORIOUS SPECS end end Thursday, May 3, 12

Slide 40

Slide 40 text

# spec/twitter_wrangler/roles/tweeting_patient_spec.rb describe TweetingPatient do let(:patient) { mock() } before do patient.stubs({body_mass_index: 27, percent_of_body_mass_index_goal: “95%” body_mass_index_goal: 25 }) patient.extend TweetingPatient end it ‘asserts some fantastic things’ do #GLORIOUS SPECS end end Thursday, May 3, 12

Slide 41

Slide 41 text

# lib/twitter_wrangler/Contexts/BMITwitterUpdate.rb module TwitterWrangler::Contexts class BMITwitterUpdate attr_accessor: patient def initialize(patient) @patient = patient @patient.extend TweetingPatient end def call TwitterWranglerQueue.add @patient.bmi_twitter_message end end end Thursday, May 3, 12

Slide 42

Slide 42 text

# lib/twitter_wrangler/Contexts/BMITwitterUpdate.rb module TwitterWrangler::Contexts class BMITwitterUpdate attr_accessor: patient def initialize(patient) @patient = patient @patient.extend TweetingPatient end def call TwitterWranglerQueue.add @patient.bmi_twitter_message end end end Thursday, May 3, 12

Slide 43

Slide 43 text

# lib/twitter_wrangler/Contexts/BMITwitterUpdate.rb module TwitterWrangler::Contexts class BMITwitterUpdate attr_accessor: patient def initialize(patient) @patient = patient @patient.extend TweetingPatient end def call TwitterWranglerQueue.add @patient.bmi_twitter_message end end end Thursday, May 3, 12

Slide 44

Slide 44 text

# lib/twitter_wrangler/Contexts/BMITwitterUpdate.rb module TwitterWrangler::Contexts class BMITwitterUpdate attr_accessor: patient def initialize(patient) @patient = patient @patient.extend TweetingPatient end def call TwitterWranglerQueue.add @patient.bmi_twitter_message end end end Thursday, May 3, 12

Slide 45

Slide 45 text

# lib/twitter_wrangler/Contexts/BMITwitterUpdate.rb module TwitterWrangler::Contexts class BMITwitterUpdate attr_accessor: patient def initialize(patient) @patient = patient @patient.extend TweetingPatient end def call TwitterWranglerQueue.add @patient.bmi_twitter_message end end end Thursday, May 3, 12

Slide 46

Slide 46 text

# spec/models/patient_bmi_observer_spec.rb describe PatientBMIObserver do let(:bmi_update) { mock() } before do BMITwitterUpdate.stubs(:new).returns bmi_update bmi_update.stubs :call end it ‘sends a twitter update on mah BMI’ do # expect #call to have been called once end end Thursday, May 3, 12

Slide 47

Slide 47 text

# spec/models/patient_bmi_observer_spec.rb describe PatientBMIObserver do let(:bmi_update) { mock() } before do BMITwitterUpdate.stubs(:new).returns bmi_update bmi_update.stubs :call end it ‘sends a twitter update on mah BMI’ do # expect #call to have been called once end end Thursday, May 3, 12

Slide 48

Slide 48 text

Web App Library body mass index tweet called Authorization Twitter message Authentication Queueing User sees tweet Thursday, May 3, 12

Slide 49

Slide 49 text

Big Win: Freedom to implement with Google +. (If we were Google employeees) Thursday, May 3, 12

Slide 50

Slide 50 text

Treat /lib with love and respect. Use common sense composition to focus your domain models. Thursday, May 3, 12

Slide 51

Slide 51 text

Thanks! Thursday, May 3, 12