Just a small Sinatra app to show xkcd comics with the mouseover text visible at the bottom of the page. xkcd.heroku.com does the same thing but I find their images are too small sometimes.
-
-
Save BinaryMuse/654553 to your computer and use it in GitHub Desktop.
sinatra | |
haml |
.rvmrc |
$: << '.' | |
require 'xkcd' | |
run Sinatra::Application |
require 'net/http' | |
require 'uri' | |
require 'sinatra' | |
require 'json' | |
require 'haml' | |
# Blank route for the favicon to keep errors low | |
get '/favicon.ico' do | |
nil | |
end | |
# Random gets a random comic | |
get '/random' do | |
current = symbolize_keys(JSON::parse(get_comic_json(:current))) | |
current = current[:num] | |
random = rand(current) + 1 | |
# Make sure we don't hit 404, which is XKCD's actual 404 page. | |
while random == 404 | |
random = rand(current) + 1 | |
end | |
redirect "/#{random}" | |
end | |
# Index gets the current comic | |
get '/' do | |
return do_comic(:current) | |
end | |
get '/:number' do |number| | |
return do_comic(number) | |
end | |
def do_comic(number) | |
begin | |
# Get the JSON data | |
current = symbolize_keys(JSON::parse(get_comic_json(:current))) | |
@data = symbolize_keys(JSON::parse(get_comic_json(number))) | |
rescue | |
@data = { :safe_title => "Not Found" } | |
return haml :error | |
end | |
# Turn the "year", "month", and "day" properties into numbers | |
[:year, :month, :day].each { |key| @data[key] = @data[key].to_i } | |
# Calculate the "previous" and "next" comics, if any | |
@data[:previous] = @data[:num] - 1 unless @data[:num] == 1 || @data[:num].nil? | |
unless current[:num] == @data[:num] | |
@data[:next] = @data[:num] + 1 unless @data[:num].nil? | |
end | |
# Make sure we don't hit 404, which is XKCD's actual 404 page. | |
@data[:previous] -= 1 if @data[:previous] == 404 | |
@data[:next] += 1 if @data[:next] == 404 | |
# Render the page | |
haml :comic | |
end | |
# If the comic number is 0, get today's comic | |
# Else get the given comic | |
def get_comic_json(number) | |
if number == :current | |
Net::HTTP.get(URI.parse("http://xkcd.com/info.0.json")) | |
else | |
Net::HTTP.get(URI.parse("http://xkcd.com/#{number}/info.0.json")) | |
end | |
end | |
# change string keys into symbols | |
def symbolize_keys(arg) | |
case arg | |
when Array | |
arg.map { |elem| symbolize_keys elem } | |
when Hash | |
Hash[ | |
arg.map { |key, value| | |
k = key.is_a?(String) ? key.to_sym : key | |
v = symbolize_keys value | |
[k,v] | |
}] | |
else | |
arg | |
end | |
end | |
__END__ | |
@@ layout | |
%html | |
%head | |
%title xkcd mini: #{@data[:safe_title]} | |
%body | |
= yield | |
@@ error | |
%h1 Not Found | |
%p | |
That comic was not found. | |
%p | |
%a{:href => "/"}Home | |
@@ comic | |
%h1= @data[:safe_title] | |
%p | |
- unless @data[:previous].nil? | |
%a{:href => "/1"}< | |
<< First | |
| | |
%a{:href => "/#{@data[:previous]}"}< | |
< Previous | |
- else | |
<< First | |
| | |
< Previous | |
| | |
%a{:href => "/random"}< | |
Random | |
| | |
- unless @data[:next].nil? | |
%a{:href => "/#{@data[:next]}"}< | |
Next > | |
| | |
%a{:href => "/"}< | |
Last >> | |
- else | |
Next > | |
| | |
Last >> | |
%p | |
%img{:src => @data[:img]} | |
%p= @data[:alt] | |
%hr | |
%p | |
Read this comic on | |
%a{:href => "http://xkcd.com/#{@data[:num]}"}xkcd.com |
Magic for Sinatra, actually. Looks like Sinatra doesn't even depend on the DATA stream; it reads the file and then splits on __END__
. https://github.com/sinatra/sinatra/blob/aa06ed/lib/sinatra/base.rb#L900-923
So I'm guessing sinatra defines the :comic symbol on line 39 when it parses the template and sees "@@ comic"? Sorry if my line number is off...there is a rendering bug in Firefox that causes the line number gutter to be vertically offset.
Yeah, it looks like it creates a hash, using symbols created from whatever's after @@
as the key and all the lines up to the next line that matches /^@@\s*(.*\S)\s*$/
as the value. The haml
call just wraps a call to render
and passes in :haml
as the rendering engine to use. https://github.com/sinatra/sinatra/blob/aa06ed/lib/sinatra/base.rb#L377-379
Cool!
Pretty slick! Ruby really is about minimal effort, isn't it? I had to look up the whole Sinatra thing...it's cool!
in the post END data there, I guess the "layout" and "comic" labels are magic for haml?