Transcript

Merb 2.0

The long march into the future

Core Merb Principles

In Depth

Performance

Requests Per Second

Thin Mongrel

run proc do |env| [ 200, {"Content-Type" => "text/html"}, "Hello from rack" ]end

class QuickApp def call(env) [ 200, {"Content-Type" => "text/html"}, "Hello from rack" ] endend

run QuickApp.new

class QuickApp def call(env) [ 200, {"Content-Type" => "text/html"}, "Hello from rack" ] endend

run QuickApp.new

Thin Mongrel

match("/router").defer_to do |req, res| [ 200, {"Content-Type" => "text/html"}, "Hello" ]end

Thin Mongrel

class MyApp < Application def string "String" end end

class MyApp < Application def string "String" end end

Thin Mongrel

class MyApp < Application

def index render end def string "String" end end

class MyApp < Application

def index render end def string "String" end end

Close to the metal as possible

Close to the metal as you want

Performance Testing

KCacheGrind

use Merb::Rack::Profile

profile/url/callgrind.out.time

5% or greater

10% or greater

Merb::RenderMixin::_get_layout10.05 %

10% or greater

Mini-demo

Concurrency

0

4

8

12

16

20

1 2 4 8 16 32

Ideal concurrency curve

1 request in 16ms (16ms/req)

2 requests in 16ms (8ms/req)

4 requests in 16ms (4ms/req)

8 requests in 16ms (2ms/req)

16 requests in 16ms (1ms/req)

32 requests in 16ms (2req/ms)

0

4

8

12

16

20

1 2 4 8 16 32

Ideal concurrency curve

0

3.75

7.50

11.25

15.00

1 2 4 8 16 32

Chart 10

Merb MRI concurrency curve

0

3.75

7.50

11.25

15.00

1 2 4 8 16 32

Chart 12

Merb JRuby (Mongrel) concurrency curve

0

3.75

7.50

11.25

15.00

1 2 4 8 16 32 64 128 256

Chart 13

Merb Glassfish concurrency curve

0

3.75

7.50

11.25

15.00

1 2 4 8 16 32 64 128 256

Chart 13

Not ideal

0

3.75

7.50

11.25

15.00

1 2 4 8 16 32 64 128 256

Chart 13

Not ideal

0

3.75

7.50

11.25

15.00

1 2 4 8 16 32 64 128 256

Chart 13

Not ideal (but it works)

0

3.75

7.50

11.25

15.00

1 2 4 8 16 32 64 128 256

Chart 13

Not ideal (but it works)

Threadsafety

Merb::Config[:use_mutex]

class User cattr_accessor :currentend

class Foo < Merb::Controller before do User.current = session.user endend

class User cattr_accessor :currentend

class Foo < Merb::Controller before do User.current = session.user endend

FAIL

Shared state hurts puppies

Solutions

Thread-local

class User def self.current=(user) Thread.current[:user] = user end def self.current Thread.current[:user] endend

class Foo < Merb::Controller before do User.current = session.user endend

Using a Hash across threads

Hash {:x => 5}

Thread 1 Thread 2

Hash {:x => 5}

Thread 1 Thread 2

Hash {:x => 5}

Thread 1 Thread 2

Read x

Hash {:x => 5}

Thread 1 Thread 2

Read x Write x=”1”

Hash {:x => 5}

Thread 1 Thread 2

Read x Write x=”1”

clear x

Hash {:x => 5}

Thread 1 Thread 2

Read x Write x=”1”

clear x

is there an x?

Hash {:x => 5}

Thread 1 Thread 2

Read x Write x=”1”

clear x

is there an x?

x = 1no? return nil

Hash {:x => 5}

Mutex

Hash {:x => 5}

Thread 1 Thread 2

Hash {:x => 5}

Thread 1 Thread 2

Read x

is there an x?

yes? return 5

Hash {:x => 5}

Thread 1 Thread 2

Write x=”1”

clear x

x = 1

Read x

is there an x?

yes? return 5

Hash {:x => 5}

Hash {:x => 5}

Thread 1 Thread 2

Hash {:x => 5}

Thread 1 Thread 2

Write x=”1”

clear x

x = 1

Hash {:x => 5}

Thread 1 Thread 2

Write x=”1”

clear x

x = 1

Read x

is there an x?

yes? return 1

Hash {:x => 5}

Mutexes make non-atomic operations atomic

Modularity

merb_gems_version = "1.0.4"dm_gems_version = "0.9.7"

dependency "merb-action-args", merb_gems_versiondependency "merb-assets", merb_gems_version dependency "merb-cache", merb_gems_version dependency "merb-helpers", merb_gems_version dependency "merb-mailer", merb_gems_version dependency "merb-slices", merb_gems_version dependency "merb-auth-core", merb_gems_versiondependency "merb-auth-more", merb_gems_versiondependency "merb-auth-slice-password", merb_gems_versiondependency "merb-param-protection", merb_gems_versiondependency "merb-exceptions", merb_gems_version dependency "dm-core", dm_gems_version dependency "dm-aggregates", dm_gems_version dependency "dm-migrations", dm_gems_version dependency "dm-timestamps", dm_gems_version dependency "dm-types", dm_gems_version dependency "dm-validations", dm_gems_version

merb_gems_version = "1.0.4"dm_gems_version = "0.9.7"

dependency "merb-action-args", merb_gems_versiondependency "merb-assets", merb_gems_version dependency "merb-cache", merb_gems_version dependency "merb-helpers", merb_gems_version dependency "merb-mailer", merb_gems_version dependency "merb-slices", merb_gems_version dependency "merb-auth-core", merb_gems_versiondependency "merb-auth-more", merb_gems_versiondependency "merb-auth-slice-password", merb_gems_versiondependency "merb-param-protection", merb_gems_versiondependency "merb-exceptions", merb_gems_version dependency "dm-core", dm_gems_version dependency "dm-aggregates", dm_gems_version dependency "dm-migrations", dm_gems_version dependency "dm-timestamps", dm_gems_version dependency "dm-types", dm_gems_version dependency "dm-validations", dm_gems_version

merb_gems_version = "1.0.4"dm_gems_version = "0.9.7"

dependency "merb-action-args", merb_gems_versiondependency "merb-assets", merb_gems_version dependency "merb-cache", merb_gems_version dependency "merb-helpers", merb_gems_version dependency "merb-mailer", merb_gems_version dependency "merb-slices", merb_gems_version dependency "merb-auth-core", merb_gems_versiondependency "merb-auth-more", merb_gems_versiondependency "merb-auth-slice-password", merb_gems_versiondependency "merb-param-protection", merb_gems_versiondependency "merb-exceptions", merb_gems_version dependency "dm-core", dm_gems_version dependency "dm-aggregates", dm_gems_version dependency "dm-migrations", dm_gems_version dependency "dm-timestamps", dm_gems_version dependency "dm-types", dm_gems_version dependency "dm-validations", dm_gems_version

merb_gems_version = "1.0.4"dm_gems_version = "0.9.7"

dependency "merb-action-args", merb_gems_versiondependency "merb-assets", merb_gems_version dependency "merb-cache", merb_gems_version dependency "merb-helpers", merb_gems_version dependency "merb-mailer", merb_gems_version dependency "merb-slices", merb_gems_version dependency "merb-auth-core", merb_gems_versiondependency "merb-auth-more", merb_gems_versiondependency "merb-auth-slice-password", merb_gems_versiondependency "merb-param-protection", merb_gems_versiondependency "merb-exceptions", merb_gems_version dependency "dm-core", dm_gems_version dependency "dm-aggregates", dm_gems_version dependency "dm-migrations", dm_gems_version dependency "dm-timestamps", dm_gems_version dependency "dm-types", dm_gems_version dependency "dm-validations", dm_gems_version

JRuby 1.1.4

merb_gems_version = "1.0.4"dm_gems_version = "0.9.7"

dependency "merb-action-args", merb_gems_versiondependency "merb-assets", merb_gems_version dependency "merb-cache", merb_gems_version dependency "merb-helpers", merb_gems_version dependency "merb-mailer", merb_gems_version dependency "merb-slices", merb_gems_version dependency "merb-auth-core", merb_gems_versiondependency "merb-auth-more", merb_gems_versiondependency "merb-auth-slice-password", merb_gems_versiondependency "merb-param-protection", merb_gems_versiondependency "merb-exceptions", merb_gems_version dependency "dm-core", dm_gems_version dependency "dm-aggregates", dm_gems_version dependency "dm-migrations", dm_gems_version dependency "dm-timestamps", dm_gems_version dependency "dm-types", dm_gems_version dependency "dm-validations", dm_gems_version

merb_gems_version = "1.0.4"dm_gems_version = "0.9.7"

dependency "merb-action-args", merb_gems_versiondependency "merb-assets", merb_gems_version dependency "merb-cache", merb_gems_version dependency "merb-helpers", merb_gems_version dependency "merb-mailer", merb_gems_version dependency "merb-slices", merb_gems_version dependency "merb-auth-core", merb_gems_versiondependency "merb-auth-more", merb_gems_versiondependency "merb-auth-slice-password", merb_gems_versiondependency "merb-param-protection", merb_gems_versiondependency "merb-exceptions", merb_gems_version dependency "dm-core", dm_gems_version dependency "dm-aggregates", dm_gems_version dependency "dm-migrations", dm_gems_version dependency "dm-timestamps", dm_gems_version dependency "dm-types", dm_gems_version dependency "dm-validations", dm_gems_version

JRuby 1.1.5+

@overridable

# This is a stub method so plugins can # implement param filtering if they want.## ==== Parameters# params<Hash{Symbol => String}>:: # A list of params## ==== Returns# Hash{Symbol => String}:: # A new list of params, filtered as desired# # :api: plugin# @overridabledef self._filter_params(params) paramsend

class Articles < Application params_accessible :article => [:title, :body]end

class Articles < Application params_accessible :article => [:title, :body]end

class Articles < Application def self._filter_params(params) # deep_clone ret = Marshal.load(Marshal.dump(params))

ret[:post].reject! do |k,v| !k.in?(:title, :body) end endend

class Articles < Application def self._filter_params(params) # deep_clone ret = Marshal.load(Marshal.dump(params))

ret[:post].reject! do |k,v| !k.in?(:title, :body) end endend

# :api: public# @overridabledef _template_location(ctx, type, ctrlr) _conditionally_append_extension( ctrlr ? "#{ctrlr}/#{ctx}" : "#{ctx}", type)end

class Articles < Application def self._template_location( ctx, type, ctrlr)

"#{ctrlr}.#{ctx}.#{type}" endend

class Articles < Application def self._template_location( ctx, type, ctrlr)

"#{ctrlr}.#{ctx}.#{type}" endend

class Articles < Application def self._template_location( ctx, type, ctrlr)

"#{ctrlr}.#{ctx}.#{type}" endend

controller = “layout”

class Articles < Application self.template_roots = [ [ Merb.root / "app" / "views", :_template_location ], [ Merb.root / "my_views", :_my_template_location ] ]end

>> MerbAuthSlicePassword::Sessions.template_roots#=> [["/Users/wycats/Code/wycats/test/awesome/app/views", :_template_location], ["/Library/Ruby/Gems/1.8/gems/merb-auth-slice-password-1.0.4/app/views", :_slice_template_location], ["/Users/wycats/Code/wycats/test/awesome/slices/merb-auth-slice-password/app/views", :_slice_template_location]]

>> MerbAuthSlicePassword::Sessions.template_roots#=> [["/Users/wycats/Code/wycats/test/awesome/app/views", :_template_location], ["/Library/Ruby/Gems/1.8/gems/merb-auth-slice-password-1.0.4/app/views", :_slice_template_location], ["/Users/wycats/Code/wycats/test/awesome/slices/merb-auth-slice-password/app/views", :_slice_template_location]]

>> MerbAuthSlicePassword::Sessions.template_roots#=> [["/Users/wycats/Code/wycats/test/awesome/app/views", :_template_location], ["/Library/Ruby/Gems/1.8/gems/merb-auth-slice-password-1.0.4/app/views", :_slice_template_location], ["/Users/wycats/Code/wycats/test/awesome/slices/merb-auth-slice-password/app/views", :_slice_template_location]]

>> MerbAuthSlicePassword::Sessions.template_roots#=> [["/Users/wycats/Code/wycats/test/awesome/app/views", :_template_location], ["/Library/Ruby/Gems/1.8/gems/merb-auth-slice-password-1.0.4/app/views", :_slice_template_location], ["/Users/wycats/Code/wycats/test/awesome/slices/merb-auth-slice-password/app/views", :_slice_template_location]]

>> MerbAuthSlicePassword::Sessions.template_roots#=> [["/Users/wycats/Code/wycats/test/awesome/app/views", :_template_location], ["/Library/Ruby/Gems/1.8/gems/merb-auth-slice-password-1.0.4/app/views", :_slice_template_location], ["/Users/wycats/Code/wycats/test/awesome/slices/merb-auth-slice-password/app/views", :_slice_template_location]]

>> MerbAuthSlicePassword::Sessions.template_roots#=> [["/Users/wycats/Code/wycats/test/awesome/app/views", :_template_location], ["/Library/Ruby/Gems/1.8/gems/merb-auth-slice-password-1.0.4/app/views", :_slice_template_location], ["/Users/wycats/Code/wycats/test/awesome/slices/merb-auth-slice-password/app/views", :_slice_template_location]]

>> MerbAuthSlicePassword::Sessions.template_roots#=> [["/Users/wycats/Code/wycats/test/awesome/app/views", :_template_location], ["/Library/Ruby/Gems/1.8/gems/merb-auth-slice-password-1.0.4/app/views", :_slice_template_location], ["/Users/wycats/Code/wycats/test/awesome/slices/merb-auth-slice-password/app/views", :_slice_template_location]]

def _slice_template_location(ctx, type, ctrlr) if ctrlr && ctrlr.include?('/') # skip first segment if given (which is the module name) segments = ctrlr.split('/') "#{segments[1,segments.length-1].join('/')}/#{ctx}.#{type}" else # default template location logic _template_location(ctx, type, ctrlr) endend

Templates

def load_template_io(path) file = Dir[ "#{path}.{#{template_extensions.join(',')}}" ].first File.open(file, "r") if fileend

def load_template_io(path) templates = { Merb.root / "app" / "views" / "template_io" / "index.html.erb" => "Hello world", Merb.root / "app" / "views" / "template_io" / "two.html.erb" => "Two" }

if templates[path + ".erb"] VirtualFile.new(templates[path + ".erb"], path + ".erb") else file = Dir[ "#{path}.{#{template_extensions.join(',')}}" ].first File.open(file, "r") if file endend

Merb::Router.prepare {default_routes}

class TemplateIo < Merb::Controller def index render end def two render endend

Quick demo

Good Ruby citizen

Rubygems

Rubygems :(

Rubygems -- but getting :)

Working with community == helping the community

Rack

Rack middleware

Where is this going?

Apps as a first-class concept

module MyApp class TemplateIo < Application def index render end def two render end endend

module MyApp class TemplateIo < Application def index render end def two render end endend

module MyApp class TemplateIo < Application def index render end def two render end endend

MyApp::Application

module MyApp extend Merb::App mount ::Blog, :at => "/blog" Config[:framework] = flatend

Blog::Config[:log_delimiter] = "BLOG: "

Admin Application/Framework

CMS Application

DB Admin Application

Slices on Steroids

Resources as a first-class concept

module MyApp class Resource < Merb::Resource def list(klass) klass.all end def get(klass, ids) klass.get(ids) end def authorized?(namespace, *args) user == "wycats" end

def user request.session.user end endend

Why?

DRYing up common idioms

Increasing flexibility (where needed)

Core principle:Simple cases can’t get harder

Further improve merb server

Short term

Long term

Dynamic worker pools

Remove need for nginx

Self-managing cluster

Additional modules

i18n

l10n

Feed syndication

Flat pages

More powerful router

Router directly to a view

even better resource()

Framework for OSS Apps

Authentication

User Management

Authorization

Note:Communication primitives

Tailored stacks

Designers

Web shops

top related