Content-DrivenWeb Applications with
Magnolia and Ruby on Rails
Patrik Metzmacher, Dievisionhttp://www.dievision.de
I’m not really talking aboutRuby or Rails
Creating an environment which
clients enjoy towork with
and
(our) developers loveto develop for.
The Approach
How to run a Rails app within Magnolia
Developing a minimalMagnolia/Rails application
Testing
The Rails Ecosystem
The Approach
Same paradigms for content- and application-driven websites
Clean, consistent MVC architecture
File-based configuration and application data
Work with version control andyour favorite text editors/IDEs
Invent as few wheels as possible
Leverage ready-made web-focussed solutions, best practices and a large development community
CachingDependency Management E-Mails
ORM
Forms
Validation
AJAX
JavaScript
Integration
SecurityMVC
Helpers/
Utilites
Templating
Prototyping
HTML/CSS
Preprocessors
Perfor
mance
Monitor
ingReusable Components
Deployment
Client-side performance optimizationUnit Te
sting
Integration Testing
IDE/Editor support
Scalability
Running Rails within Magnolia
Developers Clients
JCRRepository
Hierarchical NoSQL-Storethat can be edited by
normal people.
JCRRepository
SQLDatabase
…
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 Web Application
Rack Connector
Installation
Rack Filter
Aggregation Filter
Rendering FilterMagnolia Filter Chain
Rails Application
Rails “Base” controller providingcontent, page, state objects
Mapping between Magnolia Templates and Rails Layouts
MyApp::Application.routes.draw do
match "/(*cmspath)" => "sinicum/controllers/base#index"
end
config/routes.rb
<html> <head> <title> <%= cms_out :title %> </title> </head> <body> <%= main_bar %> <h1> <%= cms_out :title %> </h1> <%= edit_bar :dialog => "title_dialog" %> </body></html>
app/views/layouts/homepage.html.erb
Model View Controller
Magnolia Module for JRuby/Rack integration
Magnolia/Rails “Base” Controller
Helpers to match the Taglib
JCR-Wrapper withRuby Semantics
ActiveRecord-like JCR/Object Mapper
File-based configuration
Utilities: Scheduler, Imaging, Testing, etc.
Developing a minimal Magnolia/Rails application
The Task
class Templates::PressRelease include Sinicum::Jcr::Base def title_homepage title_short || title endend
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
app/models/templates/press_release.rb
MyApp::Application.routes.draw do
match "/en" => "home#index" match "/(*cmspath)" => "sinicum/controllers/base#index"
end
config/routes.rb
class HomeController < Sinicum::Controllers::Base def index @releases = Templates::PressRelease.for_homepage cms_render end end
app/controllers/home_controller.rb
<ul> <% @releases.each do |release| -%> <li> <%= link_to release.title_homepage, release %> </li> <% end -%></ul>
app/views/layouts/homepage.html.erb
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
app/models/templates/press_release.rb
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>
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 endend
db/migrate/20100916101243_create_product_registration.rb
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
db/jcr/module/dialogs/video_dialog.yml
Testing
<ul> <% @releases.each do |release| -%> <li> <%= link_to release.title_homepage, release %> </li> <% end -%></ul>
app/views/layouts/homepage.html.erb
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") endend
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") endend
spec/views/layouts/homepage.html.erb_spec.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
spec/factories/templates/press_releases.rb
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