Rails Engine | Modular application


Published on

presents how to make modular application using ruby engines

Published in: Technology
  • Be the first to comment

No Downloads
Total Views
On Slideshare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Rails Engine | Modular application

  1. 1. Problem monolithic application no reusability slow tests bigger application, more mess
  2. 2. Possible solutions extract common functionality to modules create gems tie gem with rails application with railtie rails engine
  3. 3. What is a Gem ? packaged application or library  code  tests  documentation  gemspec
  4. 4. Creating gem bundle gem simplify ├── Gemfile ├── LICENSE.txt ├── README.md structure ├── Rakefile  code (lib/) ├── lib │ ├── simplify  tests (test/ or spec/) │ │ └── version.rb  documentation (doc/ README) │ └── simplify.rb └── simplify.gemspec  executable files (bin/) information about gem  simplify.gemspec
  5. 5. 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
  6. 6. in-memory testing with sqlitespec/spec_helper.rbrequire "active_record"require "sqlite3”ActiveRecord::Base.establish_connection{ :adapter => "sqlite3”, :database => ":memory:”}load "db/schema.rb" db/schema.rb ActiveRecord::Schema.define doRSpec.configure do |config| create_table "items", :force => true do |t| config.around do |example| t.string "name” ActiveRecord::Base.transaction do t.datetime "created_at", :null => false example.run t.datetime "updated_at", :null => false raise ActiveRecord::Rollback end end endend
  7. 7. testing with mysql databasespec/spec_helper.rb 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 config = HashWithIndifferentAccess.new({ :adapter => "mysql2", load "db/schema.rb" :host => "", :username => "root", RSpec.configure do |config| :password => "", # the same from previous slide :database => ”database_name” end })
  8. 8. Tie gem to Rails app Rails::Railtie extend Rails framework  load all needed dependencies several hooks methods for all needsrequire "wnm_support/railtie" if defined?(Rails) lib/wnm_support.rbmodule WnmSupport lib/wnm_support/railtie.rb class Railtie < Rails::Railtie initializer "wnm_support.view_helpers" do ActionView::Base.send :include,BoolToHuman end endend
  9. 9. 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 end end
  10. 10. 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  generators config.to_prepare do AppController.send(:include, Controller::Authentication) end
  11. 11. 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
  12. 12. Engine walkthrough generate new engine basic commands testing mounting to host app overriding defaults
  13. 13. Generating Engine rails plugin new blorgh --mountable – full everything (assets, controller, models, views) are inside Blorgh namespace module Blorgh class Engine < ::Rails::Engine isolate_namespace Blorgh end end
  14. 14. 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
  15. 15. 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 testsFactoryGirl.define do factory :user, :class => Blorgh ::User do sequence(:login) { |n| "admin_#{n}" } RSpec.configure do |config| password "too_secret" config.include Blorgh ::Engine.routes.url_helper password_confirmation { password } end endend
  16. 16. 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"
  17. 17. 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 %>
  18. 18. Overriding engine classes  Controller, Model, etc.  reopening classes  class_eval, module_evalmodule MySite class Railtie < ::Rails::Railtie config.to_prepare do Dir["app/decorators/**/*.rb"].each do |path| load path app/decorators/models/blorgh/article.rb end end Blorgh::Article.class_eval do end has_many :users, :class_name => „Userend end
  19. 19. Overriding engine views simple copy and paste view file that you want to override to the same location in host app
  20. 20. Application Modules with Rails Engine every new engine means new namespace application modules means for us more engines with same namespace inspiration from refinerycms
  21. 21. The same namespace different engines module Wnm module Wnm module Core module Pages class Engine < ::Rails::Engine class Engine < ::Rails::Engine isolate_namespace Wnm isolate_namespace Wnm engine_name :wnm_core engine_name :wnm_pages end end end endrake wnm_core:install:migrations rake wnm_pages:install:migrationsWnm::Core::Engine.load_seed Wnm::Pages::Engine.load_seed both are nested in `Wnm` namespace
  22. 22. Developing from local Production from git  bundler does not allow to have same package from different sources in Gemfileexport LOAD_GEMS_FROM_LOCAL=1 .rvmrcif ENV["LOAD_GEMS_FROM_LOCAL"] == "1" Gemfile 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
  23. 23. Deploying change LOAD_GEMS_FROM_LOCAL to 0 and run `bundle` commit and push `cap deploy`
  24. 24. Tips if you are overriding views heavily always prefix routes path with engine module name<%= link_to page.name, wnm_page.page_path(page), :title => page.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
  25. 25. 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
  26. 26. 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
  27. 27. 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
  28. 28. 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
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.