staying railsy - while scaling complexity or ruby on rails in enterprise software

72
Staying Railsy while scaling complexity Rails in the Enterprise

Upload: coupa-software

Post on 01-Jul-2015

449 views

Category:

Technology


1 download

DESCRIPTION

Staying railsy - while scaling complexity. Making Ruby on rails excel in complex software applications.

TRANSCRIPT

Page 1: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Staying Railsywhile scaling complexity

Rails in the Enterprise

Page 2: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

David Williams@metakube

Page 3: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software
Page 4: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Of all the terms I hate with a passion, “professional” would probably rank

above “enterprise” and just below “potty mouth”.

–@dhh in 2009

Page 5: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

What does

ENTERPRISEmean?

Page 6: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

XML

SOAP

EDI

BPEL ABAP/4

COBOL Work

flow

Page 7: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

automationvisibilitycompliance

Page 8: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

it comes down to

$

Page 9: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

But we’re software people. Start talking tech.

–y’all, just now

Page 10: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

COMPLEXITY

Page 11: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

What does

COMPLEXITYmean?

Page 12: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

languagesstandard user rolescontrollers database tablespermissionslines of codeAPI calls / dayrows in biggest tablespend through system

514

166183

157758k

>100k3m

$5.6b

Page 13: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

InternationalizationLocalization

XSS protectionSingle sign-onData securityExtensibility

CustomizationAPI integration

S*@P

Page 14: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

mixin

sco

mple

x queries

state

machin

es

RULEm

etaco

de

Page 15: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

mixin

sco

mple

x queries

state

machin

es

RULEm

etaco

de

Page 16: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Aspect Oriented Programming

Cross Cutting Concerns

Decorator Pattern

Adapter Pattern

Pointcuts

Separation of Concerns

Multiple Inheritance

Page 17: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Mixins

Page 18: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader

< ActiveRecord::Base

has_custom_fields

acts_as_revisionable

securable_by_account

api_in

api_out

end

Page 19: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader

< ActiveRecord::Base

has_custom_fields

acts_as_revisionable

securable_by_account

api_in

api_out

end

Let’s talk about these

Page 20: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

mixin

sco

mple

x queries

state

machin

es

RULESm

etaco

de

Page 21: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

I ♥ruby

Page 22: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software
Page 23: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software
Page 24: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class Api::DepartmentsController < Api::BaseController api_scaffold :department

end

Coupa::Application.routes.draw do namespace :api do resources :departments endend

Page 25: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class Department < ActiveRecord::Base

api_in [:name, :active], [], {:keys => ["id", "name"]}

api_out [:name, :active]

end

Page 26: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class Department < ActiveRecord::Base

api_in [:name, :active], [], {:keys => ["id", "name"]}

api_out [:name, :active]

end

(lack of) associations

Page 27: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class Department < ActiveRecord::Base

api_in [:name, :active], [], {:keys => ["id", "name"]}

api_out [:name, :active]

end

(lack of) associations

Page 28: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class Api::BaseController < AppController

def self.api_scaffold(model_id, options={})options.assert_valid_keys(:class_name, :scope, :user_scope, :validate_if, :before_save)singular_name = model_id.to_sclass_name = options[:class_name] || singular_name.camelizemodel_class = class_name.constantize

[way more code than I can fit]self.module_eval(overlay_methods)

end

end

Page 29: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

APIs and...

Page 30: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

bulk loaderscustom fieldssearchable table viewsbudgetingapproval workflowsrevision trackingreportingnotificationscachingand more...

APIs and...

Page 31: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

mixin

sco

mple

x queries

state

machin

es

RULEm

etaco

de

Page 32: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software
Page 33: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software
Page 34: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

We use AASM*Monkeypatched for transactional error handling, and for the event transitions to work like they used to.

*

Page 35: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader < ActiveRecord::Basestate :pending_buyer_action, :enter => Proc.new { |r|

Notifier.req_requires_action(r) }

state :pending_approval, :after_enter => Proc.new { |r|

ApprovalNotify.next_approver(r)}

state :ordered, :enter => Proc.new { |r|

OrderHeader.create_from_req(r) }end

Page 36: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader < ActiveRecord::Basestate :pending_buyer_action, :enter => Proc.new { |r|

Notifier.req_requires_action(r) }

state :pending_approval, :after_enter => Proc.new { |r|

ApprovalNotify.next_approver(r) }

state :ordered, :enter => Proc.new { |r|

OrderHeader.create_from_req(r) }end

Page 37: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader < ActiveRecord::Base event :submit_for_approval do

transitions :to => :pending_approval,

:from => [:draft, :cart], :guard => :approvable?

transitions :to => :pending_buyer_action,

:from => [:draft, :cart],

:guard => :submittable? endend

Page 38: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader < ActiveRecord::Base event :submit_for_approval do

transitions :to => :pending_approval,

:from => [:draft, :cart], :guard => :approvable?

transitions :to => :pending_buyer_action,

:from => [:draft, :cart],

:guard => :submittable? endend

Page 39: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader < ActiveRecord::Base event :submit_for_approval do

transitions :to => :pending_approval,

:from => [:draft, :cart], :guard => :approvable?

transitions :to => :pending_buyer_action,

:from => [:draft, :cart],

:guard => :submittable? endend

Page 40: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

What’s the point?

Page 41: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader < ActiveRecord::Base validates_presence_of :ship_to_address,

:if => Proc.new { |requisition_header|

requisition_header.status && !%w(draft cart pending_buyer_action). include?(requisition_header.status)

}

end

Page 42: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class RequisitionHeader < ActiveRecord::Base def editable?

user = User.current_user case self.status when 'pending_approval' approvable_by? && user && user.authorized?('approver', 'edit') when 'cart', 'draft' user == self.requested_by || user == self.created_by when 'pending_buyer_action' user && user.authorized?('buying', 'edit') else false end endend

Page 43: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Yawn. Show me a hack.

–y’all, just now

Page 44: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

module ActiveSupport::Inflector def humanize_with_translation(underscored_word) begin (I18n.translate!("statuses.#{underscored_word}",

:default => :"activerecord.models.#{underscored_word}", :count => 1) unless underscored_word.blank?) || ''

rescue I18n::MissingTranslationData => e humanize_without_translation(underscored_word) end end alias_method_chain :humanize, :translationend

I18n loose end tying: Ugly but useful

Page 45: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

mixin

sco

mple

x queries

state

machin

es

RULEm

etaco

de

Page 46: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

IF YOU TREAT YOUR DB AS DUMB

HOW CAN IT LOVE YOU?

IF YOU TREAT YOUR DB AS DUMB

HOW CAN IT LOVE YOU?

Page 47: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

SELECT distinct suppliers.id FROM suppliers JOIN supplier_items ON supplier_items.supplier_id = suppliers.id LEFT OUTER JOIN catalogs ON catalogs.id = supplier_items.catalog_id LEFT OUTER JOIN contracts ON contracts.id = supplier_items.contract_id LEFT OUTER JOIN business_group_assignments ON (business_group_assignments.securable_id = contracts.id AND business_group_assignments.securable_type = 'Contract') STRAIGHT_JOIN items ON (items.id = supplier_items.item_id AND items.active = 1 AND (items.connect_item_id IS NULL OR items.imported_from_connect = 1)) WHERE ( suppliers.status = 'active' AND (supplier_items.catalog_id IS NULL OR ( catalogs.status = 'accepted' AND (catalogs.start_date IS NULL OR '2011-11-15 18:30:00' >= catalogs.start_date) AND (catalogs.end_date IS NULL OR '2011-11-15 18:30:00' < catalogs.end_date) )) AND (supplier_items.contract_id IS NULL OR (contracts.status = 'published' AND business_group_assignments.business_group_id in (3,2,1) AND contracts.start_date <= '2011-11-15 18:30:00' AND (contracts.end_date IS NULL OR contracts.end_date > '2011-11-15 18:30:00') ))

Page 48: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

SELECT distinct suppliers.id FROM suppliers JOIN supplier_items ON supplier_items.supplier_id = suppliers.id LEFT OUTER JOIN catalogs ON catalogs.id = supplier_items.catalog_id LEFT OUTER JOIN contracts ON contracts.id = supplier_items.contract_id LEFT OUTER JOIN business_group_assignments ON (business_group_assignments.securable_id = contracts.id AND business_group_assignments.securable_type = 'Contract') STRAIGHT_JOIN items ON (items.id = supplier_items.item_id AND items.active = 1 AND (items.connect_item_id IS NULL OR items.imported_from_connect = 1)) WHERE ( suppliers.status = 'active' AND (supplier_items.catalog_id IS NULL OR ( catalogs.status = 'accepted' AND (catalogs.start_date IS NULL OR '2011-11-15 18:30:00' >= catalogs.start_date) AND (catalogs.end_date IS NULL OR '2011-11-15 18:30:00' < catalogs.end_date) )) AND (supplier_items.contract_id IS NULL OR (contracts.status = 'published' AND business_group_assignments.business_group_id in (3,2,1) AND contracts.start_date <= '2011-11-15 18:30:00' AND (contracts.end_date IS NULL OR contracts.end_date > '2011-11-15 18:30:00') ))Not very Rails

y

Page 49: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

So let’s step back a moment and talk about a common problem...

Page 50: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Displaying paginated search results

Page 51: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software
Page 52: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

What you’re querying on doesn’t always match what you’re

displaying

Page 53: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Suppliers

Contacts

Addresses

Commodities

Users

Countries

Roles Permissions

And this is a simple one...

Page 54: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Suppliers

Contacts

Addresses

Commodities

Users

Countries

Roles Permissions

And this is a simple one...

Shipping Terms

Payment Terms

Phone Numbers

Online Stores

Parent Suppliers

Page 55: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

outer join != :include

Page 56: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

.joins(begin options[:include].deep_exec{|c|

c.to_sym.send(:outer) }

rescue []end)

Depends on Ernie Miller’s awesome Metawhere / Squeel

Depends on a Ruby monkeypatch

Page 57: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Wow, that’s nasty. Why would you do that?

–y’all, just now

Page 58: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software
Page 59: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

30% improvement in common cases

Page 60: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

30% improvement in common cases

90% improvement in some nasty cases

Page 61: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

30% improvement in common cases

90% improvement in some nasty cases

YMMV

Page 62: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

MySQL doesn’t ♥ sorting

Page 63: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Narrow your SELECT clauses

Page 64: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

.paginate({:select => 'SQL_CALC_FOUND_ROWS

DISTINCT #{table}.id', :per_page => 20, :page => options[:page] })

Page 65: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

.paginate({:select => 'SQL_CALC_FOUND_ROWS

DISTINCT #{table}.id', :per_page => 20, :page => options[:page] })

MySQL count query avoidance hack. YMMV.

Page 66: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

.paginate({:select => 'SQL_CALC_FOUND_ROWS

DISTINCT #{table}.id', :per_page => 20, :page => options[:page] })

MySQL count query avoidance hack. YMMV.

[Then query just the rows you want]

Page 67: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class Array def deep_exec(&blk) result = [] each do |e| if e.respond_to?(:deep_exec) result << e.deep_exec(&blk) else result << yield(e) end end result endend

Like deep_clone? Try deep_exec.

Page 68: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

class Array def deep_exec(&blk) result = [] each do |e| if e.respond_to?(:deep_exec) result << e.deep_exec(&blk) else result << yield(e) end end result endend

class Hash def deep_exec(&blk) result = {} each do |k,v| result[yield k] =

if v.respond_to?(:deep_exec) v.deep_exec(&blk) else yield v end

end result endend

Like deep_clone? Try deep_exec.

Page 69: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

Totally out of time...

Page 70: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software

We’re Hiring!(I know, total surprise.)

Page 72: Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Software