Markaby is a Ruby library that lets you write HTML in Ruby. It's built into the Camping framework as the templating engine. To create elements, you call a method of that name.

# In Camping, you don't have to set up the Builder.
# I'll leave it out in the other examples.
mab = Markaby::Builder.new
mab.p "This is a paragraph."
mab.div "I can do the same thing with a div."

To nest your HTML, you can pass a block.

form :method => 'post' do
legend 'Import stuff'
textarea :name => 'queries', :rows => '25', :cols => '100'
input :type => 'submit', :value => 'Import!'
end

[A side note, the textarea method inserts some whitespace by default. Very annoying, so if you know how to get rid of it in a non-hacky way, please let me know.]

It uses lots of metaprogramming trickery to make your life a whole lot easier. For example, you can add an arbitrary class to an element by calling the class name as a method.

div.example "This div will be of class example."

The class methods can also be chained.

div.example.special "This div will be of class example _and_ special."

You can do the same thing with an id by simply appending a bang (!) to the class name.

div.main_content! "This div will have the id main_content."

But what if you want a variable class name? I can't just tack the variable name on because the magic will mean that the class will be the name of the variable, not the value.

%w[one two three].each {|c| puts mab.p.c.to_s}

That's not going to work. It will output this.

<p class="c"/>
<p class="c"/>
<p class="c"/>

Ruby's send method to the rescue.

%w[one two three].each {|c| puts mab.p.send(c)}

This correctly outputs this.

<p class="one"/>
<p class="two"/>
<p class="three"/>

In one of the experiments I'm running for my research, I have to take a snapshot of a page and serve it locally. Of course, if I just grab the HTML, any relative URLs will break and the locally served page is unlikely to look much like the original. So, I put together a bit of code to make the links absolute. I remember trying to do this a few years ago in Python and having enormous headaches, but this Ruby version was relatively painless. That says more about my skills as a coder than anything about the relative (get ...
[read more]
Turns out I don't go through the most rigorous process to decide what technology to use. I've been using Camping for quite a while now, on a couple of small internal projects. I know there are other lightweight Ruby frameworks out there, such as Sinatra and Ramaze. So, why did I choose Camping? Why? Simply because Camping seems more interesting, and things should be fun. And maybe that question is part of the answer, _why.
I've been working on Habari a bit recently, so have been writing more PHP than Ruby lately. Given that Habari's code quality is pretty good, that hasn't been too terrible. However, I recently had to prepend a URL to a bunch of file paths, and was thinking in Ruby, where I would have done something like this: url = "http://twofishcreative.com/" resources = %w[one two three] resources.map! { |resource| url + resource } puts resources The beauty of this is that it keeps the callback code with the calling code, making it easier to read. PHP doesn't have blocks, but it ...
[read more]
I've written here and here about serving static files with Camping, but each time I've set up a new route for each resource type. You can serve all static files with a single route by following the instructions on the wiki. Here's the code that's presented there. module Camping::Controllers class Static < R '/static/(.+)' TYPES = {'.css' => 'text/css', '.js' => 'text/javascript', '.jpg' => 'image/jpeg'} PATH = File.expand_path(File.dirname(__FILE__)) def get(path) @headers['Content-Type'] = TYPES[path[/\.\w+$/, 0]] || "text/plain" unless path.include? ".." # prevent directory traversal attacks @headers['X-Sendfile'] = "#{PATH}/static/#{path}" else @status = "403" "403 - Invalid path" end end end ...
[read more]
Camping is great, yeah. JQuery too. Maybe you want to use them together? Here's how. First, you need to set up a route for your JQuery. I've talked about sending static files with Camping before, so this is just a modification of that. [You can serve static files more sensibly than setting up a route for each type. Maybe one day I'll write about that.] In your controller: module MyApp::Controllers class Index < R '/' def get render :index end end class JQuery < R '/resources/jquery.js' def get current_dir = File.expand_path(File.dirname(__FILE__)) @headers['Content-Type'] = "text/javascript" @headers['X-Sendfile'] = "#{current_dir}/resources/jquery.js" end end end ...
[read more]
Camping interprets every request to the server using the paths in the controllers, so if you want to serve static files, like external stylesheets or images, you need to provide a controller for them. Take this bit of Markaby: img :src => 'images/logo.png' This will get interpreted by Camping and end up with this HTML: <img src='images/logo.png' /> When the browser reads that, it will send a GET request to the Camping server looking for logo.png, that Camping will try to match against the controller paths. If you haven't set one up, you'll get an error. So, in your ...
[read more]
The title says it all really. I'm using hpricot for the first time, and the page I'm scraping has quaint uppercase HTML elements, shouting its TABLEs at me. It took me quite a while to work out that I didn't have to shout at hpricot.
When working with Ruby's Find, don't change directories with FileUtils.cd, as Find gets confused. You're better off working with full paths (use File.expand_path).
If you want to pass information to a Camping controller, all you have to do is define the class like this: class Index < R '/(\d+)' get(id) Now whatever was matched by the regex \d+ can be retrieved by the variable id. If you want to pass more than one variable, just chain them like this: class Index < R '/(\d+)/(\w+)' get(id, action) Each of your regex groups will be passed as a variable. If you forget to add the arguments to the get method, you'll get an error like this:

Camping Problem!

MyApp::Controllers::Index.GET

ArgumentError wrong number of arguments ...

[read more]