merb

90
merb kicking mvc into high gear

Upload: yehuda-katz

Post on 18-May-2015

13.730 views

Category:

Technology


1 download

DESCRIPTION

All about Merb

TRANSCRIPT

Page 1: Merb

merbkicking mvc into high gear

Page 2: Merb

Booth 501

I work for Engine Yard. They help fund projects like Merb and Rubinius, and allow me to spend time making DataMapper better. They rock.

Page 3: Merb

what’s merb?

Page 4: Merb

web framework

Page 5: Merb

emphasizes efficiency and hackability

Instead of emphasizing a complete, monolithic framework with everything built in for the 80-20 case, Merb emphasizes making it easy to build what you need. That doesn’t mean we don’t want to make it easy to get started. Merb’s defaults provide the get-up-and-running feel that you get from more monolithic frameworks.

Page 6: Merb

modular

Page 7: Merb

improved api

We try to learn from other frameworks in Ruby and in the programming world at large. When we find something we like, we use it. When we find something we think we can make easier to interact with, we do that.

Page 8: Merb

give you as much headroom as possible

Our goal is to maintain as small a footprint as possible to give your app more system resources to use.

Page 9: Merb

no &:sym in merb

For instance, we don’t use Symbol#to_proc inside Merb, which is a convenience but dramatically slows down operations that use it.

Page 10: Merb

slowdowns are a bug

Slowdowns are not unfortunate. They are a bug. If you notice that a commit to Merb makes it slower, file a ticket on LightHouse. We will take it seriously.

Page 11: Merb

simplicity ftw

Page 12: Merb

MOTTO: no code is faster

than no code

Page 13: Merb

things you hear

Page 14: Merb

developer time is expensive

Page 15: Merb

servers are cheap

Page 16: Merb

ergo: efficiency doesn’t matter

Page 17: Merb

you can always throw more hardware at it

Page 18: Merb

And we at EY are happy to sell you hardware.

Page 19: Merb

but what if you could have both?

What if we didn’t have to choose between developer efficiency and machine efficiency?

Page 20: Merb

?

Would that be the holy grail?

Page 21: Merb

no, that’s merb.

Page 22: Merb

merb 0.9

Page 23: Merb

based on rack

Rack is based on Python’s WSGY, which allows a web framework like Merb to interact with a bunch of web servers (like Mongrel, Thin, Ebb, Webrick, CGI, FastCGI)

Page 24: Merb

class ApiHandler def initialize(app) @app = app end

def call(env) ... endend

use ApiHandlerrun Merb::Rack::Application.new

Here’s an example Rack handler.

Page 25: Merb

def call(env) request = Merb::Request.new if request.path =~ %r{/api/(.*)} [200, {“Content-Type” => “text/json”}, Api.get_json($1)] else @app.call(env) endend

Here’s what the call method looks like. You return a tuple of [status_code, headers, body].

Page 26: Merb

router

Merb has a kick-ass router.

Page 27: Merb

Merb::Router.prepare do |r| r.resources :posts do |post| post.resources :comments endend

We support simple stuff, like resources or even nested resources.

Page 28: Merb

Merb::Router.prepare do |r| r.match(%r{/login/(.*)}, :user_agent => /MSIE/). to(:controller => “account”, :action => “login”, :id => “[1]”end

But we also support regexen.

Page 29: Merb

Merb::Router.prepare do |r| r.match(%r{/login/(.*)}, :user_agent => /MSIE/). to(:controller => “account”, :action => “login”, :id => “[1]”end

checks request.user_agent

Or any part of the Request object.

Page 30: Merb

Merb::Router.prepare do |r| r.match(%r{/login/(.*)}, :user_agent => /MSIE/). to(:controller => “account”, :action => “login”, :id => “[1]”end

You can use any capture from your regex in #to hashes via “[capture_number]”

Page 31: Merb

Merb::Router.prepare do |r| r.match(%r{/login/(.*)}, :method => “post”). to(:controller => “account”, :action => “login”, :id => “[1]”end

Here’s another example of using the Request object.

Page 32: Merb

Merb::Router.prepare do |r| r.match(%r{/login/(.*)}, :protocol => “http://”). to(:controller => “account”, :action => “login”, :id => “[1]”end

Page 33: Merb

accepts header,meet provides

Your client tells Merb what it can accept. You tell Merb what a controller can provide.

Page 34: Merb

class Post < Application provides :json, :yaml

def show @post = Post[params[:id]] display @post endend

Here, you tell Merb to provide JSON and YAML. Then, you tell it to serialize the @post object into the appropriate form depending on what the client requested.

Page 35: Merb

display flowdisplay @foo

get content_type

XML

Look for foo.xml.* yes render

Here’s how Merb determines what to render given a specific parameter to display. This workflow assumes the client asked for XML.

Page 36: Merb

display flowdisplay @foo

get content_type

XML

Look for foo.xml.*

@foo.to_xmlno?

Page 37: Merb

display flowdisplay @foo

get content_type

XML

Look for foo.xml.*

@foo.to_xml

Page 38: Merb

default mimes

In order to make this work simply and seamlessly, we need Merb to know about a set of default mime types. Here’s the list.

Page 39: Merb

Merb.add_mime_type(:all, nil, %w[*/*])

Merb.add_mime_type(:yaml, :to_yaml, %w[application/x-yaml text/yaml])

Merb.add_mime_type(:text, :to_text, %w[text/plain])

Merb.add_mime_type(:html, :to_html, %w[text/html application/xhtml+xml application/html])

The first parameter is the internal name for the mime used by Merb. It is also the extension to use for a URL if you want that mime to be used (http://foo.com/index.json will use the :json type).

Page 40: Merb

Merb.add_mime_type(:xml, :to_xml, %w[application/xml text/xml application/x-xml], :Encoding => "UTF-8")

Merb.add_mime_type(:js, :to_json, %w[text/javascript application/javascript application/x-javascript])

Merb.add_mime_type(:json, :to_json, %w[application/json text/x-json])

The second parameter is an Array of mime types from the Accept header that will match the mime. The first item in the list is the mime-type that will be returned to the client.

Page 41: Merb

class Post < Application provides :json, :yaml, :xml

def show @post = Post[params[:id]] display @post endend

Here’s the example again.

Page 42: Merb

class Post < Application provides :json, :yaml, :xml

def show(id) @post = Post[id] display @post endend

merb-action-args

We can use the merb-action-args to simplify our controllers.

Page 43: Merb

a benefit of modularity

Let’s take a look at an simple benefit of Merb’s modularity.

Page 44: Merb

class Mail < Merb::Mailer before :shared_behavior

def index render_mail endend

Mailers look the same as controllers.

Page 45: Merb

class Mail < Merb::Mailer before :shared_behavior

def index render_mail :foo endend

You can pass them a symbol, which will use a different template.

Page 46: Merb

class Mail < Merb::Mailer before :shared_behavior

def index render_mail :html => :foo, :text => :bar endend

You can also use special templates for html and text, without any fancy methods.

Page 47: Merb

class Mail < Merb::Mailer before :shared_behavior

def index attach File.open(“/foo/bar”) render_mail :html => :foo, :text => :bar endend

You can attach a file, which will automatically figure out the mime and use it.

Page 48: Merb

class Mail < Merb::Mailer before :shared_behavior layout :mailers

def index attach File.open(“/foo/bar”) render_mail :html => :foo, :text => :bar endend

You can use layouts, just like regular controllers.

Page 49: Merb

send_mail Mail, :index, {:to => “[email protected]”}

You call mailers in your controllers like this.

Page 50: Merb

class Mail < Merb::Mailer before :shared_behavior layout :mailers

def index attach File.open(“/foo/bar”) render_mail :html => :foo, :text => :bar endend

Page 51: Merb

class Mail < Merb::Mailer before :shared_behavior layout :mailers

def index mail.to = [“[email protected]”] attach File.open(“/foo/bar”) render_mail :html => :foo, :text => :bar endend

You also have access to the raw mailer object (a MailFactory object).

Page 52: Merb

testing

What about testing?

Page 53: Merb

controllers are plain old ruby objects

You can test Merb objects like real Ruby objects.

Page 54: Merb

instantiated with new

Page 55: Merb

actions are methods

Page 56: Merb

they return data

Page 57: Merb

describe MyController do it “returns some json” do @mock = fake_request( :accept => “application/json”) c = MyController.new(@mock) c._dispatch(:index).should == {:foo => “bar”}.to_json endend

This is the raw way of doing testing. You probably wouldn’t do this, and it uses a private method (_dispatch).

Page 58: Merb

describe MyController do it “returns some json” do dispatch_to( MyController, :index, {:foo => :bar}, {:accept => “application/json”} ) @controller.body.should == {:foo => :bar}.to_json endend

You can use Merb’s helpers as well.

Page 59: Merb

describe MyController do it “returns some json” do dispatch_to( MyController, :index, {:foo => :bar}, {:accept => “application/json”} ) @controller.body.should == {:foo => :bar}.to_json endend

controller

Page 60: Merb

describe MyController do it “returns some json” do dispatch_to( MyController, :index, {:foo => :bar}, {:accept => “application/json”} ) @controller.body.should == {:foo => :bar}.to_json endend

action

Page 61: Merb

describe MyController do it “returns some json” do dispatch_to( MyController, :index, {:foo => :bar}, {:accept => “application/json”} ) @controller.body.should == {:foo => :bar}.to_json endend

params

Page 62: Merb

describe MyController do it “returns some json” do dispatch_to( MyController, :index, {:foo => :bar}, {:accept => “application/json”} ) @controller.body.should == {:foo => :bar}.to_json endend

request environment

Page 63: Merb

getting started

Page 64: Merb

sake (edge)

The preferred way to keep up with Merb now is to use the sake tasks.

Page 65: Merb

sudo gem install sake

sake -i http://merbivore.com/merb-dev.sake

mkdir ~/merb_sources

cd ~/merb_sources

sake merb:clone

sake merb:install

sake merb:upate # later

Do this.

Page 66: Merb

gem install merb

You can also get the latest released gems from Rubyforge.

Page 67: Merb

merb-gen app my_app

This is the equivalent of rails my_app.

Page 68: Merb

init.rb

You’ll probably want to customize init.rb.

Page 69: Merb

use_orm :datamapper

use_test :rspec

dependency “merb_more” # or what you need

... other dependencies ...

Merb::BootLoader.before_app_loads do DataMapper.setup(:salesforce, ...)end

Use before_app_loads for things that need to know about the structure of the framework (where your controllers/models/etc. are). The dependency method automatically handles the appropriate time to load the plugin.

Page 70: Merb

merb != Rails

We’ve had a lot of back-and-forth about things that are not the same between Merb and Rails. Here’s a list of things that you should be aware of when coming from a Rails background.

Page 71: Merb

before vs. before_filter

Controllers use before :foo instead of before_filter :foo.

Page 72: Merb

provides vs. respond_to

As shown above, we use a completely different API than Rails does for deciding what mime-type to return.

Page 73: Merb

:except vs. :exclude

We use before :foo, :except => :bar instead of before_filter :foo, :exclude => :bar

Page 74: Merb

logger vs. merb.logger

We do not have a Kernel method for our logger. You get access to our logger by doing Merb.logger.info!

Page 75: Merb

fooscontroller vs. foos

Since we don’t use const_missing to figure out where to load things, there are no naming requirements for any class. All classes are loaded at boot-time.

Page 76: Merb

url_for vs. url

We do url(:resource, @resource) instead of url_for_resource(@resource).

Page 77: Merb

css_include_tag :foo, :bundle => :basevs.

stylesheet_link_tag :foo, :bundle => :base

We use css_include_tag instead of stylesheet_link_tag. We also use js_include_tag for consistency.

Page 78: Merb

mailers

As shown above, our mailers are different (and much cooler).

Page 79: Merb

no rjs

We don’t use RJS. We recommend using Unobtrusive JavaScript techniques and a merb_jquery plugin along these lines is available.

Page 80: Merb

mandatory render

Merb actions return strings. This provides massive additional flexibility when using actions from other methods. Our render method just returns a string, and is thus required at the end of actions.

Page 81: Merb

render returns string

Page 82: Merb

render :foo

render :foo renders a template, but ...

Page 83: Merb

render “foo”

render “foo” renders the string wrapped in the layout. Just “foo” renders the string itself.

Page 84: Merb

render_json

render :json => :... becomes render_json :...

Page 85: Merb

no render :json => ...

Page 86: Merb

it’s considered a bug if

There’s a bunch of notions that are typically not considered bugs, but which we do.

Page 87: Merb

★ symbol#to_proc

★ alias_method_chain

★ merb gets slower

★ private api use

★ public api change*

★ non-documented

★ params

★ options keys

★ returns

★ yields

it’s considered a bug

*without deprecation period and notice in public API changelog

alias_method_chain is ok in your app code, but it’s a bug if we use it in Merb.

Page 88: Merb

thank you.

Page 89: Merb

any questions?

Feel free to comment on this blog post or email me at [email protected].

Page 90: Merb

</railsconf>