Simple Rails API for GeekTool and Status Board
Inspired by Brett Terpstra’s series of posts about GeekTool I’ve been using the iheartquotes API to display quotes on my desktop for quite some time now. So long, as a matter of fact, that I was starting to get bored by seeing the same quotes all the time. In comes this movie-trivia page I made a long time ago. It’s basically the first rails app I ever wrote, so really nothing special, but since I’ve been posting those little titbits almost daily for over two years now, they amassed to quite a nice count. The ideal target for my Geeklet.
I pretty much modelled the API for my page after the iheartquotes API, but since I’m currently playing with Status Board I also added an API call for status board.
We’re starting by adding the needed routes and controller actions:
MTotD::Application.routes.draw do
get 'api/random', to: 'trivia#random', defaults: { :format => 'text' }
get 'api/status_board', to: 'trivia#status_board'
end
class TriviaController < ApplicationController
def random
@trivia = Trivia.published.order("random()")
if params[:min_characters]
@trivia = @trivia.where("length(body) > ?", params[:min_characters])
end
if params[:max_characters]
@trivia = @trivia.where("? > length(body)", params[:max_characters])
end
if params[:min_lines]
@trivia = @trivia.select { |trivium| word_wrap(trivium.body, :line_width => 70).lines.count + 1 >= params[:min_lines].to_i }
end
if params[:max_lines]
@trivia = @trivia.select { |trivium| word_wrap(trivium.body, :line_width => 70).lines.count + 1 <= params[:max_lines].to_i }
end
@trivia = @trivia.first
respond_to do |format|
format.text
format.html { render layout: false }
end
end
def status_board
render layout: false
end
end
With the random
action we are giving the user a few options to filter the trivia and are then loading a random one of the remaining. For the min_lines and max_lines filter we’re calling word_wrap
on our text, which inserts a line break every 70 characters.1 This is to ensure that every line has about the same length. The action defaults to text format because it’s more useful for GeekTool, but can also be called as HTML, which we’ll use for the Status Board API call. Furthermore we have to make sure that our two HTML views won’t render the application layout, if we want them to look shiny in Status Board.
Now we create our random.text.erb
view:
<% if @trivia.present? %>
<%= raw word_wrap(@trivia.body.gsub(/\r\n\r\n/, "\r\n").gsub(/[*,_]/, ''), :line_width => 70) %>
— <%= raw @trivia.film.title %>
<% end %>
First we’re making sure we’re not calling the attributes of a nil object,2 then we just word_wrap the trivia text as mentioned above and add a line with the film title. I’m also removing empty lines and some Markdown markup.
The random.html.erb
view looks pretty similar:
<% if @trivia.present? %>
<div id="trivia">
<%= markdown(@trivia.body + " <span>— " + @trivia.film.title + "</span>") %>
</div>
<% end %>
The only difference here is, that I don’t word_wrap the trivia, but instead call my markdown
helper method to parse it to HTML.
All that’s left is the status_board.html.erb
view. Since Status Board won’t refresh our side automatically, we’ll have to handle this ourselves:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Cache-control" content="no-cache" />
<title>Trivia</title>
<style type="text/css">
...
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
function refresh() {
var req = new XMLHttpRequest();
req.onreadystatechange=function() {
if (req.readyState==4 && req.status==200) {
document.getElementById('trivia-container').innerHTML = req.responseText;
}
}
req.open("GET", 'http://movietriviaoftheday.com/api/random.html?max_lines=6', true);
req.send(null);
}
function init() {
refresh()
var int=self.setInterval(function(){refresh()},600000);
}
</script>
</head>
<body onload="init()">
<div id="main">
<div id="trivia-container"></div>
</div>
</body>
</html>
So rather than loading a view with trivia in it, we load an empty page, replace the #trivia-container
with the contents of our random.html.erb
view via JavaScript and refresh them every 10 minutes.3
And that’s our simple API.
If you want to try a Geeklet with random trivia from my page just create a new shell Geeklet and use the following one-line command:
$ curl -s 'http://movietriviaoftheday.com/api/random?max_lines=6'
Adjust the font and colour to your liking and the result could look like this:
On you Status Board you just need to create a new Do-It-Yourself panel and point it at:
http://movietriviaoftheday.com/api/status_board
You can also check out the API Documentation.