Skip to content

Wildfires in Boulder

Here’s the view out my kitchen window, most mornings:

Here’s what it looks like this fine evening:

From fire

Click the album link for more. From my porch, I can see parts of the mountainside flashing from the lights of the fire trucks. They’ve evacuated 11,000 homes, but the wind is blowing the fire the other direction. There have been clouds of smoke all afternoon, but once the sun set, I has able to see the flames.

More details about the fires here: http://www.dailycamera.com/news/2009/jan/07/i-70-closed-over-vail-pass-avalanche-control/

I boughted a car!

Mazda3 5-door. Its pretty:

Merbcamp

Here’s my own personal store of interesting crap I picked up in merbcamp. I’ll add to this as the talks go on.

  • carllerche is a pretty cool guy for adding useful stuff to the merb router for me
  • mauth is pretty sweet
  • When can I get a 4K tv in my living room?
  • FiveRuns tuneup for merb
  • webrat looks to make acceptance specs/stories actually useable
  • Ruby Language book by Matz is a good read for learning the internals of Ruby
Tagged ,

DataMapper 0.9.6 released

I just pushed 0.9.6 of dm-core, dm-more and data_objects up to rubyforge, as well as 0.9.8 of extlib. There’s several bugfixes that were applied in the runup to merbcamp. This is also preparing for the imminent release of merb 1.0RC1.

Tagged ,

HOWTO: Better JSON parsing when POSTing to Merb Apps

Where I work, we have fairly extensive, JSON-based web services in all out applications. As a quick example, here’s what you would get if you were to GET http://config.ssbe.example.com/configurations/90 with the mime-type application/vnd.absperf.sscj1+json:

{
  "_type":                      "Configuration",
  "href":                       "http://config.ssbe.localhost/configurations/90",
  "id":                         "4c5895f2-28a3-4299-a558-270889e6f065",
  "name":                       "lacquered",
  "notes":                      "Hosted hundredfold broomstick",
  "platform":                   "AIX",
  "client_href":                "http://core.ssbe.localhost/clients/jousting",
  "registered_templates_href":  "http://config.ssbe.localhost/configurations/90/registered_templates",
  "parent_configuration_href":  "http://config.ssbe.localhost/configurations/90",
  "created_at":                 "2008-10-07T16:38:29-06:00",
  "updated_at":                 "2008-10-08T15:20:51-06:00"
}

I’m planning on a bigger post about exactly what our JSON document means, and our mime-types, and everything. For now, a good explaination of the reasoning behind our mime-types can be found over on Peter’s blog.

That aside, now that I’ve GETed this document, I’d love to be able to just string-manipulate the one or two things I want to modify, and just PUT it back where I got it, in the same format, with all the same attributes. The problem with that, though, is that several of these attributes are determined server-side, such as _type, href, and id. These values a set by the server, and a few of them aren’t even properties on the model. I could throw an error back when someone tries to submit a value for an unchangeable attribute, but then I wouldn’t be able to POST the identical document that I just GETed. I’d have to know a fair amount about the document to know which attributes I have to remove from the document before I can give it back. I’d much prefer the server just ignore it. Now, I could throw an error if someone tries to change one of these attributes, but I’ll save that for later. In any event, right now, I just want my controller to parse the JSON, and let it ignore the attributes I don’t care about.

To that end, I implemented a custom JSON parser in a before filter in my Application controller:

class Application < Merb::Controller
  before :parse_supplied_sscj1, :if => :has_sscj1_content         #[1]

  def has_sscj1_content
    request.content_type == 'application/vnd.absperf.sscj1+json'  #[2]
  end

  def parse_supplied_sscj1
    begin 
      jobj = JSON.parse(request.raw_post)                         #[3]
      raise UnprocessableEntity unless jobj.is_a?(Hash)           #[4]

      model_class = jobj["_type"].snake_case                      #[5]

      params[model_class] = jobj
    rescue JSON::ParserError => e
      raise BadRequest.new(e.message)                             #[6]
    end
  end
end

A brief description of what all this means:

  1. Set up the before filter to do the parsing, but only under the right conditions.
  2. Those conditions are merely if somebody set the Content-Type header on the request to my sscj1 mime-type.
  3. JSON parse the body of the request. Request#raw_post is how you get to the raw data that was POSTed (and PUT, too)
  4. I expect every JSON document i get to be parsed into a Hash object, so throw a standard HTTP error if its not.
  5. Because I have the _type attribute in my document, I can use that to put the parsed attributes in the right place. From the example above, I end up with params = {"configuration" => {"name" => "lacquered", ...}, ...}
  6. Oh, and if we got an invalid (unparseable) JSON document, raise a 400 Bad Request error.

So that takes care of the JSON parsing. Its a little better than the one built-in to merb, because of the error handling, and putting the attributes into a useable place in the form. Now, what do we do about the attributes we want to ignore? I added a couple class methods to Controller for handling that.

class Application < Merb::Controller
  class << self
    attr_accessor :attributes_to_ignore

    def ignore_attributes(*attrs)
      @attributes_to_ignore = attrs
    end

  end

  def attributes_to_ignore
    %w[_type href id created_at updated_at] + self.class.attributes_to_ignore
  end

end

class Configurations < Application
  provides :sscj1

  ignore_attributes 'registered_templates_href'

  # ...
end

This is all pretty simple. Essentially, I just added a #ignore_attributes class method to my controllers, so I can provide a list of attributes to be ignored, specific to each controller. The #attributes_to_ignore method lists the default ones, and in this case, I want my configurations to ignore registered_templates_href in addition to those. Now I can just delete those from the parsed JSON object in my #parse_supplied_sscj1 method:

  attributes_to_ignore.each do |key|
    jobj.delete(key)
  end

Simple!

Now, I have that pesky parent_configuration_href attribute still coming in. I dont want to ignore it, but I do need a parent_id attribute in my configuration model, representing a self-referential join. To do that, I’d love to be able to run the given uri through merb’s router and parse out the id, but unfortunetly, thats not part of the public API (yet). I’ll just have to write my own simple regex parser to pull it out, and have a nice clever way to set that in my Configurations controller. So on to the code:

class Application < Merb::Controller
  class << self
    attr_accessor :attributes_to_alter
    def alter_attribute(attribute, &block)
      @attributes_to_alter ||= {}
      @attributes_to_alter[attribute] = block
    end
  end
  def attributes_to_alter
    Merb.logger.info self.class.attributes_to_alter.inspect
    self.class.attributes_to_alter || {}
  end

end

class Configurations < Application
  provides :sscj1

  alter_attribute 'parent_configuration_href' do |_,uri|
    {'parent_id' => extract_configuration_id(uri)}
  end

  def self.extract_configuration_id(uri)
    return nil unless uri
    %r{/configurations/(\d+)}.match(uri)
    $1
  end

end

So, here we have something similar to the #ignore_attributes, except now we have a block to be called on the attribute we want to change. In this case, I match the configurations part of the URI, and capture the id. Then , in my #parse_supplied_sscj1 method, I replace the old value with the new one:

def parse_supplied_sscj1
  begin 
    jobj = JSON.parse(request.raw_post)
    raise UnprocessableEntity unless jobj.is_a?(Hash)

    model_class = jobj["_type"].snake_case

    attributes_to_ignore.each do |key|
      jobj.delete(key)
    end

    attributes_to_alter.each do |attribute, block|
      new_attrs = block.call(attribute, jobj.delete(attribute))
      jobj.merge!(new_attrs)
    end

    params[model_class] = jobj
  rescue JSON::ParserError => e
    raise BadRequest.new(e.message)
  end
end

Thats the entire method that I’m using right now. I hope to package this all up as a merb plugin soon, keep and eye on my github, and I’ll probably post something about it here, soon.

Tagged , ,

HOWTO: Exception Handling in Merb

Our app is very (JSON) web-service heavy, and so having helpful error messages in our web service documents is pretty important. Luckily, Merb makes this, like everything, a metric shitton easier than it is in rails. There are a couple poorly documented things I had to stumble through, so I thought I would write some up on how to do this.

In Merb, if anything raises an exception, it looks for an action with the same name in the Exceptions controller. merb-gen gave you a simple one in app/controllers/exception.rb. Here’s what mine looks like now:

app/controllers/exceptions.rb

class Exceptions < Application
  provides :json                                                  # [1]

  # handle NotFound exceptions (404)
  def not_found
    return standard_error if content_type == :json                # [2]
    render
  end

  # handle NotAcceptable exceptions (406)
  def not_acceptable
    return standard_error if content_type == :json
    render
  end

  # handle NotAuthorized exceptions (403)
  def not_authorized
    return standard_error if content_type == :json
    render
  end

  # Everything else (500)
  def standard_error                                              # [3]
    # Re-Raise so we get the pretty merb error document instead.
    raise request.exceptions.first if content_type == :html       # [4]

    @exceptions = request.exceptions
    @show_details = Merb::Config[:exception_details]
    render :standard_error                                        # [5]
  end

end

Some things to note about what I’ve done:

  1. Make sure it #provides for the web-service content-type.
  2. Since I just wanted to use the same view template for every error (see below), I had to explicitly make all web-service calls render that action instead. I could have just removed those methods and deleted the templates, but then any html views in the app would be generic, and I wanted custom ones for 403 and 404.
  3. Since all errors inherit from StandardError, this will catch everything.
  4. However, in the case of HTML documents, we want to use the fancy merb one, so re-raise the error so that merb’s default error controller will handle it for us.
  5. Set up some variables to use in the view, then render that. Be sure to include :standard_error so that the other error handlers know which template to render!

And finally, here’s what my view looks like. You can do whatever you want, of course. #j is just a global helper I have that basically does #to_json on whatever you give it, with some special cases for date formatting and indenting in dev vs production.

app/views/exceptions/standard_error.json.erb:

<%= j( {
  :_type        => "InternalServerError",
  :request_uri  => request.env['REQUEST_URI'],
  :parameters   => params,
  :exceptions   => @exceptions.map do |exception|
    {
      :name       => exception.class,
      :message    => exception.message,
      :backtrace  => @show_details ? exception.backtrace : exception.backtrace.first.to_a
    }
  end
} ) %>
Tagged , ,

HOWTO: Getting a sidebar in Merb

In several of my pages, I have a side-bar menu-y thingie. I didn’t want to have to rewrite a controller-specific layout each time, but luckily Merb supports something similar to Rails’s content_for block that I wrote about earlier. In Merb, its done using throw_content(API) and catch_content(API).

Put the catch_content into your application layout view. You probably already have catch_content :for_layout in there, by default. Here’s what mine looks like:

%html
  %head
    %meta{:'http-equiv' => 'content-type', 'content' => 'application/xhtml+xml; charset=UTF-8'}

    = css_include_tag "layout", "style"

    %title Page with Sidebar

  %body

    #side-bar
      = catch_content :sidebar

    #main
      = catch_content :for_layout

    #footer
      .left= copyright
      .right= last_modified

Using haml, I’ve put my sidebar in a div with id #side-bar.

Now in the view, add a throw_content for what you want in the sidebar. In my case, I’m using a partial that gets picked up out of the controller’s view directory automatically.

- throw_content(:sidebar, partial('sidebar'))

%h1 This page has a sidebar

And ta-da! I only have to write the sidebar partial once for each controller, and I don’t have to write an extra layout for each one. I have a fairly uncomplicated layout, and fill out the various parts of it by throwing rendered partials into it.

Tagged , ,

HOWTO: DataMapper - Setting the default repository for a model

Had to google for quite a while before I was able to find the solution. Essentially, I have a model that I want to always use a different repository than what I #setup in :default. To do that:

class Person
  include DataMapper::Resource

  def self.default_repository_name
    :other
  end

  property :name, String
# ...
end

This will make Person.all and all other queries use the :other repository, without having to use the #repository(:other) { } block.

Application-wide Config loader for Merb Apps

I saw this post by Stephen Bartholomew and thought that it was a pretty neat idea, so I adapted it for Merb applications. Merb already has the Merb::Config for storing config options, so I just added the config options to that, rather than the AppConfig class used in Steve’s post. That also greatly simplifies its implementation:

require 'yaml'

class AppConfig  
  def self.load
    config_file = File.join(Merb.root, "config", "application.yml")

    if File.exists?(config_file)
      config = YAML.load(File.read(config_file))[Merb.environment]

      config.keys.each do |key|
        Merb::Config[key.to_sym] = config[key]
      end
    end
  end
end

Put that in lib/app_config.rb. Then, anywhere in config/init.rb, add:

Merb::BootLoader.after_app_loads do
  require 'app_config'
  AppConfig.load
end

Finally, add your config options to config/application.yml, using the same development, production, etc environment keys as in the database.yml. I use it to keep the connection params for a couple non-RDBMS DataMapper adapters I’ve written.

Tagged ,

Resourceful - Adding default options

I just commited a change that allows you to specify some default headers to attach to all requests made on a resource. Best shown in an example, I’ll use the sample code I gave in my last post:

require 'rubygems'
require 'resourceful'

http = Resourceful::HttpAccessor.new(:logger => Resourceful::StdOutLogger.new,
                                     :cache_manager => Resourceful::InMemoryCacheManager.new)


res = http.resource("http://core.ssbe.localhost/service_desciptors", 
                    :accept => 'application/json')

res.get
res.get

Just a little less typing required.

Tagged ,