ruby off rails (english)
DESCRIPTION
Web applications development with Ruby (but without Rails)TRANSCRIPT
Ruby off Rails
by Stoyan ZhekovKyoto, 17-May-2008
08/05/19 2
Table of content● Coupling and Cohesion● Common needs for various web apps● ORM - Sequel (short overview)● Ruby Web Evolution● Enter Rack - The Middle Man● Thin, Ramaze, Tamanegi● Summary, Extras, Questions
08/05/19 3
Coupling and Cohesion (1)● Cohesion
http://en.wikipedia.org/wiki/Cohesion_(computer_science)● Coupling
http://en.wikipedia.org/wiki/Coupling_(computer_science)
08/05/19 4
Coupling and Cohesion (2)
Applications should be composed of components that show:
● High cohesion– doing small number of things and do them well
● Low (loosely) coupling– easy replace any component with another one
08/05/19 5
Coupling and Cohesion (3)
Google vs Amazon
08/05/19 6
Coupling and Cohesion (4)
VS
08/05/19 7
What about Rails?
08/05/19 8
Ruby on Rails● Full Stack – was good. Now?● Convention over configuration – good?● DRY – possible?
– Generators– Plugins– Libraries
08/05/19 9
Life without Rails
Is it possible?
08/05/19 10
Common needs for various web apps
● Database handling: ORM● Request processing: CGI env, uploads● Routing/dispatching: map URLs to classes● Rendering/views: rendering libraries● Sessions: persist objects or data
08/05/19 11
Database ORM● Active Record ?= ActiveRecord● ActiveRecord – a lot of problems
– http://datamapper.org/why.html● Data Mapper – fast, but not ready
– No composite keys (when I started using it)– Have it's own database drivers
● Sequel – good, I like it
08/05/19 12
ORM – Sequel (Why?)● Pure Ruby● Thread safe● Connection pooling● DSL for constructing DB queries● Lightweight ORM layer for mapping records to
Ruby objects● Transactions with rollback
08/05/19 13
ORM – Sequel (Core)● Database Connect
require 'sequel'DB = Sequel.open 'sqlite:///blog.db'
DB = Sequel.open 'postgres://cico:12345@localhost:5432/mydb'
DB = Sequel.open("postgres://postgres:postgres@localhost/my_db", :max_connections => 10, :logger => Logger.new('log/db.log'))
08/05/19 14
ORM – Sequel (Core, 2)
DB << "CREATE TABLE users (name VARCHAR(255) NOT NULL)"
DB.fetch("SELECT name FROM users") do |row| p r[:name]end
dataset = DB[:managers].where(:salary => 50..100).order(:name, :department)
paginated = dataset.paginate(1, 10) # first page, 10 rows per pagepaginated.page_count #=> number of pages in datasetpaginated.current_page #=> 1
08/05/19 15
ORM – Sequel (Model)
class Post < Sequel::Model(:my_posts) set_primary_key [:category, :title]
belongs_to :author has_many :comments has_and_belongs_to_many :tags
after_create do set(:created_at => Time.now) end
end
08/05/19 16
ORM – Sequel (Model, 2)
class Person < Sequel::Model one_to_many :posts, :eager=>[:tags]
set_schema do primary_key :id text :name text :email foreign_key :team_id, :table => :teams endend
08/05/19 17
Evolution
05/19/08 18
Ruby Web Evolution
Mongrel EventMachine
ThinWebrick Ebb
05/19/08 19
Thin web server● Fast – Ragel and Event Machine● Clustering● Unix sockets (the only one I found) - nginx● Rack-based – Rails, Ramaze etc.● Rackup files support (follows)
05/19/08 20
Thin + Nginx
thin start --servers 3 --socket /tmp/thinthin start --servers 3 –post 3000
# nginx.confupstream backend { fair; server unix:/tmp/thin.0.sock; server unix:/tmp/thin.1.sock; server unix:/tmp/thin.2.sock;}
05/19/08 21
The problem
Merb Ramaze Sinatra ...
CGI Webrick Mongrel Thin
How to connect them?
05/19/08 22
The solution - Rack
Merb Ramaze Sinatra ...
CGI Webrick Mongrel Thin
Rack – The Middle Man
05/19/08 23
What is Rack?
05/19/08 24
What IS Rack?
http://rack.rubyforge.org/
By Christian Neukirchen
05/19/08 25
WHAT is Rack?● Specification (and implementation) of a minimal
abstract Ruby API that models HTTP– Request -> Response
● An object that responds to call and accepts one argument: env, and returns:– a status, i.e. 200– the headers, i.e. {‘Content-Type’ => ‘text/html’}– an object that responds to each: ‘some string’
05/19/08 26
This is Rack!
class RackApp def call(env) [ 200, {"Content-Type" => "text/plain"}, "Hi!"] endend
app = RackApp.new
05/19/08 27
Free Hugs
http://www.freehugscampaign.org/
05/19/08 28
Hugs Application
p 'Hug!'
05/19/08 29
ruby hugs.rb
%w(rubygems rack).each { |dep| require dep }
class HugsApp def call(env) [ 200, {"Content-Type" => "text/plain"}, "Hug!"] endend
Rack::Handler::Mongrel.run(HugsApp.new, :Port => 3000)
05/19/08 30
Rack::Builderrequire 'hugs'app = Rack::Builder.new { use Rack::Static, :urls => ["/css", "/images"], :root => "public" use Rack::CommonLogger use Rack::ShowExceptions map "/" do run lambda { [200, {"Content-Type" => "text/plain"}, ["Hi!"]] } end map "/hug" do run HugsApp.new end}
Rack::Handler::Thin.run app, :Port => 3000
05/19/08 31
Rack Building Blocks● request = Rack::Request.new(env)
– request.get?● response = Rack::Response.new('Hi!')
– response.set_cookie('sess-id', 'abcde')● Rack::URLMap● Rack::Session● Rack::Auth::Basic● Rack::File
05/19/08 32
Rack Middleware
Can I create my own use Rack::... blocks?
class RackMiddlewareExample def initialize app @app = app end
def call env @app.call(env) endend
05/19/08 33
Common needs for various web apps
● Database handling: ORM Sequel● Request processing: Rack::Request(env)● Routing/dispatching: Rack 'map' and 'run'● Rendering/views: Tenjin, Amrita2● Sessions: Rack::Session
05/19/08 34
Does it scale?
05/19/08 35
rackup hugs.ru
require 'hugs'app = Rack::Builder.new {use Rack::Static, :urls => ["/css", "/images"], :root => "public" map "/" do run lambda { [200, {"Content-Type" => "text/plain"}, ["Hi!"]] }end map "/hug" do run HugsApp.newend}Rack::Handler::Thin.run app, :Port => 3000
05/19/08 36
Hugs Service● rackup -s webrick -p 3000 hugs.ru● thin start --servers 3 -p 3000 -R hugs.ru● SCGI● FastCGI● Litespeed● ....
05/19/08 37
Rack-based frameworks● Invisible – thin-based framework in 35 LOC
– http://github.com/macournoyer/invisible/● Coset – REST on Rack● Halcyon – JSON Server Framework● Merb● Sinatra● Ramaze● Rails !?
05/19/08 38
Rack coming to Rails● Rails already have Rack, but:
– raw req -> rack env -> Rack::Request ->CGIWrapper -> CgiRequest
● Ezra Zygmuntowicz (merb)'s repo on github– http://github.com/ezmobius/rails– raw req -> rack env -> ActionController::RackRequest– ./script/rackup -s thin -c 5 -e production
05/19/08 39
Ramaze● What: A modular web application framework● Where: http://ramaze.net/● How: gem install ramaze● git clone http://github.com/manveru/ramaze.git● Who: Michael Fellinger and Co.
05/19/08 40
Why Ramaze?● Modular (low [loosely] coupling)● Rack-based● Easy to use● A lot of examples (~25) – facebook, rapaste● Free style of development● Friendly community - #ramaze● “All bugs are fixed within 48 hours of reporting.”
05/19/08 41
Deployment options
● CGI● FastCGI● LiteSpeed● Mongrel● SCGI● Webrick
● Ebb● Evented Mongrel● Swiftiplied Mongrel● Thin● Whatever comes along
and fits into the Rack
05/19/08 42
Templating engines● Amrita2● Builder● Erubis● Ezmar● Haml● Liquid● Markaby
● RedCloth● Remarkably● Sass● Tagz● Tenjin● XSLT
05/19/08 43
Ezamar
05/19/08 44
Easy to use?
%w(rubygems ramaze).each {|dep| require dep}
class MainController < Ramaze::Controller def index; "Hi" endend
class HugsController < Ramaze::Controller map '/hug' def index; "Hug!" endend
Ramaze.start :adapter => :thin, :port => 3000
05/19/08 45
Ramaze Usage
class SmileyController < Ramaze::Controller map '/smile' helper :smiley
def index smiley(':)') endend
05/19/08 46
Ramaze Usage (2)module Ramaze module Helper module SmileyHelper FACES = { ':)' => '/images/smile.png', ';)' => '/images/twink.png'} REGEXP = Regexp.union(*FACES.keys)
def smiley(string) string.gsub(REGEXP) { FACES[$1] } end end endend
05/19/08 47
Alternative Stack● ORM – Sequel● Web server – Thin + NginX● Rack Middleware● Ramaze framework● Templates – HAML, Tenjin, Amrita2
05/19/08 48
Summary● Not trying to tell you to not use Rails● Just keep your mind open● Sequel is good ORM● Use Rack – DIY framework, DIY server● Ramaze is cool and easy to use