Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make generated Metal bits a pure rack endpoint application (not middl…
…eware) Instead of calling super to pass the request on, return a 404. The modified app looks like this: # app/metal/poller.rb class Poller def self.call(env) if env["PATH_INFO"] =~ /^\/poller/ [200, {"Content-Type" => "text/html"}, "Hello, World!"] else [404, {"Content-Type" => "text/html"}, "Not Found"] end end end But you aren't locked in to just Rails: # app/metal/api.rb require 'sinatra' Sinatra::Application.default_options.merge!(:run => false, :env => :production) Api = Sinatra.application unless defined? Api get '/interesting/new/ideas' do 'Hello Sinatra!' end
- Loading branch information
Showing
3 changed files
with
13 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,17 @@ | ||
module Rails | ||
module Rack | ||
class Metal | ||
def self.new(app) | ||
apps = Dir["#{Rails.root}/app/metal/*.rb"].map do |file| | ||
File.basename(file, '.rb').camelize.constantize | ||
end | ||
apps << app | ||
::Rack::Cascade.new(apps) | ||
end | ||
|
||
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 |
8 changes: 4 additions & 4 deletions
8
railties/lib/rails_generator/generators/components/metal/templates/metal.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +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) | ||
class <%= class_name %> | ||
def self.call(env) | ||
if env["PATH_INFO"] =~ /^\/<%= file_name %>/ | ||
[200, {"Content-Type" => "text/html"}, "Hello, World!"] | ||
[200, {"Content-Type" => "text/html"}, ["Hello, World!"]] | ||
else | ||
super | ||
[404, {"Content-Type" => "text/html"}, ["Not Found"]] | ||
end | ||
end | ||
end |
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious why the generator has been changed to return an array instead of a string now…?
BTW – that you can use Sinatra this easily is 100% awesome. Thank you.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@trevorturk Returning strings is being removed from the Rack spec since Ruby 1.9 doesn’t have String#each. Long story: http://groups.google.com/group/rack-devel/browse_thread/thread/e6132c3509438df
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this much better. Great work Josh!
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@josh Awesome, it’s easier to understand the distinction between Metal and Middleware now. Updated my blog post to match.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The distinction between metal and middleware became clearer, but IMHO the useage turned towards the ugly side.
Here’s how I’d like it to look:
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tjogin The initial Rails::Metal class hid this stuff from you. However the initial confusion made me decide to expose it. Nothing magic anymore, thats just a class.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@josh: Personally, I liked it hidden. :P
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah.
I also liked the way it was.
I think that the default 404 is going to cause the most confusion,
It makes me think “I dont want to return a 404!”,
and I’d probably delete it.
Saying that, a magical “super” is probably equally as brittle.
Anyway,
its good stuff.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about defining a constant
Metal::PassThru = [404, {"Content-Type" => "text/html"}, ["Not Found"]]
And then in the code you can just return Metal::PassThru and voila, you keep the logic plain and simple, and the “magic” 404 becomes a name which clearly states your intentions.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or maybe it’s clearer if PassThru is an exception and the code that adds each metal class on the call chain rescues PassThru and does a next on the iterator? (I didn’t look at the implementation, so maybe this won’t work)
But you get the idea.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@foca
NotFound? http://github.com/rails/rails/tree/61a41154f7d50099da371e0d2f22fd25ab9113c2/railties/lib/rails/rack/metal.rb#L12
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@foca +1
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if I really want to 404?
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One question: the Rack specification says that Rack-Applications are not allowed to be Classes. AFAIK, it doesn’t enforce this, but wouldn’t it be good to stick to the Spec?
I’m okay with using Cascade and 404.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@skade A valid rack application is anything that responds to “call”, there are no type requirements. Even if you wanted to restrict this to objects, in Ruby classes are objects ;)
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@josh http://rack.rubyforge.org/doc/files/SPEC.html
“A Rack application is an Ruby object (not a class) that responds to call”
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@josh: yes, ruby classes are objects. Thanks for the lesson… But they are special objects with subtle differences. There is a distinction, although classes behave like objects in almost every case. For example: classes are (usually) globally unique, objects (usually) not.
And that might just be the reason why the spec explicitly forbids classes.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
woot, this is gonna be huge +1
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@skade, pager: “object, not a class” means that Rack won’t try to instantiate anything for you. As Josh correctly points out, all it wants is an object that has a “call” method, and classes are objects too.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cypher, josh: this may be up to interpretation, but I don’t really want to push this.
On the other hand: if you skip Metals implementing #call on a class level and just instantiate them, you even save the need for self. The default construction for Objects is an empty one, so this is easy. I find it a much better interface, especially because of the symmetry to controller actions.
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@skade you can always just say
61a4115
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does anyone know of an actual workaround or patch so that I can return a 404 from my metal? The docs(http://guides.rubyonrails.org/rails_on_rack.html) say to use some other Rack middleware but I'm not really sure how I'm supposed to differentiate between the 404 my metal returns to say pass on to rails and the 404 I actually want to return to the user.