Rails Engine | Modular application
-
date post
20-Oct-2014 -
Category
Technology
-
view
5.405 -
download
3
description
Transcript of Rails Engine | Modular application
![Page 2: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/2.jpg)
Problem
monolithic application
no reusability
slow tests
bigger application, more mess
![Page 3: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/3.jpg)
Possible solutions
extract common functionality to modules
create gems
tie gem with rails application with railtie
rails engine
![Page 4: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/4.jpg)
What is a Gem ?
packaged application or library code tests documentation gemspec
![Page 5: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/5.jpg)
Creating gem
bundle gem simplify
structure code (lib/) tests (test/ or spec/) documentation (doc/ README) executable files (bin/)
information about gem simplify.gemspec
├── Gemfile├── LICENSE.txt├── README.md├── Rakefile├── lib│ ├── simplify│ │ └── version.rb│ └── simplify.rb└── simplify.gemspec
![Page 6: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/6.jpg)
Releasing Gem
change version
run `bundle`
commit changes
run `rake release`
$ rake releasestupid_spam_protection 0.0.2 built to pkg/stupid_spam_protection-0.0.2.gemTagged v0.0.2Pushed git commits and tagsPushed stupid_spam_protection 0.0.2 to rubygems.org
![Page 7: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/7.jpg)
in-memory testing with sqlite
require "active_record"require "sqlite3”
ActiveRecord::Base.establish_connection { :adapter => "sqlite3”, :database => ":memory:”}
load "db/schema.rb"
RSpec.configure do |config| config.around do |example| ActiveRecord::Base.transaction do example.run raise ActiveRecord::Rollback end endend
ActiveRecord::Schema.define do create_table "items", :force => true do |t| t.string "name” t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end
spec/spec_helper.rb
db/schema.rb
![Page 8: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/8.jpg)
testing with mysql database
begin ActiveRecord::Base.establish_connection(config)rescue ActiveRecord::Base.establish_connection(config.merge('database' => nil)) ActiveRecord::Base.connection.create_database(config['database'], { :charset => 'utf8’, :collation => 'utf8_unicode_ci’})end
load "db/schema.rb"
RSpec.configure do |config| # the same from previous slideend
config = HashWithIndifferentAccess.new({ :adapter => "mysql2", :host => "127.0.0.1", :username => "root", :password => "", :database => ”database_name”})
spec/spec_helper.rb
![Page 9: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/9.jpg)
Tie gem to Rails app Rails::Railtie
extend Rails framework load all needed dependencies
several hooks methods for all needs
module WnmSupport class Railtie < Rails::Railtie initializer "wnm_support.view_helpers" do ActionView::Base.send :include, BoolToHuman end endend
require "wnm_support/railtie" if defined?(Rails) lib/wnm_support.rb
lib/wnm_support/railtie.rb
![Page 10: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/10.jpg)
Railtie hooks 1
Plugin hooks initializer generators rake_tasks console
initializer "wnm_support.active_record_ext" do ActiveSupport.on_load(:active_record) do ActiveRecord::Base.send :include, WnmSupport::ActiveRecordExt::DestroyValidation endend
![Page 11: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/11.jpg)
Railtie hooks 2
Rails configuration hooks to_prepare (once in production, every request in
development)
before_initialize after_initialize app_middleware before_configuration before_eager_load generatorsconfig.to_prepare do AppController.send(:include, Controller::Authentication)end
![Page 12: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/12.jpg)
What is Rails Engine ?
every rails app is an engine
Rails::Engine < Rails::Railtie
all together gem skeleton railtie structure for app dummy app for testing assets
![Page 13: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/13.jpg)
Engine walkthrough
generate new engine
basic commands
testing
mounting to host app
overriding defaults
![Page 14: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/14.jpg)
Generating Engine
rails plugin new blorgh --mountable –full
everything (assets, controller, models, views) are inside Blorgh namespacemodule Blorgh class Engine < ::Rails::Engine isolate_namespace Blorgh endend
![Page 15: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/15.jpg)
Basics commands
in root directory of the engine bundle rake rspec rails generate # generate code for engine
in spec/dummy or test/dummy location rails console rails server rails generate # generate code for dummy app
![Page 16: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/16.jpg)
Testing
generally as in standard rails application
engine is tested inside dummy app
small problems with factory girl explicite model class in factory girl include routes for request tests
FactoryGirl.define do factory :user, :class => Blorgh ::User do sequence(:login) { |n| "admin_#{n}" } password "too_secret" password_confirmation { password } endend
RSpec.configure do |config| config.include Blorgh ::Engine.routes.url_helpersend
![Page 17: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/17.jpg)
Mounting Engine to host application
Gemfile gem “blorgh”, :path => “~/Sites/blorgh”
install migration `rake blorgh:install:migrations`
load seeds data Blorgh::Engine.load_seed # in rails console
require assets if needed *= require blorgh/application #
assets/stylesheets/application.css
mount to routes.rb mount Blorgh::Engine => "/blorgh"
![Page 18: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/18.jpg)
Get to engine and get back
routes to engine from host app prefix with engine name example engine “blorgh”
<%= link_to “Users”, blorgh.users_path %>
routes from engine to host app prefix with main_app example engine “blorgh”
<%= link_to “Home page”,main_app.root_path %>
![Page 19: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/19.jpg)
Controller, Model, etc. reopening classes
class_eval, module_eval
Overriding engine classes
Blorgh::Article.class_eval do has_many :users, :class_name => ‘User'end
app/decorators/models/blorgh/article.rb
module MySite class Railtie < ::Rails::Railtie config.to_prepare do Dir["app/decorators/**/*.rb"].each do |path| load path end end endend
![Page 20: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/20.jpg)
Overriding engine views
simple copy and paste view file that you want to override to the same location in host app
![Page 21: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/21.jpg)
Application Modules with Rails Engine
every new engine means new namespace
application modules means for us more engines with same namespace
inspiration from refinerycms
![Page 22: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/22.jpg)
The same namespacedifferent engines
module Wnm module Core class Engine < ::Rails::Engine isolate_namespace Wnm engine_name :wnm_core endend
module Wnm module Pages class Engine < ::Rails::Engine isolate_namespace Wnm engine_name :wnm_pages endend
rake wnm_core:install:migrations
Wnm::Core::Engine.load_seed
rake wnm_pages:install:migrations
Wnm::Pages::Engine.load_seed
both are nested in `Wnm` namespace
![Page 23: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/23.jpg)
Developing from localProduction from git
bundler does not allow to have same package from different sources in Gemfile
if ENV["LOAD_GEMS_FROM_LOCAL"] == "1" gem "wnm_core", :path => "~/Sites/wnm_core" gem "wnm_pages", :path => "~/Sites/wnm_pages" gem "wnm_customers", :path => "~/Sites/wnm_customers"else gem "wnm_core", :git => "git@…/wnm_core.git", :tag => "v1.0.0" gem "wnm_pages", :git => "git@…/wnm_pages.git", :tag => "v1.0.0" gem "wnm_customers", :git => "git@.../wnm_customers.git", :tag => "v1.0.0"end
export LOAD_GEMS_FROM_LOCAL=1 .rvmrc
Gemfile
![Page 24: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/24.jpg)
Deploying
change LOAD_GEMS_FROM_LOCAL to 0 and run `bundle`
commit and push
`cap deploy`
![Page 25: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/25.jpg)
Tips
if you are overriding views heavily always prefix routes path with engine module name
use class methods instead of constants
always write full path to class/module if it is in namespace ::ApplicationController instead of ApplicationController
testing is essential
<%= link_to page.name, wnm_page.page_path(page), :title => page.name %>
![Page 26: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/26.jpg)
Why use Rails::Engine ?
reusable code code hole application assets
modular thinking
gem with MVC
faster test
testing gem in dummy app infrastructure
some well known engines devise, kaminari, refinerycms
![Page 27: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/27.jpg)
Problems
if you are bending engines weird thinks can happen
a lot of weird errors application does not run in development mode from git
sources tests passed, but in browser it is not running in browser it is running but tests are throwing exceptions
overriding classes in host app
helper methods from host app does not work in overridden views
some things from documentation does not work at all
![Page 28: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/28.jpg)
Why we did this like that ?
a lot of our project are in general the same
build core for site quickly “scaffold” for application
rails engine is infrastructure for modular application
we can have backend and frontend in the same application module
![Page 29: Rails Engine | Modular application](https://reader038.fdocuments.net/reader038/viewer/2022103109/5444e008afaf9fe32b8b45a5/html5/thumbnails/29.jpg)
Sources
http://guides.rubygems.org/what-is-a-gem/ http://www.engineyard.com/blog/2010/extending-rails-3-with-
railties/ http://www.slideshare.net/AndyMaleh/rails-engine-patterns http://edgeguides.rubyonrails.org/engines.html http://edgeapi.rubyonrails.org/classes/Rails/Railtie.html http://edgeapi.rubyonrails.org/classes/Rails/Engine.html http://pivotallabs.com/users/shagemann/blog/articles/1994-
migrating-from-a-single-rails-app-to-a-suite-of-rails-engines http://railscasts.com/episodes/301-extracting-a-ruby-gem http://railscasts.com/episodes/303-publishing-a-gem http://railscasts.com/episodes/277-mountable-engines