rails engines in large apps

Post on 05-Dec-2014

661 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

Rails engines inlarge apps

Created by / Enrico Teotti @agenteo

Start off with a smallapplication

and the app complexity willgrow over time

Start off with a big complexapplication

ignoring complexity will hurtyou

In Ruby on Rails that is:

too many responsibilities inactive record models

long controller methods

helpers modules doing agazillion things

concerns

These poor decisions are sometime justified as:

"This is the Rails way!""Those are the Rails

conventions!""This is what a Rails developer

expect to see!"

bullshit

Rails gives conventions that fit small application domains but doesn'thave any for larger problems

How can Ruby on Rails engineshelp?

Engines can drop in functionality in your Rails monolithic app

DeviseKaminari

But they can do more that that"Rails::Engine allows you to wrap a specific Rails application or subset of

functionality and share it with other applications or within a largerpackaged application. Since Rails 3.0, every Rails::Application is just an

engine, which allows for simple feature and application sharing."

Rails API

we will create very specific engine only used in this app

Domain ModelThis is a domain model taken from Evan's book DDD

It is a simplified version of a real live problem

use Engines as building bricksof the application!

First, create an integration testin the main appspec/features/ship_cargo_spec.rb

require 'spec_helper'

feature 'As a staff member I want to ship a cargo' do scenario %{ Given I am on the cargo shipping page When I fill in a customer And I fill in a origin and destination Then I want to see an itinerary } do visit '/ship_cargo' endend

rspecF

Failures:

1) As a staff member I want to ship a cargoGiven I am on the cargo shipping pageWhen I fill in a customerAnd I fill in a origin and destinationThen I want to see an itineraryFailure/Error: visit '/ship_cargo'ActionController::RoutingError:No route matches [GET] "/ship_cargo"# ./spec/features/ship_cargo_spec.rb:10:in ̀block (2 levels) in <top (required)="">'

Finished in 0.00523 seconds1 example, 1 failure </top>

Create an enginerails plugin new cargo_shipping --mountable -T --dummy-path=spec/dummy

You can create a nested folderstructure

If you want to be more formal about the separation ofresponsibilities between the engines

The engine name will match ubiquitous language you share withstakeholders

engines/├── application_layer├── domain_layer│ ├── billing│ ├── customer│ └── shipping└── presentation_layer └── cargo_shipping

Main app Gemfilegem 'cargo_shipping', path: 'engines/presentation_layer/cargo_shipping'

Cargo Shipping Enginestructure

engines/presentation_layer/cargo_shipping/├── Gemfile├── Gemfile.lock├── MIT-LICENSE├── README.rdoc├── Rakefile├── app│ ├── assets│ ├── controllers│ ├── helpers│ ├── mailers│ └── views├── cargo_shipping.gemspec├── config│ └── routes.rb├── lib│ ├── cargo_shipping│ ├── cargo_shipping.rb│ └── tasks└── spec └── dummy

CargoShipping Engine routesengines/presentation_layer/cargo_shipping/config/routes.rbmodule PresentationLayer CargoShipping::Engine.routes.draw do get 'ship_cargo', to: 'ship_cargo#new' endend

CargoShipping Enginecontroller

engines/presentation_layer/cargo_shipping/app/controllers/cargo_shipping/ship_cargo_controller.rb

module CargoShipping class ShipCargoController < ActionController::Base

def new end

endend

Run the test again!F

Failures:

1) As a staff member I want to ship a cargoGiven I am on the cargo shipping pageWhen I fill in a customerAnd I fill in a origin and destinationThen I want to see an itineraryFailure/Error: visit '/ship_cargo'ActionController::RoutingError:No route matches [GET] "/ship_cargo"# ./spec/features/ship_cargo_spec.rb:10:in ̀block (2 levels) in <top (required)="">'

Finished in 0.00523 seconds1 example, 1 failure </top>

We need to change the mainapp routes!

config/routes.rbMaersk::Application.routes.draw do mount CargoShipping::Engine, at: "/"end

rspec.

Finished in 0.05205 seconds1 example, 0 failures

CargoShipping Engineengines/presentation_layer/cargo_shipping/app/controllers/cargo_shipping/ship_cargo_controller.rb

module CargoShipping class ShipCargoController < ActionController::Base

def new @customers = Customers::CustomerRepository.all end

endend

rspecF

Failures:

1) As a staff member I want to ship a cargoGiven I am on the cargo shipping pageWhen I fill in a customerAnd I fill in a origin and destinationThen I want to see an itineraryFailure/Error: visit '/ship_cargo'NameError:uninitialized constant CargoShipping::ShipCargoController::Customers# ./engines/presentation_layer/cargo_shipping/app/controllers/cargo_shipping/ship_cargo_controller.rb:5:in ̀new'# ./spec/features/ship_cargo_spec.rb:10:in ̀block (2 levels) in <top (required)="">'

Finished in 0.04333 seconds1 example, 1 failure </top>

Create a domain layer enginerails plugin new customers --mountable -T --dummy-path=spec/dummy

Main app Gemfilegem 'cargo_shipping', path: 'engines/presentation_layer/cargo_shipping'gem 'customers', path: 'engines/domain_layer/customers'

Generating models inCustomers Engine

engines/domain_layer/customers/app/models/customers/customer_repository.rb

rails generate model CustomerRepository email nameinvoke active_recordcreate db/migrate/20130921154844_create_customers_customer_repositories.rbcreate app/models/customers/customer_repository.rbinvoke rspeccreate spec/models/customers/customer_repository_spec.rb

rspec.

Finished in 0.05205 seconds1 example, 0 failures

Mailing list:https://groups.google.com/forum/?hl=en#!forum/components-in-rails

References:Wrangling Large Rails CodebasesArchitecting your Rails app for success!http://pivotallabs.com/leave-your-migrations-in-your-rails-engines/http://pivotallabs.com/experience-report-engine-usage-that-didn-t-work/

THE ENDEnrico Teotti / @agenteo / teotti.com

top related