feature flagging with rails engines v0.2

59
deploying portions of a Rails app with Engines Enrico Teotti -- @agenteo

Transcript of feature flagging with rails engines v0.2

Page 1: feature flagging with rails engines v0.2

deploying portions of a Rails app with Engines

Enrico Teotti -- @agenteo

Page 2: feature flagging with rails engines v0.2

admin user interface

public user interface

domain logic

Page 3: feature flagging with rails engines v0.2

requirementrun the private and public portions of the app

on separate servers

Page 4: feature flagging with rails engines v0.2

service oriented architecture

first proposal

http://teotti.com/rails-service-oriented-architecture-alternative-with-components/

Page 5: feature flagging with rails engines v0.2

two apps sharing components via engines

second proposal

http://teotti.com/git-precommit-hooks-helping-local-ruby-gems-development/

Page 6: feature flagging with rails engines v0.2

one app feature flagging engines components

third proposal

Page 7: feature flagging with rails engines v0.2

public_uiadmin_ui

Page 8: feature flagging with rails engines v0.2

config/routes.rb

Rails.application.routes.draw do

case AppRunningMode.value

when :admin

mount AdminUi::Engine => "/"

when :public

mount PublicUi::Engine => "/"

else

mount AdminUi::Engine => "/"

mount PublicUi::Engine => "/"

end

end

Page 9: feature flagging with rails engines v0.2

RUNNING_MODE=public rails s

RUNNING_MODE=admin rails s

http://worldwideshipping-super-secret-domain.com/admin

http://worldwideshipping.com

rails s

http://localhost:3000

Page 10: feature flagging with rails engines v0.2

● are ruby gems● are special ruby gems that provide extra

behaviour (models, views, routes, rake tasks) to a Rails application

● they can be hosted on a gemserver or they can live inside your repository

● can be tested in isolation

rails engines

Page 11: feature flagging with rails engines v0.2

admin user interface

public user interface

domain logic

Page 12: feature flagging with rails engines v0.2

mkdir components

Page 13: feature flagging with rails engines v0.2

rails plugin new admin_ui --mountable --dummy=spec/dummy -O -T

mv admin_ui components

Page 14: feature flagging with rails engines v0.2

admin user interface

public user interface

domain logic

admin_ui

Page 15: feature flagging with rails engines v0.2

proceeding without automated tests

could drive you crazy

Page 16: feature flagging with rails engines v0.2

shared user interface (preview)

shared domain

admin_ui

Page 17: feature flagging with rails engines v0.2

Rails.application.routes.draw do

# ... public routes here

mount AdminUi::Engine => "/"

end

config/routes.rb

Page 18: feature flagging with rails engines v0.2

gem 'admin_ui', path: 'components/admin_ui'

Gemfile

Page 19: feature flagging with rails engines v0.2

public user interface

admin_ui

Page 20: feature flagging with rails engines v0.2

public user interface

admin_ui

public_ui

Page 21: feature flagging with rails engines v0.2

rails plugin new public_ui --mountable --dummy=spec/dummy -O -T

Page 22: feature flagging with rails engines v0.2

gem 'admin_ui', path: 'components/admin_ui'

gem 'public_ui', path: 'components/public_ui'

Gemfile

Page 23: feature flagging with rails engines v0.2

public_uiadmin_ui

Page 24: feature flagging with rails engines v0.2

Gem::Specification.new do |s|

# ... other fields up here

s.name = "public_ui"

s.add_dependency "rails", "~> 4.1.1"

s.add_dependency 'jquery-rails'

s.add_dependency 'mongoid'

s.add_runtime_dependency "admin_ui"

s.add_development_dependency 'byebug'

s.add_development_dependency 'database_cleaner'

s.add_development_dependency 'rspec-rails', '2.99.0'

s.add_development_dependency 'capybara'

s.add_development_dependency 'poltergeist'

end

public_ui.gemspec

Page 25: feature flagging with rails engines v0.2

Gem::Specification.new do |s|

# ... other fields up here

s.name = "public_ui"

s.add_dependency "rails", "~> 4.1.1"

s.add_dependency 'jquery-rails'

s.add_dependency 'mongoid'

s.add_runtime_dependency "admin_ui"

s.add_development_dependency 'byebug'

s.add_development_dependency 'database_cleaner'

s.add_development_dependency 'rspec-rails', '2.99.0'

s.add_development_dependency 'capybara'

s.add_development_dependency 'poltergeist'

end

public_ui.gemspec

Page 26: feature flagging with rails engines v0.2

Rails.application.routes.draw do

case AppRunningMode.value

when :admin

mount AdminUi::Engine => "/"

when :public

mount PublicUi::Engine => "/"

else

mount AdminUi::Engine => "/"

mount PublicUi::Engine => "/"

end

end

config/routes.rb

Page 27: feature flagging with rails engines v0.2

the two engines are now glued together!

Page 28: feature flagging with rails engines v0.2

admin_ui public_ui

domain_logic

Page 29: feature flagging with rails engines v0.2

rails plugin new domain_logic --dummy-path=spec/dummy

--mountable -O -T

Page 30: feature flagging with rails engines v0.2

admin_ui public_ui

shared_ui domain_logic

Page 31: feature flagging with rails engines v0.2

rails plugin new shared_ui --dummy-path=spec/dummy

--mountable -O -T

Page 32: feature flagging with rails engines v0.2

admin_ui/app/views/cargo_preview/show.html.erb

<%# admin console stuff here %>

<%= render partial: 'shared_ui/cargos/show' %>

<%# admin spaceship here %>

public_ui/app/views/cargos/show.html.erb

<%= render partial: 'shared_ui/cargos/show' %>

Page 33: feature flagging with rails engines v0.2

group :admin_app do

path 'components' do

gem 'admin_ui'

gem 'legacy_migration'

end

end

group :public_app do

path 'components' do

gem 'public_ui'

end

end

Gemfile

http://teotti.com/reduce-memory-footprint-requiring-portions-of-your-component-based-rails-applications/http://teotti.com/gemfiles-hierarchy-in-ruby-on-rails-component-based-architecture/

Page 34: feature flagging with rails engines v0.2

common pitfalls in Rails engines land

Page 35: feature flagging with rails engines v0.2

Gemfile

#gem 'domain_logic', path: 'components/domain_logic'

#gem 'shared_ui', path: 'components/shared_ui'

gem 'admin_ui', path: 'components/admin_ui'

gem 'public_ui', path: 'components/public_ui'

Page 36: feature flagging with rails engines v0.2

$ bundle

Resolving dependencies...

Could not find gem 'shared_ui (>= 0) ruby', which is required by gem

'admin_ui (>= 0) ruby', in any of the sources.

Page 37: feature flagging with rails engines v0.2

Gem::Specification.new do |s|

# ... other fields up here

s.name = "admin_ui"

s.add_dependency "rails", "~> 4.1.1"

s.add_dependency 'jquery-rails'

s.add_dependency 'mongoid'

s.add_dependency 'faraday'

s.add_dependency "domain_logic"

s.add_dependency "shared_ui"

s.add_development_dependency 'byebug'

s.add_development_dependency 'database_cleaner'

s.add_development_dependency 'rspec-rails', '2.99.0'

s.add_development_dependency 'vcr'

s.add_development_dependency 'webmock'

s.add_development_dependency 'capybara'

s.add_development_dependency 'poltergeist'

end

admin_ui.gemspec

Page 38: feature flagging with rails engines v0.2

Gem::Specification.new do |s|

# ... other fields up here

s.name = "admin_ui"

s.add_dependency "rails", "~> 4.1.1"

s.add_dependency 'jquery-rails'

s.add_dependency 'mongoid'

s.add_dependency 'faraday'

s.add_dependency "domain_logic"

s.add_dependency "shared_ui"

s.add_development_dependency 'byebug'

s.add_development_dependency 'database_cleaner'

s.add_development_dependency 'rspec-rails', '2.99.0'

s.add_development_dependency 'vcr'

s.add_development_dependency 'webmock'

s.add_development_dependency 'capybara'

s.add_development_dependency 'poltergeist'

end

admin_ui/admin_ui.gemspec

Page 39: feature flagging with rails engines v0.2

source "https://rubygems.org"

gem 'domain_logic', path: '../domain_logic'

gem 'shared_ui', path: '../shared_ui'

# Declare your gem's dependencies in admin_ui.gemspec.

# Bundler will treat runtime dependencies like base

dependencies, and

# development dependencies will be added by default to the :

development group.

gemspec

admin_ui/Gemfile

Page 40: feature flagging with rails engines v0.2

require

Page 41: feature flagging with rails engines v0.2

require 'nokogiri'

require 'faraday'

manually require

domain_logic/lib/domain_logic.rb

Page 42: feature flagging with rails engines v0.2

the test dummy app

Page 43: feature flagging with rails engines v0.2

require File.expand_path("../dummy/config/environment", __FILE__)

admin_ui/spec/rails_helper.rb

admin_ui/spec/dummy/config/boot.rb

# Set up gems listed in the Gemfile.

ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)

require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])

$LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)

Rails.application.routes.draw do

mount AdminUi::Engine => "/admin_ui"

end

admin_ui/spec /dummy/config/routes.rb

Page 44: feature flagging with rails engines v0.2

Rails.application.routes.draw do

mount AdminUi::Engine => "/admin_ui"

end

admin_ui/spec /dummy/config/routes.rb

Page 45: feature flagging with rails engines v0.2

https://github.com/shageman/the_next_big_thing/blob/master/build.sh

#!/bin/bash

unset BUNDLE_GEMFILE

result=0

if [ "$CI" == "true" ]; then

BUNDLE_PATH="$HOME/vendor/bundle"

fi

for test_script in $(find . -name test.sh); do

pushd `dirname $test_script` > /dev/null

source "$HOME/.rvm/scripts/rvm"

rvm use $(cat .ruby-version)@$(cat .ruby-gemset)

./test.sh

result+=$?

popd > /dev/null

done

if [ $result -eq 0 ]; then

echo "☺ SUCCESS"

else

echo "☹ FAILURE"

fi

exit $result

testing multiple engines

Page 46: feature flagging with rails engines v0.2

require_dependency "admin_ui/application_controller"

module AdminUi

class CargosController < ApplicationController

namespace application controller

Page 47: feature flagging with rails engines v0.2

module AdminUi

class CargosController < AdminUi::ApplicationController

namespace application controller

Page 48: feature flagging with rails engines v0.2

module AdminUi

class VoyagesController < ApplicationController

namespace application controller

Page 49: feature flagging with rails engines v0.2

module AdminUi

class VoyagesController < ApplicationController

namespace application controller

Page 50: feature flagging with rails engines v0.2

scaffolding and other generators

Page 51: feature flagging with rails engines v0.2

rails generate scaffold_controller admin_ui/cargo source destination

weight --model-name=DomainLogic::Cargo --orm=mongoid -t=''

scaffolding and other generators

Page 52: feature flagging with rails engines v0.2

require_dependency "admin_ui/domain_logic/application_controller"

scaffolding and other generators

Page 53: feature flagging with rails engines v0.2

Failures:

1) Staff booking a cargo booking a cargo fitting a pending voyage

Failure/Error: visit '/admin/cargos'

LoadError:

No such file to load -- admin_ui/domain_logic/application_controller

# ./engines/admin_ui/app/controllers/admin_ui/cargos_controller.rb:1:in `<top (required)>'

# ./spec/features/book_cargo_spec.rb:12:in `block (2 levels) in <top (required)>'

Finished in 0.03067 seconds (files took 1.85 seconds to load)

2 examples, 1 failure

scaffolding and other generators

Page 54: feature flagging with rails engines v0.2

Failures:

1) Staff booking a cargo booking a cargo fitting a pending voyage

Failure/Error: visit '/admin/cargos'

RuntimeError:

Circular dependency detected while autoloading constant AdminUi::AdminUi::CargosHelper

# ./engines/admin_ui/app/controllers/admin_ui/application_controller.rb:2:in `<module:AdminUi>'

# ./engines/admin_ui/app/controllers/admin_ui/application_controller.rb:1:in `<top (required)>'

# ./engines/admin_ui/app/controllers/admin_ui/cargos_controller.rb:1:in `<top (required)>'

# ./spec/features/book_cargo_spec.rb:12:in `block (2 levels) in <top (required)>'

scaffolding and other generators

Page 55: feature flagging with rails engines v0.2

Failures:

1) Staff booking a cargo booking a cargo fitting a pending voyage

Failure/Error: visit '/admin/cargos'

ActionView::Template::Error:

undefined local variable or method `new_domain_logic_cargo_path' for #<#<Class:0x007fb38887bb38>:0x007fb388873690>

# ./engines/admin_ui/app/views/admin_ui/cargos/index.html.erb:29:in

`_engines_admin_ui_app_views_admin_ui_cargos_index_html_erb__4343322268368929370_70204533119300'

# ./spec/features/book_cargo_spec.rb:12:in `block (2 levels) in <top (required)>'

scaffolding and other generators

Page 56: feature flagging with rails engines v0.2

fang:domain_logic agenteo$ rails generate mongoid:config

/Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/mongoid-4.0.0/lib/rails/generators/mongoid/config/config_generator.rb:16:in `app_name': undefined

method `parent' for nil:NilClass (NoMethodError)

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/thor-0.19.1/lib/thor/command.rb:27:in `run'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/thor-0.19.1/lib/thor/invocation.rb:126:in `invoke_command'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/thor-0.19.1/lib/thor/invocation.rb:133:in `block in invoke_all'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/thor-0.19.1/lib/thor/invocation.rb:133:in `each'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/thor-0.19.1/lib/thor/invocation.rb:133:in `map'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/thor-0.19.1/lib/thor/invocation.rb:133:in `invoke_all'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/thor-0.19.1/lib/thor/group.rb:232:in `dispatch'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/thor-0.19.1/lib/thor/base.rb:440:in `start'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/railties-4.1.4/lib/rails/generators.rb:157:in `invoke'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/railties-4.1.4/lib/rails/commands/generate.rb:11:in `<top (required)>'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/railties-4.1.4/lib/rails/engine/commands.rb:19:in `require'

from /Users/agenteo/.rvm/gems/ruby-2.1.2@worldwide_shipping/gems/railties-4.1.4/lib/rails/engine/commands.rb:19:in `<top (required)>'

from bin/rails:12:in `require'

from bin/rails:12:in `<main>'

mongoid generators

Page 57: feature flagging with rails engines v0.2
Page 58: feature flagging with rails engines v0.2
Page 59: feature flagging with rails engines v0.2

FOLLOWUP READShttp://guides.rubyonrails.org/engines.html

https://leanpub.com/cbra

teotti.com/topics/component-based-rails-architecture

http://cbra.info/

Enrico Teotti @agenteo [email protected]