Upload
yehuda-katz
View
4.959
Download
3
Tags:
Embed Size (px)
Citation preview
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