Skip to content

Commit

Permalink
Replaced the Controller with a piece of Metal:
Browse files Browse the repository at this point in the history
* removed controller
* removed routing
* added a Metal/Rack middleware to handle the requests instead
* updated README

Many thanks to Daniel Neighman (hassox) for his help converting this to Rack at RailsCamp in Melbourne!
  • Loading branch information
justinfrench committed Nov 22, 2009
1 parent dbdae08 commit fdd7343
Show file tree
Hide file tree
Showing 11 changed files with 202 additions and 147 deletions.
9 changes: 4 additions & 5 deletions README.rdoc
Expand Up @@ -25,12 +25,12 @@ PluginAssets takes these "view paths" and builds the same thing for "public path


Given a request for "/stylesheets/foo/bah.css", the web server would first look for "bah.css" in "RAILS_ROOT/public/foo/bah.css". If that resource doesn't exist, the request would usually be passed through to Rails, which will try to route the request to a controller action and eventually respond with a 404 (file not found). Given a request for "/stylesheets/foo/bah.css", the web server would first look for "bah.css" in "RAILS_ROOT/public/foo/bah.css". If that resource doesn't exist, the request would usually be passed through to Rails, which will try to route the request to a controller action and eventually respond with a 404 (file not found).


Instead, this plugin sets up a controller and appropriate routing to handle the request, searching for the file in your plugins and gem plugins. In this case, it would search for "bah.css" in the following locations: Instead, this plugin intercepts the requests for these missing assets with a piece of middleware, searching through the installed plugins. In this case, it would search for "bah.css" in the following locations:


* RAILS_ROOT/vendor/plugins/plugin_123/public/stylesheets/foo/bah.css * RAILS_ROOT/vendor/plugins/plugin_123/public/stylesheets/foo/bah.css
* RAILS_ROOT/vendor/plugins/plugin_456/public/stylesheets/foo/bah.css * RAILS_ROOT/vendor/plugins/plugin_456/public/stylesheets/foo/bah.css


As soon as a matching file is found, it's passed back to the browser with the correct mime type. If none of the plugins have such a file, the regular Rails 404 response for a missing template is invoked. As soon as a matching file is found, it's passed back to the browser with the correct mime type. If none of the plugins have such a file, the request is passed onto Rails, which will handle the 404 like it usually does.


It works for plugins and gem plugins. It works for the following URLs: It works for plugins and gem plugins. It works for the following URLs:


Expand All @@ -43,7 +43,7 @@ One day I'll make it configurable for other assets, but in the meantime it's eas


==Caveats ==Caveats


PluginAssets leverages the view_paths array used by Rails plugins that supply their own views. Rails plugins and gems are only added to the view_paths array automatically if there's an app/views directory inside the plugin. If you're supplying public assets, chances are pretty good you're supplying views too, so that probably isn't a big deal. Publicious leverages the view_paths array used by Rails plugins that supply their own views. Rails plugins and gems are only added to the view_paths array automatically if there's an app/views directory inside the plugin. If you're supplying public assets, chances are pretty good you're supplying views too, so that probably isn't a big deal.




==Isn't it inefficient? ==Isn't it inefficient?
Expand All @@ -58,11 +58,10 @@ I wrote this on the train this morning as a proof of concept, so there's a long
* It should be a gem plugin, so that other gem plugins can list it as a dependency * It should be a gem plugin, so that other gem plugins can list it as a dependency
* I haven't written any tests * I haven't written any tests
* I haven't tackled caching yet (probably need to do both page caching and header response caching) * I haven't tackled caching yet (probably need to do both page caching and header response caching)
* Thinking this could be Rack middleware or Rails Metal rather than a full Rails request stack.


I actually want to see this in Rails core, so help me make it awesome! I actually want to see this in Rails core, so help me make it awesome!




==Etc ==Etc


Publicious is Copyright (c) 2009 Justin French, released under the MIT license. Your feedback, forkings and contributions are greatly welcomed. Publicious is Copyright (c) 2009 Justin French, released under the MIT license. Your feedback, forkings and contributions are greatly welcomed. Many thanks to Daniel Neighman (hassox) for his help converting Publicious to Rack middleware rather than a regular Rails routing and controller stack.
44 changes: 0 additions & 44 deletions app/controllers/publicious_controller.rb

This file was deleted.

49 changes: 49 additions & 0 deletions app/metal/publicious_metal.rb
@@ -0,0 +1,49 @@
class PubliciousMetal

def self.call(env)
request = Rack::Request.new(env)
if request.path_info =~ %r{^\/(#{allowed_dirs.join("|")})}
file_name = nil
path = nil

public_paths.detect do |pub_path|
path = pub_path
fp = File.join(pub_path, request.path_info)
file_name = fp if File.file?(fp)
end

return respond_not_found! unless file_name

# Make sure pricks aren't ../../config/database.yml ing us
respond_not_found! unless file_name.gsub(%r[^#{path}], "") == request.path_info

Rack::Response.new(
File.open(file_name),
200,'Content-Type' => content_type_for_file(file_name)
).finish
else
respond_not_found!
end
end

def self.respond_not_found!
Rack::Response.new("Not Found", 404).finish
end

def self.allowed_dirs
%w(stylesheets javascripts images)
end

def self.content_type_for_file(name)
file_name = File.basename(name).split(".").last.to_s
Mime::Type.lookup_by_extension(file_name).to_s
end

def self.public_paths
ActionController::Base.view_paths.map do |vp|
full_path = File.expand_path(vp.to_s, RAILS_ROOT)
full_path.sub("app/views", "public") if full_path =~ /vendor/
end.compact
end

end
7 changes: 0 additions & 7 deletions config/routes.rb

This file was deleted.

3 changes: 3 additions & 0 deletions rails/init.rb
@@ -0,0 +1,3 @@
Mime::Type.register "image/jpeg", :jpg
Mime::Type.register "image/gif", :gif
Mime::Type.register "image/png", :png
54 changes: 54 additions & 0 deletions test/multiple_match_test.rb
@@ -0,0 +1,54 @@
require File.dirname(__FILE__) + '/test_helper'

class MultipleMatchesTest < Test::Unit::TestCase

include TestHelper

def setup
super

setup_plugin :no_file_here
setup_plugin :my_plugin
setup_plugin :your_plugin

@path = 'foo/bah.css'

@filename_1 = "/tmp/vendor/my_plugin/public/stylesheets/#{@path}"
@filecontents_1 = "hello from my stylesheet"
FileUtils.mkdir(File.dirname(@filename_1))
File.open(@filename_1, 'w') do |file|
file << @filecontents_1
end

@filename_2 = "/tmp/vendor/your_plugin/public/stylesheets/#{@path}"
@filecontents_2 = "hello from your stylesheet"
FileUtils.mkdir(File.dirname(@filename_2))
File.open(@filename_2, 'w') do |file|
file << @filecontents_2
end

get "/stylesheets/#{@path}"
end

def test_should_respond_with_success
assert last_response.ok?
end

def test_view_paths_should_contain_three_items
assert_equal 4, ActionController::Base.view_paths.size
end

def test_public_paths_should_contain_two_items
assert_equal 3, app.public_paths.size # one less that view_paths
end

def test_public_paths_should_contain_the_plugins_public_dir
assert_equal "/tmp/vendor/my_plugin/public", app.public_paths.second
end

def test_should_respond_with_first_file_contents
assert_equal File.read(@filename_1), last_response.body
assert_equal @filecontents_1, last_response.body
end

end
12 changes: 12 additions & 0 deletions test/no_match_test.rb
@@ -0,0 +1,12 @@
require File.join(File.dirname(__FILE__), "test_helper")

class NoMatchTest < Test::Unit::TestCase

include TestHelper

def test_redirect_logged_in_users_to_dashboard
get "/stylesheets/not_here.css"
assert last_response.not_found?
end

end
12 changes: 0 additions & 12 deletions test/no_matches_test.rb

This file was deleted.

52 changes: 0 additions & 52 deletions test/publicious_base_test.rb

This file was deleted.

39 changes: 23 additions & 16 deletions test/single_match_test.rb
@@ -1,37 +1,44 @@
require File.dirname(__FILE__) + '/test_helper' require File.join(File.dirname(__FILE__), "test_helper")

class SingleMatchTest < PubliciousBaseTest


class SingleMatchTest < Test::Unit::TestCase

include TestHelper

def setup def setup
super super

setup_plugin :my_plugin setup_plugin :my_plugin


@path = ['foo', 'bah.css'] @path = 'foo/bah.css'
@filename = "/tmp/vendor/my_plugin/public/stylesheets/#{@path.join('/')}" @filename = "/tmp/vendor/my_plugin/public/stylesheets/#{@path}"
@filecontents = "hello from stylesheet" @filecontents = "hello from stylesheet"
FileUtils.mkdir(File.dirname(@filename)) FileUtils.mkdir(File.dirname(@filename))
File.open(@filename, 'w') do |file| File.open(@filename, 'w') do |file|
file << "hello from stylesheet" file << "hello from stylesheet"
end end


get :show, :path => @path get "/stylesheets/#{@path}"
end end


test "view_paths should contain two items" do def test_should_respond_with_success
assert_equal 2, @controller.view_paths.size assert last_response.ok?
end end


test "public_paths should contain one item (one less than view paths)" do def test_view_paths_should_contain_three_items
assert_equal 1, @controller.send(:public_paths).size assert_equal 2, ActionController::Base.view_paths.size
end end


test "public_paths should contain the plugin's public dir" do def test_public_paths_should_contain_two_items
assert_equal "/tmp/vendor/my_plugin/public", @controller.send(:public_paths).first assert_equal 1, app.public_paths.size # one less that view_paths
end

def test_public_paths_should_contain_the_plugins_public_dir
assert_equal "/tmp/vendor/my_plugin/public", app.public_paths.first
end end


test "should respond with the file contents" do def test_should_respond_with_first_file_contents
assert_equal File.read(@filename), @controller.response.body assert_equal File.read(@filename), last_response.body
assert_equal @filecontents, @controller.response.body assert_equal @filecontents, last_response.body
end end


end end

0 comments on commit fdd7343

Please sign in to comment.