Creating a Rack Middleware for Minifying Your Javascript files
If you are in the Ruby world you have probably heard of Rack. If you haven’t, and you are a Pythonista, then you only need to know that Rack is a sort of “WSGI for Ruby”. And in case you don’t know what Rack or WSGI are, then here’s a brief description:
“Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.”
So you can think of Rack as a “frameworks framework“. Merb and other ruby frameworks already support Rack, which means that they can share the middleware described above.
Creating a Rack Middleware
So let’s say you want your application (built on Rails, Merb, Ramaze or even Rack) to intercept any request for javascript files so that you can first minify them. Below is the sample code for achieving this with a Rack Middleware:
(You can checkout this code, its spec and example from http://github.com/lucianopanaro/rack-javascript-minifier. The javascript minifier is a port from Douglas Crockford’s library done by Ryan Grove.)
class JavascriptMinifier
F = ::File
def initialize(app, path)
@app = app
@root = F.expand_path(path)
raise "Provided path #{@root} does not exist" unless F.directory?(@root)
end
def call(env)
path = F.join(@root, Utils.unescape(env["PATH_INFO"]))
unless path.match(/.*\/(\w+\.js)$/) and F.file?(path)
return @app.call(env)
end
if env["PATH_INFO"].include?("..") or !F.readable?(path)
body = "Forbidden\n"
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]]
end
last_modified = F.mtime(path)
min_path = F.join(@root, "m_#{last_modified.to_i}_#{F.basename(path)}")
unless F.file?(min_path)
F.open(path, "r") { |file|
F.open(min_path, "w") { |f| f.puts JSMin.minify(file) }
}
end
[200, {
"Last-Modified" => F.mtime(min_path).httpdate,
"Content-Type" => "text/javascript",
"Content-Length" => F.size(min_path).to_s
}, F.new(min_path, "r")]
end
end
Rack Middleware’s interface is dead simple. As Marc-AndrĂ© Cournoyer points out: “It must have a call method and receive a Rack app as the first argument of new”.
So here, when we initialize the JavascriptMinifier, we pass the root path where the javascript files are placed. Every time it gets called, the middleware first checks if the request is for a javascript file. If it’s not, it passes it to the application. If the request is for a javascript in a parent directory, then we return a forbidden (403) page. Finally, if the request passes these filters, the middleware creates a minified version of the javascript file and returns it.
Running your application with middleware
This is also really simple using Rack::Builder:
“Rack::Builder implements a small DSL to iteratively construct Rack applications. use adds a middleware to the stack, run dispatches to an application.”
So here is an example of a rackup file that you can use to startup your application with some middleware:
use Rack::CommonLogger use Rack::ShowExceptions use JavascriptMinifier, "./" run Rack::Lobster.new
Testing and Spec’ing helpers
Rack provides two helpers that will be of great use for testing your application: Rack::MockRequest and Rack::MockResponse, so you have no excuses to test your Rack Middleware and do some TDD/BDD!
Where to go from here
To learn how to develop your own Rack Middleware, your best resource will be Rack sources. You will learn a lot of Rack (and Ruby in general) by reading the code and specs they provide: URLMap, CommonLogger, File. There also is a group where you can ask your questions too.
So enjoy your trip with Rack Middleware and feel free to comment your thoughts and questions!
Creating a news carousel with jQuery
Last week I had to do a news carousel for a project I’m developing. It had been a while since I had the chance to do something interesting with jQuery, so I wanted to share the experience of how easily you can build similar widgets for your site.
So first let’s take a look at what we want to build.
Now, I know that there are a few plugins out there for jQuery that probably can do this, but the point of this post is to show how simple it is to create something like this with a few lines of jQuery and CSS.
Let’s begin by defining how we will organize the content. Being a list of news, we can either use an ordered or an unordered list.
<div id="news_carousel">
<ul class="news">
<li>
<img src="" alt="" />
<strong><a href="#">Title</a></strong>
<span>Description</span>
</li>
</ul>
</div>
Now that we have our content, we have to style it. The keys here are to:
- Align the list elements one next to the other.
- Make #news_carousel just show one list element at a time
- Use relative and absolute positioning to show the titles and descriptions over each image
Here’s the CSS used in the sample with some comments:
#news_carousel {
width: 444px;
height: 333px;
margin: 0;
padding: 0;
overflow: hidden; /* this will make only show 1 li */
position: relative;
}
#news_carousel ul.news {
list-style-type: none;
margin: 0;
padding: 0;
position: relative;
}
#news_carousel ul li {
margin: 0;
padding: 0;
position: relative; /* so that we can do absolute positioning of the paragraph inside of it */
float: left; /* align one next to the other */
}
#news_carousel ul.news li p {
position: absolute;
bottom: 10px;
left: 0;
margin: 5px;
}
#news_carousel ul.news li p strong {
display: block;
padding: 5px;
margin: 0;
font-size: 20px;
background: #444;
}
#news_carousel ul.news li p span {
padding: 2px 5px;
color: #000;
background: #fff;
}
#news_carousel ul.controls {
position: absolute;
top: 0px; right: 20px;
list-style-type: none;
}
#news_carousel ul.controls li a {
float: left;
font-size: 15px;
margin: 5px;
padding: 2px 7px;
background: #000;
text-decoration: none;
outline: none;
}
#news_carousel ul.controls li a.active {
border: 2px solid #ccc;
}
The Javascript code is pretty self-explanatory:
var news_carousel = function() {
var items_size = $('#news_carousel ul li').length;
if (items_size == 0) return;
// Calculate the total width and set that value to the ul.news width
// Store each item width
var width = 0;
var widths = [];
$('#news_carousel ul.news li img').each(function(i, e) {
widths[i] = $(e).width();
width += widths[i];
});
$("#news_carousel ul.news").width(width);
// Append the controls
controls = '<ul class="controls"><li><a class="active" href="#">1</a>';
for ( var i = 2; i <= items_size; i++) {
controls += '</li><li><a href="#">' + i + '</a></li>';
}
controls += '</ul>';
$('#news_carousel').append(controls);
$('#news_carousel ul.controls li a').click(function(event) {
// if the ul is already moving, then do nothing
if ($("#news_carousel ul.news:animated").length > 0) return false;
var clicked_item = $(event.target);
var current_active = $("#news_carousel ul.controls li a.active");
var current_index = parseInt(current_active.text());
var new_index = parseInt(clicked_item.text());
var move = new_index - current_index; // get how many items it should be moved
if (move != 0) {
direction = (move > 0)? "-=": "+=";
$('#news_carousel ul.news').animate({marginLeft: direction + widths[new_index-1] }, 300);
clicked_item.addClass("active");
current_active.removeClass("active");
}
return false;
});
}();
And that’s it! Around 100 lines of code and you have your own home-made news carousel. Hope you found it useful!
(Pictures taken from: http://www.flickr.com/photos/christing/268490607/ and http://www.flickr.com/photos/11717181@N02/1170861540/.)
RSpec Link Fest!
Now that I am developing a few new ruby and ror projects I wanted to begin with BDD and RSpec. There are lots of content all around the web so here’s a quick list of a few links that you might find useful if you are in your baby steps with RSpec (as I am
.
RSpec Story Runner is being replaced by Cucumber for RSpec 1.1.5, but as mentioned here, if you are about to start you should go with Cucumber. Here are the instructions for installing RoR support.
- RSpec’s site (there are a few examples for Rails controllers, models, views and helpers)
- RSpec on Github
- Cucumber
- Webrat
- David Chelimsky’s (RSpec’s creator) blog
- Andrzej Krzywda’s blog
- RSpec and Restful Authentication
- RSpec plain text story runner on a fresh rails app
- Story writing is fun
- Some RSpec useful links
- RSpec, it should behave like
- Understanding RSpec stories, a tutorial
- NetBeans: Screencast on RSpec support
Hope you find it useful!
2 comments