Welcome to the ECS page at RubyForge

Introduction
Project Page
Copyright
Installation
Tests
Philosophy
Usage
Source
Bugs
Contact

Introduction

ECS is a Ruby interface to Amazon's E-Commerce Service, a remarkably large API that exposes access to many of Amazon's products and services. To avoid confusion, this document refers to the Ruby interface as 'ECS' and to the web service as 'AECS.'

Please note that ECS 0.1.0 requires Libxml-Ruby.

Project Page

http://rubyforge.org/projects/ecs/

Copyright

ECS is copyright 2007 by Zachary Holt. It is released under the MIT license. See the MIT-LICENSE file in your distribution for more information.

Installation

gem install ECS or sudo gem install ECS

Tests

If you want to run the unit tests, you will need to edit the test/unit/unit_test_setup.rb file in your gem installation. Once those settings are correct, you can gem check -t ECS
or ruby test/all_tests.rb

Philosophy

In developing ECS, I've tried to stick to two main design principles. The first is to write as little code as possible. The class ECS::Help is the base class for all requests to AECS. However, none of the other request classes exists. They are all created dynamically. If you issue
ECS.help( :HelpType => 'Operation', :About => 'BrowseNodeLookup' )
then an instance of ECS::Help (which you can browse in the lib/ecs directory) will be instantiated and returned. However, if you issue
ECS.browse_node_lookup( :BrowseNodeId => 22 )
then a subclass of ECS::Help named something like ECS::BrowseNodeLookup (which you cannot browse in the lib/ecs directory) will be created. An instance of the new class will be instantiated and returned. All subsequent calls to ECS.browse_node_lookup will use the same subclass of ECS::Help.

(As of version 0.1.0, ECS::HelpResponse is not in use. The idea was to use it as the base class for response objects. However, because of the uncertainty surrounding the metadata returned by the Help operation for a ResponseGroup, this will have to be implemented later, if it is implemented at all. See this thread for more info.)

The second principle is to minimize the number of calls made to the web service. In the following code,
my_browse_node = ECS.browse_node_lookup( :BrowseNodeId => 22 )
puts my_browse_node.Name
the second line of code triggers something like the following: You don't need to call the web service explicitly. After the first line above, the second line will ensure that there is XML to manipulate.

Please note that if you If you encounter a place where these principles are violated or could be better implemented, please let me know.

Usage

Once you've installed the gem, you can do the following:
require 'rubygems'
require 'ecs'

ECS.access_key_id = 'YOURACCESSKEYID'
ECS.cache_directory = '/path/to/the/cache/directory/'
ECS::TimeManagement.time_file = '/path/to/the/timefile/'
The access_key_id is your AWS access key id. The cache_directory is the directory where the cached XML should be stored. The user as whom the code is running must have write privileges on that directory. The time_file is the name of the file in which ECS will store data about its last call to AECS. In order to follow the one-request-per-second rule, ECS needs to track when requests are made.


book = ECS.item_lookup( :ItemId => '0316769029' )  # Note that no call has yet been made to the web service
book.each_Author { |a| puts a.content } # => 'J.D. Salinger'
puts book.Title.content # => 'Franny and Zooey'
The book object above contains an instance variable called xml, which is an altered XML::Document object. If you send a method to book that triggers a method_missing, then book calls

self.xml.find( '//Title' ) # this is what book would do if you called book.Title
If the resulting XML::Node::Set is of size 1, then that XML::Node is returned. Otherwise, the XML::Node::Set is returned.

A major exception to this is sending an each_* method with a block. In the call to book.each_Author above, book would call

self.Author.each ...
In other words, if you pass 'each_' before the name of an element, book will iterate through each of the so named elements in its XML tree. It's important to remember that the XPath used is '//Whatever', which means "any element named Whatever"--all Whatever elements, regardless of whether they are siblings.

If you call book.each_Whatever without a block, then ECS assumes you're looking for the element named "each_Whatever."

Initially I thought it would be nice to allow each_whatever to be semantically equivalent to each_Whatever. Given the case sensitivity of XML, however, I haven't figured out a good way to do this. I'm open to input on this (and everything else!).



Now that we can create an ECS object, consider the following XML as the contents of the xml instance variable:
<?xml version="1.0" encoding="UTF-8"?>
<ItemLookupResponse>
  <OperationRequest>
    <HTTPHeaders>
      <Header Name="UserAgent"/>
    </HTTPHeaders>
    <RequestId>0FQKW2MD8T2R7JZEBDD8</RequestId>
    <Arguments>
      <Argument Name="AssociateTag" Value="limn-20"/>
      <Argument Name="ItemId" Value="0316769029"/>
      <Argument Name="Service" Value="AWSECommerceService"/>
      <Argument Name="Operation" Value="ItemLookup"/>
      <Argument Name="AWSAccessKeyId" Value=""/>
      <Argument Name="Version" Value="2007-02-22"/>
    </Arguments>
    <RequestProcessingTime>0.0246319770812988</RequestProcessingTime>
  </OperationRequest>
  <Items>
    <Request>
      <IsValid>True</IsValid>
      <ItemLookupRequest>
        <ItemId>0316769029</ItemId>
      </ItemLookupRequest>
    </Request>
    <Item>
      <ASIN>0316769029</ASIN>
      <DetailPageURL>http://www.amazon.com/gp/redirect.html...</DetailPageURL>
      <ItemAttributes>
        <Author>J.D. Salinger</Author>
        <Manufacturer>Back Bay Books</Manufacturer>
        <ProductGroup>Book</ProductGroup>
        <Title>Franny and Zooey</Title>
      </ItemAttributes>
    </Item>
  </Items>
</ItemLookupResponse> 
book = ECS.item_lookup( :ItemId => '0316769029' )
book.Author # legal, returns an XML::Node, because there is only one Author element in the whole tree
items_node = book.Items # legal, returns an XML::Node, because there is only one Items element in the whole tree
items_node.Author # illegal, as there is no Author child of Items
The book object above is an instance of a subclass of ECS::Help. In ECS::Help#method_missing, the find that is performed looks for any so-named element (the XPath has two leading slashes).

In the modified XML::Node#method_missing, however, the find that is performed looks only for children (the XPath has one leading slash). Because the items_node object above is an instance of the modified XML::Node, its method_missing will only search for children (not grandchildren, great-grandchildren, etc.). Thus, items_node.Author will raise an error, even though items_node has a descendant element named Author.

Source

You can browse the source via your web browser, or svn checkout svn://rubyforge.org/var/svn/ecs/trunk

Bugs

Please submit bugs here.

Contact

Please feel free to use the forums!