HappyMapper, Making XML Fun Again

via RailsTips - Home by jnunemaker on 11/17/08

In which I show that XML does not have to suck—instead you can just HappyMap it!

As much as I write about XML, you would swear it is all I do, but I promise it is not. In fact, I do not really use XML that often, but I will admit that I am intrigued by it. A while back, you may remember, I posted about ROXML, a ruby object to xml mapping library. I liked the idea but not the implementation. Soon after, I started playing around with what I have named HappyMapper, a ruby object to xml mapping library.

I wrote nearly 95% of it in a weekend and then let it sit. I let it sit so long that it started to rot. Today it hit me that I do not have to finish something in order to release it. The thing that wasn’t working was xml with a default namespace. For good reasons I am sure, libxml-ruby does not like having default namespaces. I thought to myself, you know, this library is cool even without namespace junk. I mean who even uses namespaces other than Amazon. I started to package it for release and then I noticed a few nitpicky things. I tweaked them and five hours later I had also fixed the namespace issue and changed the API a bit. So much for releasing unfinished code in hopes that someone smarter than I would finish it up…

Examples

But I digress, you do not care about all that, right? How about some examples? Twitter’s xml seems to be popular on this here blawg, so I will start with that. Given this xml sample from twitter:

<statuses type="array"> 
  <status> 
    <created_at>Sat Aug 09 05:38:12 +0000 2008</created_at> 
    <id>882281424</id> 
    <text>I so just thought the guy lighting the Olympic torch was falling when he began to run on the wall. Wow that would have been catastrophic.</text> 
    <source>web</source> 
    <truncated>false</truncated> 
    <in_reply_to_status_id>1234</in_reply_to_status_id> 
    <in_reply_to_user_id>12345</in_reply_to_user_id> 
    <favorited></favorited> 
    <user> 
      <id>4243</id> 
      <name>John Nunemaker</name> 
      <screen_name>jnunemaker</screen_name> 
      <location>Mishawaka, IN, US</location> 
      <description>Loves his wife, ruby, notre dame football and iu basketball</description> 
      <profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/53781608/Photo_75_normal.jpg</profile_image_url> 
      <url>http://addictedtonew.com</url> 
      <protected>false</protected> 
      <followers_count>486</followers_count> 
    </user> 
  </status> 
</statuses>

You could setup the following ruby objects:

class User
  include HappyMapper

  element :id, Integer
  element :name, String
  element :screen_name, String
  element :location, String
  element :description, String
  element :profile_image_url, String
  element :url, String
  element :protected, Boolean
  element :followers_count, Integer
end

class Status
  include HappyMapper

  element :id, Integer
  element :text, String
  element :created_at, Time
  element :source, String
  element :truncated, Boolean
  element :in_reply_to_status_id, Integer
  element :in_reply_to_user_id, Integer
  element :favorited, Boolean
  has_one :user, User
end

statuses = Status.parse(xml_string)
statuses.each do |status|
  puts status.user.name, status.user.screen_name, status.text, status.source, ''
end

You can note a few things about HappyMapper from that example.

  1. Each xml element and attribute can be typecast.
  2. You can define association-like elements that are formed from other HappyMapper objects (see has_one :user, User in Status).
  3. You get a parse method when including HappyMapper that takes a string and does all the magic for you.

That was an easy one, how about something more complex and ugly, like some Amazon xml. Given some Amazon xml such as this:

<?xml version="1.0" encoding="UTF-8"?>
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05">
    <OperationRequest>
        <HTTPHeaders>
            <Header Name="UserAgent">
            </Header>
        </HTTPHeaders>
        <RequestId>16WRJBVEM155Q026KCV1</RequestId>
        <Arguments>
            <Argument Name="SearchIndex" Value="Books"></Argument>
            <Argument Name="Service" Value="AWSECommerceService"></Argument>
            <Argument Name="Title" Value="Ruby on Rails"></Argument>
            <Argument Name="Operation" Value="ItemSearch"></Argument>
            <Argument Name="AWSAccessKeyId" Value="dontbeaswoosh"></Argument>
        </Arguments>
        <RequestProcessingTime>0.064924955368042</RequestProcessingTime>
    </OperationRequest>
    <Items>
        <Request>
            <IsValid>True</IsValid>
            <ItemSearchRequest>
                <SearchIndex>Books</SearchIndex>
                <Title>Ruby on Rails</Title>
            </ItemSearchRequest>
        </Request>
        <TotalResults>22</TotalResults>
        <TotalPages>3</TotalPages>
        <Item>
            <ASIN>0321480791</ASIN>
        <DetailPageURL>http://www.amazon.com/gp/redirect.html%3FASIN=0321480791%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321480791%253FSubscriptionId=dontbeaswoosh</DetailPageURL>
            <ItemAttributes>
                <Author>Michael Hartl</Author>
                <Author>Aurelius Prochazka</Author>
                <Manufacturer>Addison-Wesley Professional</Manufacturer>
                <ProductGroup>Book</ProductGroup>
                <Title>RailsSpace: Building a Social Networking Website with Ruby on Rails (Addison-Wesley Professional Ruby Series)</Title>
            </ItemAttributes>
        </Item>
    </Items>
</ItemSearchResponse>

You could create the following objects to obtain Item information:

module PITA
  class Item
    include HappyMapper

    tag 'Item' # if you put class in module you need tag
    element :asin, String, :tag => 'ASIN'
    element :detail_page_url, String, :tag => 'DetailPageURL'
    element :manufacturer, String, :tag => 'Manufacturer', :deep => true
  end

  class Items
    include HappyMapper

    tag 'Items' # if you put class in module you need tag
    element :total_results, Integer, :tag => 'TotalResults'
    element :total_pages, Integer, :tag => 'TotalPages'
    has_many :items, Item
  end
end

item = PITA::Items.parse(xml_string, :single => true, :use_default_namespace => true)
item.items.each do |i|
  puts i.asin, i.detail_page_url, i.manufacturer, ''
end

The previous example showed a few more things.

  1. You can put your HappyMapper objects in a module and define the tag name (see tag ‘Item’ inside PITA::Item).
  2. You can create nice methods for crappy camel cased xml tags (see element :total_pages, Integer, :tag => 'TotalPages' in PITA::Items).
  3. There is also a has_many association-like method that allows defining a collection of HappyMapper objects. (see has_many :items, Item in PITA::Items)
  4. You do not have to map exact parent child relationships. You can go deep see diving with the :deep option on any element to pluck out grandchildren and such. (see element :manufacturer in PITA::Item)

Installation

Installation is typical as the gem is on rubyforge and github.

#rubyforge
$ sudo gem install happymapper

# github
$ sudo gem install jnunemaker-happymapper

If you run into problems, feel free to fork and add some specs for the xml it is not working with. From there you can dive in and fix them or let me know and I will take a look. Think this will be handy? Got an idea? Let me know in the comments below. Oh, and yes, in the future, HappyMapper will have killer HTTParty integration.

Run Capistrano on the server with Github post-receive URL

via RubyFlow on 11/16/08
Make your Rails application “deploy itself” with Pushr, deploying it by Github post-receive hook or by clicking a button in web interface. Read more or browse the source-code.

Creating and Integrating Bookmarklets with Rails

via RailsTips - Home by jnunemaker on 11/14/08

In which I show how to create and integrate handy dandy bookmarklets with your Rails app.

If your application stores information, one of the most critical keys to success is allowing users to get that information in quick. If the process is a pain, they’ll move on to something else. One way to allow for easy and quick data entry is a bookmarklet. I recently created a couple for an app I am scratching an itch with and thought I would share the results with you.

Two FYIs: For some reason I sniffed twice during the recording, but I decided to leave them both in to add some “reality.” Also, the ND YouTube tab was open because I was going to show how the tumblr bookmarklet works, not because I forgot the tab open.

Quicktime Creating and Integrating Bookmarklets with Rails

Character Traits

via The Richter Family by Manda on 11/3/08
Lately, I've been thinking about the kids similarities and differences. And that got me to think about the root of their differences, their main character traits.

Inara is confident. There is nothing she thinks she can't do! She can run, talk, sing, dance, jump off tall objects, play with things that pinch her fingers, etc. When she wants to try something, she wants you to leave her to do it herself. She is so independent when she is playing or working on something! And tough too! She is the toughest so far of all the kids.

Lex is decisive. When he decides something, he is not easily swayed. Sometimes this is helpful, sometimes it is not. Strong-willed, determined, resolute, bold, these words all describe Lex. He might decide he wants to do something himself, and another time he decides he must have help even if it is something he knows how to do.

All character traits have strengths and weaknesses. I hope Justin and I will be able to nurture the positive aspects of their character, not the negative aspects. It seems more natural to me to tell them what not to do (negative aspects) instead of helping show them what they should be doing (positive aspects). How to teach Inara to build her confidence into conviction, and Lex to make wise decisions. It's a big job!

What’s Hot on Github - October 2008

via Ruby Inside by Zach Inglis on 10/30/08

GitHub LogoWhat's Hot on Github is a monthly post highlighting interesting projects that are new or updated this month, within the Ruby community that are hosted on Github. Github has become an extremely popular place for Ruby and Rails developers to congregate lately, so I wanted to list some of the new projects, and some of the updated ones, that I have found interesting and that are too small for their own blog post.

This month's picks:

  • seinfield - Track your open-source contributions with Github.
  • suprails - A funky new substitute for the "rails" command.
  • nokogiri - A hpricot alternative that boasts better speed.
  • astrotrain - "Scans incoming emails for mapped recipients and sends an HTTP POST somewhere."
  • friendly_id - "monkey patched friendly_id that allows for a larger number of duplicate slugs". Includes remembering past slugs.
  • acts_as_sayable - "Rails plugin to provide text2speech functionality over models."
  • flash-message-conductor - A simple pattern for managing flash messages in your Ruby on Rails application.
  • formtastic - A Rails Form Builder with semantically rich and accessible markup.

A special thanks this month goes to the Rails Rumble team as they made some great suggestions based on what people had used for their applications.

Have any projects to add or want to recommend a project for next month's post? Please leave a comment.

session_expiry plugin - Expire plugins after inactivity

via RubyFlow on 10/30/08
The session_expiry plugin allows you to set a time in which your session is valid, and expires the session after that, with a callback method to potentially set flash messages.

What's New in Edge Rails: Even Better Conditional GET Support

via Ryan's Scraps - Blog by ryan on 10/24/08

We talked about the new conditional GET support in rails a couple months ago. As some of the comments alluded, the feature was somewhat cumbersome to use – especially by Ruby standards. Well, the feature has since been refined. So, read the original post to get the gist and come back here for the sugar.

Instead of manually setting properties directly on the response and querying the request to see if it’s fresh we have some higher-level accessors we can use. Observe (extending our previous example):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ArticlesController < ApplicationController

  def show
    @article = Article.find(params[:id])

    # If the request is stale according to the given timestamp and etag value
    # (i.e. it needs to be processed again) then execute this block
    if stale?(:last_modified => @article.published_at.utc, :etag => @article)
      respond_to do |wants|
        # ... normal response processing
      end
    end

    # If the request is fresh (i.e. it's not modified) then you don't need to do
    # anything. The default render checks for this using the parameters
    # used in the previous call to stale? and will automatically send a
    # :not_modified.  So that's it, you're done.
end

If you don’t have any special response processing and are using the default rendering mechanism (i.e. you’re not using respond_to or calling render yourself) then you’ve got an easy helper in fresh_when:

1
2
3
4
5
6
7
8
9
class ArticlesController < ApplicationController

  # This will automatically send back a :not_modified if the request is fresh, and
  # will render the default template (article.*) if it's stale.
  def show
    @article = Article.find(params[:id])
    fresh_when :last_modified => @article.published_at.utc, :etag => @article
  end
end

There you have it, the new and improved conditional GET support in Rails 2.2.

tags: ruby, rubyonrails

Lost Season 5 promo

via The Inquisitr by Duncan Riley on 10/22/08

ABC has released its first promotion clip for the 5th season of television program Lost.

Clip below. We warn though that if you haven’t seen the end of the last season, you shouldn’t watch the clip.

StumbleUpon Digg Reddit Mixx del.icio.us Fark Facebook TwitThis Kwoff Propeller

Related Posts

  • No Related Post

ParseTree version 3.0.1 has been released!

via Polishing Ruby by zenspider on 10/22/08

ParseTree is a C extension (using RubyInline) that extracts the parse tree for an entire class or a specific method and returns it as a s-expression (aka sexp) using ruby's arrays, strings, symbols, and integers.

As an example:

def conditional1(arg1) if arg1 == 0 then return 1 end return 0 end

becomes:

[:defn, :conditional1, [:scope, [:block, [:args, :arg1], [:if, [:call, [:lvar, :arg1], :==, [:array, [:lit, 0]]], [:return, [:lit, 1]], nil], [:return, [:lit, 0]]]]]

Changes:

3.0.1 / 2008-10-23

 

Tip: Managing Your Reading List

Before you get started, we want to let you know about an important feature of Google Reader.

As you view items in your reading list, they will be automatically marked as read as you scroll down (when in the "Expanded" view).

If you'd prefer to disable this feature, you can turn it off in Settings.

Dismiss this message (it will not appear again)

You haven't shared any items yet.

Sharing interesting items with your friends is easy: simply click on the sharing icon.

The item will then instantly appear on your public page at:



This page is accessible to anyone who knows its address, so all that's left to do is to let your friends know about it.

Additionally, all your friends in Gmail Chat and Google Talk that use Google Reader will then be able to see your shared items. Learn more about friends.

Find out more about sharing
Sort by oldest only shows items from the last 30 days. Learn more Dismiss
You are not subscribed to this recommended feed yet.

If you'd like to automatically receive updates to this feed, you can subscribe now.