Permalink
Browse files

Introduce Rails Metal

  # app/metal/poller.rb
  class Poller < Rails::Rack::Metal
    def call(env)
      if env["PATH_INFO"] =~ /^\/poller/
        [200, {"Content-Type" => "application/json"}, Message.recent.to_json]
      else
        super
      end
    end
  end

* There is a generator to help you get started
    `script/generate metal poller`

* Also, metal bits can be ran standalone with rackup
    `rackup app/metal/poller.rb`
  • Loading branch information...
josh committed Dec 16, 2008
1 parent c4023cb commit 8c3a54366435eebc2c8aa63b63e1349ce74a7b38
@@ -155,6 +155,8 @@ def process
initialize_framework_settings
initialize_framework_views
initialize_metal
add_support_load_paths
load_gems
@@ -533,6 +535,12 @@ def initialize_i18n
end
end
def initialize_metal
Dir["#{configuration.root_path}/app/metal/*.rb"].each do |file|
configuration.middleware.use(File.basename(file, '.rb').camelize)
end
end
# Initializes framework-specific settings for each of the loaded frameworks
# (Configuration#frameworks). The available settings map to the accessors
# on each of the corresponding Base classes.
@@ -915,6 +923,7 @@ def default_load_paths
# Followed by the standard includes.
paths.concat %w(
app
app/metal
app/models
app/controllers
app/helpers
@@ -933,6 +942,7 @@ def default_load_once_paths
def default_eager_load_paths
%w(
app/metal
app/models
app/controllers
app/helpers
@@ -2,6 +2,7 @@ module Rails
module Rack
autoload :Debugger, "rails/rack/debugger"
autoload :Logger, "rails/rack/logger"
autoload :Metal, "rails/rack/metal"
autoload :Static, "rails/rack/static"
end
end
@@ -0,0 +1,21 @@
module Rails
module Rack
class Metal
NotFound = lambda { |env|
[404, {"Content-Type" => "text/html"}, "Not Found"]
}
def self.call(env)
new(NotFound).call(env)
end
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
end
end
end
end
@@ -0,0 +1,8 @@
Description:
Cast some metal!
Examples:
`./script/generate metal poller`
This will create:
Metal: app/metal/poller.rb
@@ -0,0 +1,8 @@
class MetalGenerator < Rails::Generator::NamedBase
def manifest
record do |m|
m.directory 'app/metal'
m.template 'metal.rb', File.join('app/metal', "#{file_name}.rb")
end
end
end
@@ -0,0 +1,12 @@
# Allow the metal piece to run in isolation
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
class <%= class_name %> < Rails::Rack::Metal
def call(env)
if env["PATH_INFO"] =~ /^\/<%= file_name %>/
[200, {"Content-Type" => "text/html"}, "Hello, World!"]
else
super
end
end
end

63 comments on commit 8c3a543

@sco

This comment has been minimized.

sco replied Dec 16, 2008

Excellent.

@foca

This comment has been minimized.

Contributor

foca replied Dec 16, 2008

sweeeeeet

@ericallam

This comment has been minimized.

Contributor

ericallam replied Dec 16, 2008

drool

@anildigital

This comment has been minimized.

Contributor

anildigital replied Dec 16, 2008

kewl.. can somebody explain me, what is the metal about?

@topfunky

This comment has been minimized.

topfunky replied Dec 16, 2008

Great idea!

While the paint is still wet, how about calling it something that matches the descriptive names of “controllers” “models” and “views”?

I suggest “middleware”.

@drnic

This comment has been minimized.

Contributor

drnic replied Dec 16, 2008

No tests for this patch? :)

@mtodd

This comment has been minimized.

Contributor

mtodd replied Dec 16, 2008

Seems to be a recreation of the already simple idea of middleware… but I like it.

Also agree with calling it Middleware, though “Metal” is kinda badass. :)

@judofyr

This comment has been minimized.

judofyr replied Dec 16, 2008

You can’t kill the metal.
The metal will live on.

@dhh

This comment has been minimized.

Member

dhh replied Dec 16, 2008

Middleware is a more generic notion for anything that’s used for preprocessing of a request. Metal is intended to be end-points for things that are simple and needs to be really fast. So think of Metal < Middleware.

@amerine

This comment has been minimized.

Contributor

amerine replied Dec 16, 2008

Metal sounds much better then “middleware”. But as far as having a “built-in” place to run “middeware code”…. I’m down with that.

@norbert

This comment has been minimized.

Contributor

norbert replied Dec 16, 2008

Wow, really? Sigh.

@nakajima

This comment has been minimized.

Contributor

nakajima replied Dec 16, 2008

I like this a lot. However, I am with drnic. I’d also like to see some “metals” test helpers added to the framework, since this doesn’t seem to fall under the category of model, functional or integration (well, maybe integration).

@josh

This comment has been minimized.

Member

josh replied Dec 16, 2008

@norbert just curious what you don’t dig?

@nakajima integration tests will work great since they test the entire call stack.

@tristandunn

This comment has been minimized.

Contributor

tristandunn replied Dec 16, 2008

This would be great for a project of mine if you can still use the session with it. Can you and if so is anything extra needed?

@tjogin

This comment has been minimized.

tjogin replied Dec 16, 2008

What “regular” parts of Rails are bypassed in order to make this so much faster?

@wycats

This comment has been minimized.

Member

wycats replied Dec 16, 2008

Pretty nice! It’s great to see Rails embracing Rack and Rack middleware.

@drnic

This comment has been minimized.

Contributor

drnic replied Dec 16, 2008

@nakajima yeah I think I’d like to see a test/metal/my_metal_test.rb + some helpers. very sweet.

@josh glad it can still work via integration/cucumber etc

this is the merb-pastie integration that I always wanted; so nice.

@tjogin

This comment has been minimized.

tjogin replied Dec 16, 2008

What “regular” parts of Rails are bypassed in order to make this so much faster?

@samgranieri

This comment has been minimized.

Contributor

samgranieri replied Dec 16, 2008

@tjogin, josh replaced the old cgi engine with rack. Rails is just one big rack application now. I think this is pretty cool for intercepting requests that you dont want to be processed by actioncontroller. I’m going to use this for heartbeat monitoring with HAProxy. One caveat while developing: unlike regular controllers you need to restart mongrel/thin when you change any metal.

I think this should be renamed to spikes.

@tjogin

This comment has been minimized.

tjogin replied Dec 16, 2008

@samgranieri: So the reason “metal” is so much faster is simply because it doesn’t load actioncontroller? That’s it?

@drnic

This comment has been minimized.

Contributor

drnic replied Dec 16, 2008

+1 for “middleware”

@drnic

This comment has been minimized.

Contributor

drnic replied Dec 16, 2008

@tjogin it would bypass the normal routing system too

@tjogin

This comment has been minimized.

tjogin replied Dec 16, 2008

@drnic: Anything else?

@defunkt

This comment has been minimized.

Contributor

defunkt replied Dec 16, 2008

Blacker than the blackest black, times infinity.

@drnic

This comment has been minimized.

Contributor

drnic replied Dec 16, 2008

@tjogin – http://soylentfoo.jnewland.com/articles/2008/12/16/rails-metal-a-micro-framework-with-the-power-of-rails-m has a speed summary, btw

@samgranieri

This comment has been minimized.

Contributor

samgranieri replied Dec 16, 2008

@tjogin, that’s not entirely accurate.

When the rails app starts up, it ( loads | autoloads | ? ) the framework , including actioncontroller.

The metal is so much faster because it intercepts the Rack call. In the example, if the route containts poller, send a json message, if not, then pass the request to action controller via super

@josh

This comment has been minimized.

Member

josh replied Dec 16, 2008

@wycats Glad you like it. I hope this means we can start sharing Rack compatible code between Rails and Merb.

@tjogin it bypasses everything. whatever you code in your metal bit talks directly to the server (mongrel/thin/etc).

Still a bit of confusion about metal == middleware? The answer is no. Metal bits are designed to be endpoints.

I think this commit did not get noticed: http://github.com/rails/rails/commit/06ed8e451198b2296d8b2752741e259b4f995081
I added `config.middleware.use Rack::Cache` to the initializer as a hook for any plugins that want to connect in as middleware.

Yes, I know its really all the same thing but metal does sound really cool :)

@drnic I think integration tests are a good fit here. Thats my opinion though. If you think some helpers are inorder, go for it. (Actually, if I had my way, everything would be an integration test ;)

@tjogin

This comment has been minimized.

tjogin replied Dec 16, 2008

@josh: So, since metal bypasses “everything”, does that mean that “metal” code can’t use some of the stuff the rest of a Rails app uses? Like.. ActiveSupport, or anything like that? Maybe I’m curious for too much information too soon. Blogs are likely delve deeper into this.

@samgranieri

This comment has been minimized.

Contributor

samgranieri replied Dec 16, 2008

@tjogin, yeah, ActiveSupport works. http://gist.github.com/36861

@drnic

This comment has been minimized.

Contributor

drnic replied Dec 16, 2008

From Ezra’s (sarcastic) tweet (http://twitter.com/ezmobius/statuses/1061662672) “rack middleware you cannot use outside of rails” – what would be the mechanism for a writing a plugin that contained a rack call hook, that could be used in different frameworks (rails/merb/other-rack-super-framework)? Given the rails-specific superclass, then the call behaviour would need to be in a module, and included into either a Rails::Rack::Metal subclass via the rails plugin’s init.rb or hooked into each specific Merb/other-rack-superframework in a specific way? Or can this be generic so its uber simple to write sharable rack plugins that are framework neutral?

@josh

This comment has been minimized.

Member

josh replied Dec 16, 2008

@drnic

  1. init.rb

config.middleware.use Rack::MyFanaticMiddleware

This is the same api as the rack builder syntax. Its totally compatible. Use any rack middleware with Rails now (on edge ;)

again metal != middleware

@drnic

This comment has been minimized.

Contributor

drnic replied Dec 16, 2008

/me found answer to his question in @josh’s comment above

@wycats

This comment has been minimized.

Member

wycats replied Dec 16, 2008

drnic I’m assuming that Rails::Rack::Metal will become more featured over time. At the moment there doesn’t seem to be a big win in using it over regular middleware (but again, I expect that might change).

@samgranieri

This comment has been minimized.

Contributor

samgranieri replied Dec 16, 2008

here’s a novel use for metal: You can use http://gist.github.com/36865 to rickroll every non page cached url. Use at your own risk!

@wycats

This comment has been minimized.

Member

wycats replied Dec 16, 2008

@lifo don’t you use config.middleware.use to set up the Metal classes?

@carllerche

This comment has been minimized.

Contributor

carllerche replied Dec 16, 2008

+1 for naming it middleware.

In the Rack code itself, Rack::File is obviously an endpoint, yet is called middleware. It’s called middleware because it is in the middle of the request → application chain, not because it is an end point or not.

Naming this metal instead of middleware is just going to muddy the waters for newer developers not familiar with rack.

@josh

This comment has been minimized.

Member

josh replied Dec 16, 2008

@wycats yes, under the hood the metal is middleware. we inject it into the middleware stack and thats how it intercepts the request. The focus is on the endpoint implementation. Metal bits should be application endpoints, not filters.

@ezmobius

This comment has been minimized.

ezmobius replied Dec 16, 2008

middlewares do not have to be filters. they can absolutely be endpoints and that is the point. Making a new concept just makes learning rack harder no?

Same concept i blogged ages ago here: brainspl.at/articles/2008/02/16/so-merb-core-is-built-on-rack-you-say-why-should-i-care

I love that you guys are adding this stuff. But I implore you to not embrace and extend but rather just embrace rack and make people learn it. This is just middleware in sheep’s clothing afaict.

@josh

This comment has been minimized.

Member

josh replied Dec 17, 2008

@ezmobius The original idea was to have a single endpoint class, and force people use Rack::URLMap. But we didn’t like the idea of forcing people to use that for routing. So we moved the routing logic into the class (which is causing all this confusing). It may be clearer if it looked something like this.

class Poller
def self.call(env)
[200, ….]
end
end

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

def call(env) if env[…] == /something/ Poller.call(env) else @app.call(env) end end

end

However, we decided to merge the two for a nicer API. Okay, since Rack::Metal secretly takes an app instance under the hood, is not an endpoint but middleware. But thats not how we want to think of it.

I know still confusing.

@Roman2K

This comment has been minimized.

Roman2K replied Dec 17, 2008

+1 for “middleware”, or some other name that’s more descriptive than “metal”.

@wycats

This comment has been minimized.

Member

wycats replied Dec 17, 2008

@lifo is there an important reason for people to think of it differently? might keeping the (tiny) implementation exposed help people understand what’s going on?

@josh

This comment has been minimized.

Member

josh replied Dec 17, 2008

@wycats possibly, maybe that would clear up this mess :)

(btw, I’m not lifo)

@wycats

This comment has been minimized.

Member

wycats replied Dec 17, 2008

@josh oh snap. /me holds head

My typing fingers have betrayed me!

@josh

This comment has been minimized.

Member

josh replied Dec 17, 2008

BTW, all good comments. If someone wants to propose a change, maybe we should take this to the ML or something. I’m getting lost :)

@macournoyer

This comment has been minimized.

macournoyer replied Dec 17, 2008

+1 for naming it Awesomeware

@peterc

This comment has been minimized.

Contributor

peterc replied Dec 17, 2008

I think metal is an awesome name and reasonably descriptive. That said, middleware is probably more descriptive but doesn’t it clash nominatively with the generic concept of middleware in Rack?

@wycats

This comment has been minimized.

Member

wycats replied Dec 17, 2008

@peterc is is middleware in Rack

@actsasflinn

This comment has been minimized.

actsasflinn replied Dec 17, 2008

+1 for middleware

@lgomez

This comment has been minimized.

lgomez replied Dec 17, 2008

Pardon my ignorance but this means responses to requests intercepted by metal have to be completely generated manually or at least not with actionpack niceties, correct? Or is it just actioncontroller the one that is not loaded?

Where can I learn more about rack, metal and related subjects?

@mattetti

This comment has been minimized.

Contributor

mattetti replied Dec 17, 2008

@Igomez http://rack.rubyforge.org/ and http://book.merbist.com/getting-started/request-path

Rails Edge now uses Rack like merb/sinatra/waves/ramaze/mack so the process is the same.
The Rack middleware receives a rack env and returns a simple tuple containing status, headers, body.
The middleware has access to your models.

@lifo

This comment has been minimized.

Member

lifo replied Dec 17, 2008

@lgomez : Have a look at two posts I had made recently
- http://m.onkey.org/2008/11/17/ruby-on-rack-1
- http://m.onkey.org/2008/11/18/ruby-on-rack-2-rack-builder

Think of Metal as a minimal Rack application.

HTH.

@mattetti

This comment has been minimized.

Contributor

mattetti replied Dec 17, 2008

@lifo great posts!

@josh

This comment has been minimized.

Member

josh replied Dec 17, 2008

Maybe this will help. Metals are “application-specific” mini apps. Not really designed to be shared. However, you want want to create a plugin, engine, etc you can wrap it in a boarder middleware wrapper that would work on any Rack platform.

@softprops

This comment has been minimized.

softprops replied Dec 17, 2008

neat!

@datra

This comment has been minimized.

datra replied Dec 17, 2008

I think looks pretty awesome. Excited about the future! I must admit that it took me a while to get the idea, though.

Metal is a cool name but to me it makes no sence. I don’t really like middleware either. I would prefer a plural noun like models, views, helpers, and controllers. I don’t know, maybe spikes. :)

@technoweenie

This comment has been minimized.

Contributor

technoweenie replied Dec 17, 2008

Horns \m/

@henrik

This comment has been minimized.

Contributor

henrik replied Dec 17, 2008

app/wares? ;)

@josh

This comment has been minimized.

Member

josh replied Dec 17, 2008

Tweaked the API:

http://github.com/rails/rails/commit/61a41154f7d50099da371e0d2f22fd25ab9113c2

Metals are now pure endpoints. Not fake middleware, nor any other type of middleware. They are applications, they do not accept a app in their initializer. Did I mention they were not middleware?

@stefanoc

This comment has been minimized.

stefanoc replied Dec 17, 2008

+∞ for metal, but I like “awesomeware” too :-)
I find it really hard to believe that ppl don’t get the difference between “metal” and “middleware”. I don’t think it’s confusing at all. I mean, just look at the code, it’s all there!

@josh: just keep pulling great stuff like this

@lifo

This comment has been minimized.

Member

lifo replied Dec 17, 2008

@mattetti Thanks!

@creationmachine

This comment has been minimized.

creationmachine replied Dec 18, 2008

This will really help me out in some stuff I am working on. Thanks!

As far as naming it “Metal”, um, there are 1 billion gems with different names. I think people new to Rails will understand the subject matter after it’s mentioned once or twice. But, maybe it should be named “Derailer” or “Sidetrack”. :D

Thanks again for all your work!

@balachandranl

This comment has been minimized.

balachandranl replied Feb 1, 2010

Hi,

Is there any possibilities to do rack and metal in rails 2.2 version ?

Please any one help me ASAP.

Regards,
Bala

@jnicklas

This comment has been minimized.

Contributor

jnicklas replied Feb 1, 2010

Bala,

you probably chose the single worst place of all to ask that question. May I suggest the rails-talk mailing list or Stack Overflow as alternatives.

/Jonas

Please sign in to comment.