Content-Driven Web Applications with Magnolia and Ruby on Rails Patrik Metzmacher, Dievision http://www.dievision.de
I’m not really talking about Ruby or Rails
Creating an environment which clients enjoy to work with and (our) developers love to develop for.
The Approach How to run a Rails app within Magnolia Developing a minimal Magnolia/Rails application Testing The Rails Ecosystem
The Approach
Clean, consistent MVC architecture Same paradigms for content- and application-driven websites
File-based configuration and application data Work with version control and your favorite text editors/IDEs
Invent as few wheels as possible Leverage ready-made web-focussed solutions, best practices and a large development community
Clie nt-s perf ide Dependency orm E-Mails opti ance ching Management ing miza Ca U nit Test tion Templating ce Reu Integration an m g sable or rin Comp Testing P erf ito onents Hel pers/ on M Ut ilites Prototyping /CSS Scalab HTML ORM ility roce ssors JavaScript Prep Integration F orms AJAX IDE/Editor Validation Dep Sec loym support VC urit ent M y
Running Rails within Magnolia
Developers Clients JCR Repository
Hierarchical NoSQL-Store that can be edited by normal people.
SQL Database JCR Repository …
100% Java-based Ruby implementation Mature Fastest Ruby 1.8.7 implementation Web apps can be deployed in standard Servlet containers
JRuby Rack Filter Rack Connector Rack Web Application
Installation
Rack Filter Aggregation Filter Rendering Filter Magnolia Filter Chain Rails “Base” controller providing content, page, state objects Mapping between Magnolia Templates and Rails Layouts Rails Application
config/routes.rb app/views/layouts/homepage.html.erb <html> <head> <title> <%= cms_out :title %> MyApp::Application.routes.draw do </title> </head> match "/(*cmspath)" => <body> "sinicum/controllers/base#index" <%= main_bar %> <h1> end <%= cms_out :title %> </h1> <%= edit_bar :dialog => "title_dialog" %> </body> </html>
Model View Controller File-based configuration ActiveRecord-like JCR/ Object Mapper JCR-Wrapper with Helpers to match the Magnolia/Rails “Base” Ruby Semantics Taglib Controller Utilities: Scheduler, Imaging, Testing, etc. Magnolia Module for JRuby/Rack integration
Developing a minimal Magnolia/Rails application
The Task
app/models/templates/press_release.rb class Templates::PressRelease include Sinicum::Jcr::Base def title_homepage title_short || title end end
app/models/templates/press_release.rb class Templates::PressRelease include Sinicum::Jcr::Base def title_homepage title_short || title end def title_short @content[:title_short] end def title @content[:title] end end
config/routes.rb MyApp::Application.routes.draw do match "/en" => "home#index" match "/(*cmspath)" => "sinicum/controllers/base#index" end
app/controllers/home_controller.rb class HomeController < Sinicum::Controllers::Base def index @releases = Templates::PressRelease.for_homepage cms_render end end app/views/layouts/homepage.html.erb <ul> <% @releases.each do |release| -%> <li> <%= link_to release.title_homepage, release %> </li> <% end -%> </ul>
app/models/templates/press_release.rb class Templates::PressRelease include Sinicum::Jcr::Base def self.for_homepage where(:show_on_homepage => true). order("release_date desc", "order_day"). limit(2) end def title_homepage title_short || title end end
app/models/templates/press_release.rb class Templates::PressRelease include Sinicum::Jcr::Base scope :ordered, order("release_date desc", "order_day") scope :for_homepage, ordered.where(:show_on_homepage => true).limit(2) def title_homepage title_short || title end end
Configuration
Consistent Editable Version-Controlable Tied to the models
<?xml version="1.0" encoding="UTF-8"?> <sv:node sv:name="box_picture_teaser_large_item_dialog" xmlns:sv="http:// www.jcp.org/jcr/sv/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <sv:property sv:name="jcr:primaryType" sv:type="Name"> <sv:value>mgnl:contentNode</sv:value> </sv:property> <sv:property sv:name="jcr:mixinTypes" sv:type="Name"> <sv:value>mix:lockable</sv:value> </sv:property> <sv:property sv:name="jcr:uuid" sv:type="String"> <sv:value>92a944c5-c5b3-423c-af93-34a7b948c5be</sv:value> </sv:property> <sv:node sv:name="MetaData"> <sv:property sv:name="jcr:primaryType" sv:type="Name"> <sv:value>mgnl:metaData</sv:value> </sv:property> <sv:property sv:name="mgnl:activated" sv:type="Boolean"> <sv:value>true</sv:value> </sv:property> <sv:property sv:name="mgnl:activatorid" sv:type="String"> <sv:value>username</sv:value> </sv:property> <sv:property sv:name="mgnl:authorid" sv:type="String"> <sv:value>authorname</sv:value> </sv:property>
db/migrate/20100916101243_create_product_registration.rb class CreateProductRegistration < ActiveRecord::Migration def self.up create_table :product_registrations do |t| t.column :first_name, :string t.column :last_name, :string t.column :email, :string t.column :date_of_birth, :date t.column :date_of_purchase, :date t.column :point_of_sale, :string t.column :roles, :string t.column :newsletter_subscription, :boolean end end def self.down drop_table :product_registrations end end
db/jcr/module/dialogs/video_dialog.yml video_dialog: height: 500 tab_main: title: controlType: edit label: Title video_file: controlType: uuidLink description: Link to the video file repository: dms label: Video
Testing
app/views/layouts/homepage.html.erb <ul> <% @releases.each do |release| -%> <li> <%= link_to release.title_homepage, release %> </li> <% end -%> </ul>
spec/views/layouts/homepage.html.erb_spec.rb require 'spec_helper' describe "layouts/homepage.html.erb" do it "displays the title of a press release" do render rendered.should contain("Sustainability World Index") end end
spec/views/layouts/homepage.html.erb_spec.rb require 'spec_helper' describe "layouts/homepage.html.erb" do it "displays the title of a press release" do releases = [Templates::PressRelease.new( :title => "Lorem ipsum Sustainability World Index dolor sit amet", :title_short => "Sustainability World Index" )] assign(:releases, releases) render rendered.should contain("Sustainability World Index") end end
spec/views/layouts/homepage.html.erb_spec.rb describe "layouts/homepage.html.erb" do before(:each) do assign(:releases, [Factory(:sustainability_release)]) end it "displays the title of a press release" do render rendered.should contain("Sustainability World Index") end end spec/factories/templates/press_releases.rb Factory.define :sustainability_release, :class => Templates::PressRelease do |r| r.title "Lorem ipsum Sustainability World Index dolor sit amet", r.title_short "Sustainability World Index" end
The Ecosystem What we use
Capistrano for deployment Haml/Sass for better HTML templates and CSS NewRelic for performance monitoring Many high-quality Gems and Plugins
Our Conclusion
Very productive and “scalable” environment Performance is just fine Save/Reload vs. Save/Compile/Restart/Reload You can refactor in a dynamic language Collaboration is easier when everyone uses the same tools
http://www.dievision.de

Content-Driven Web Applications with Magnolia CMS and Ruby on Rails

  • 1.
    Content-Driven Web Applicationswith Magnolia and Ruby on Rails Patrik Metzmacher, Dievision http://www.dievision.de
  • 2.
    I’m not reallytalking about Ruby or Rails
  • 3.
    Creating an environmentwhich clients enjoy to work with and (our) developers love to develop for.
  • 4.
    The Approach How torun a Rails app within Magnolia Developing a minimal Magnolia/Rails application Testing The Rails Ecosystem
  • 7.
  • 8.
    Clean, consistent MVCarchitecture Same paradigms for content- and application-driven websites
  • 9.
    File-based configuration and application data Work with version control and your favorite text editors/IDEs
  • 10.
    Invent as fewwheels as possible Leverage ready-made web-focussed solutions, best practices and a large development community
  • 11.
    Clie nt-s perf ide Dependency orm E-Mails opti ance ching Management ing miza Ca U nit Test tion Templating ce Reu Integration an m g sable or rin Comp Testing P erf ito onents Hel pers/ on M Ut ilites Prototyping /CSS Scalab HTML ORM ility roce ssors JavaScript Prep Integration F orms AJAX IDE/Editor Validation Dep Sec loym support VC urit ent M y
  • 12.
  • 13.
    Developers Clients JCR Repository
  • 14.
    Hierarchical NoSQL-Store that can be edited by normal people.
  • 15.
    SQL Database JCR Repository …
  • 16.
    100% Java-based Ruby implementation Mature FastestRuby 1.8.7 implementation Web apps can be deployed in standard Servlet containers
  • 17.
    JRuby Rack Filter Rack Connector Rack Web Application
  • 18.
  • 20.
    Rack Filter Aggregation Filter Rendering Filter Magnolia Filter Chain Rails “Base” controller providing content, page, state objects Mapping between Magnolia Templates and Rails Layouts Rails Application
  • 21.
    config/routes.rb app/views/layouts/homepage.html.erb <html> <head> <title> <%= cms_out :title %> MyApp::Application.routes.draw do </title> </head> match "/(*cmspath)" => <body> "sinicum/controllers/base#index" <%= main_bar %> <h1> end <%= cms_out :title %> </h1> <%= edit_bar :dialog => "title_dialog" %> </body> </html>
  • 22.
    Model View Controller File-based configuration ActiveRecord-like JCR/ Object Mapper JCR-Wrapper with Helpers to match the Magnolia/Rails “Base” Ruby Semantics Taglib Controller Utilities: Scheduler, Imaging, Testing, etc. Magnolia Module for JRuby/Rack integration
  • 23.
  • 24.
  • 26.
    app/models/templates/press_release.rb class Templates::PressRelease include Sinicum::Jcr::Base def title_homepage title_short || title end end
  • 27.
    app/models/templates/press_release.rb class Templates::PressRelease include Sinicum::Jcr::Base def title_homepage title_short || title end def title_short @content[:title_short] end def title @content[:title] end end
  • 28.
    config/routes.rb MyApp::Application.routes.draw do match "/en" => "home#index" match "/(*cmspath)" => "sinicum/controllers/base#index" end
  • 29.
    app/controllers/home_controller.rb class HomeController <Sinicum::Controllers::Base def index @releases = Templates::PressRelease.for_homepage cms_render end end app/views/layouts/homepage.html.erb <ul> <% @releases.each do |release| -%> <li> <%= link_to release.title_homepage, release %> </li> <% end -%> </ul>
  • 30.
    app/models/templates/press_release.rb class Templates::PressRelease include Sinicum::Jcr::Base def self.for_homepage where(:show_on_homepage => true). order("release_date desc", "order_day"). limit(2) end def title_homepage title_short || title end end
  • 31.
    app/models/templates/press_release.rb class Templates::PressRelease include Sinicum::Jcr::Base scope :ordered, order("release_date desc", "order_day") scope :for_homepage, ordered.where(:show_on_homepage => true).limit(2) def title_homepage title_short || title end end
  • 32.
  • 33.
    Consistent Editable Version-Controlable Tied to the models
  • 35.
    <?xml version="1.0" encoding="UTF-8"?> <sv:nodesv:name="box_picture_teaser_large_item_dialog" xmlns:sv="http:// www.jcp.org/jcr/sv/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <sv:property sv:name="jcr:primaryType" sv:type="Name"> <sv:value>mgnl:contentNode</sv:value> </sv:property> <sv:property sv:name="jcr:mixinTypes" sv:type="Name"> <sv:value>mix:lockable</sv:value> </sv:property> <sv:property sv:name="jcr:uuid" sv:type="String"> <sv:value>92a944c5-c5b3-423c-af93-34a7b948c5be</sv:value> </sv:property> <sv:node sv:name="MetaData"> <sv:property sv:name="jcr:primaryType" sv:type="Name"> <sv:value>mgnl:metaData</sv:value> </sv:property> <sv:property sv:name="mgnl:activated" sv:type="Boolean"> <sv:value>true</sv:value> </sv:property> <sv:property sv:name="mgnl:activatorid" sv:type="String"> <sv:value>username</sv:value> </sv:property> <sv:property sv:name="mgnl:authorid" sv:type="String"> <sv:value>authorname</sv:value> </sv:property>
  • 36.
    db/migrate/20100916101243_create_product_registration.rb class CreateProductRegistration <ActiveRecord::Migration def self.up create_table :product_registrations do |t| t.column :first_name, :string t.column :last_name, :string t.column :email, :string t.column :date_of_birth, :date t.column :date_of_purchase, :date t.column :point_of_sale, :string t.column :roles, :string t.column :newsletter_subscription, :boolean end end def self.down drop_table :product_registrations end end
  • 37.
    db/jcr/module/dialogs/video_dialog.yml video_dialog: height:500 tab_main: title: controlType: edit label: Title video_file: controlType: uuidLink description: Link to the video file repository: dms label: Video
  • 38.
  • 39.
    app/views/layouts/homepage.html.erb <ul> <%@releases.each do |release| -%> <li> <%= link_to release.title_homepage, release %> </li> <% end -%> </ul>
  • 40.
    spec/views/layouts/homepage.html.erb_spec.rb require 'spec_helper' describe "layouts/homepage.html.erb"do it "displays the title of a press release" do render rendered.should contain("Sustainability World Index") end end
  • 41.
    spec/views/layouts/homepage.html.erb_spec.rb require 'spec_helper' describe "layouts/homepage.html.erb"do it "displays the title of a press release" do releases = [Templates::PressRelease.new( :title => "Lorem ipsum Sustainability World Index dolor sit amet", :title_short => "Sustainability World Index" )] assign(:releases, releases) render rendered.should contain("Sustainability World Index") end end
  • 42.
    spec/views/layouts/homepage.html.erb_spec.rb describe "layouts/homepage.html.erb" do before(:each) do assign(:releases, [Factory(:sustainability_release)]) end it "displays the title of a press release" do render rendered.should contain("Sustainability World Index") end end spec/factories/templates/press_releases.rb Factory.define :sustainability_release, :class => Templates::PressRelease do |r| r.title "Lorem ipsum Sustainability World Index dolor sit amet", r.title_short "Sustainability World Index" end
  • 43.
    The Ecosystem What we use
  • 44.
    Capistrano for deployment Haml/Sass for better HTML templates and CSS NewRelic for performance monitoring Many high-quality Gems and Plugins
  • 45.
  • 46.
    Very productive and“scalable” environment Performance is just fine Save/Reload vs. Save/Compile/Restart/Reload You can refactor in a dynamic language Collaboration is easier when everyone uses the same tools
  • 47.