relax 0.0.7 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +1 -1
 - data/README +137 -114
 - data/Rakefile +54 -0
 - data/VERSION.yml +4 -0
 - data/lib/relax/action.rb +39 -0
 - data/lib/relax/context.rb +40 -0
 - data/lib/relax/contextable.rb +15 -0
 - data/lib/relax/endpoint.rb +21 -0
 - data/lib/relax/instance.rb +23 -0
 - data/lib/relax/parameter.rb +19 -0
 - data/lib/relax/performer.rb +32 -0
 - data/lib/relax/service.rb +19 -88
 - data/lib/relax.rb +15 -10
 - data/spec/relax/endpoint_spec.rb +69 -0
 - data/spec/relax/integration_spec.rb +63 -0
 - data/spec/relax/service_spec.rb +21 -0
 - data/spec/services/flickr.rb +78 -0
 - data/spec/spec_helper.rb +12 -0
 - metadata +71 -30
 - data/lib/relax/parsers/base.rb +0 -30
 - data/lib/relax/parsers/factory.rb +0 -29
 - data/lib/relax/parsers/hpricot.rb +0 -133
 - data/lib/relax/parsers/rexml.rb +0 -147
 - data/lib/relax/parsers.rb +0 -13
 - data/lib/relax/query.rb +0 -46
 - data/lib/relax/request.rb +0 -107
 - data/lib/relax/response.rb +0 -82
 - data/lib/relax/symbolic_hash.rb +0 -79
 - data/spec/parsers/factory_spec.rb +0 -29
 - data/spec/parsers/hpricot_spec.rb +0 -31
 - data/spec/parsers/rexml_spec.rb +0 -36
 - data/spec/query_spec.rb +0 -60
 - data/spec/request_spec.rb +0 -114
 - data/spec/response_spec.rb +0 -98
 - data/spec/symbolic_hash_spec.rb +0 -67
 
    
        data/LICENSE
    CHANGED
    
    
    
        data/README
    CHANGED
    
    | 
         @@ -1,171 +1,194 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            = Relax
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            Relax is a  
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            parameters, and parse  
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
            It provides a basic set of functionality common to most REST consumers:
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
            - building HTTP queries (Relax::Request)
         
     | 
| 
       10 
     | 
    
         
            -
            - issuing HTTP requests (Relax::Service)
         
     | 
| 
       11 
     | 
    
         
            -
            - parsing XML responses (Relax::Response)
         
     | 
| 
      
 3 
     | 
    
         
            +
            Relax is a library that provides a foundation for writing REST consumer APIs,
         
     | 
| 
      
 4 
     | 
    
         
            +
            including the logic to handle the HTTP requests, build URLs with query
         
     | 
| 
      
 5 
     | 
    
         
            +
            parameters, and parse responses.
         
     | 
| 
       12 
6 
     | 
    
         | 
| 
       13 
7 
     | 
    
         | 
| 
       14 
8 
     | 
    
         
             
            == Tutorial
         
     | 
| 
       15 
9 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
            This short tutorial will walk you through the basic steps of creating a simple Flickr API that supports a single call to search for photos by tags.
         
     | 
| 
      
 10 
     | 
    
         
            +
            This short tutorial will walk you through the basic steps of creating a simple Flickr API consumer that supports a single call to search for photos by tags.
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
       17 
12 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
            ===  
     | 
| 
      
 13 
     | 
    
         
            +
            === First Things First
         
     | 
| 
       19 
14 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
            our Service class.
         
     | 
| 
      
 15 
     | 
    
         
            +
            The first step we'll take is to load the Relax gem.
         
     | 
| 
       22 
16 
     | 
    
         | 
| 
       23 
17 
     | 
    
         
             
              require 'rubygems'
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              gem 'relax', '~> 0.1.0'
         
     | 
| 
       24 
20 
     | 
    
         
             
              require 'relax'
         
     | 
| 
       25 
21 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
                class Service < Relax::Service
         
     | 
| 
       28 
     | 
    
         
            -
                  ENDPOINT = 'http://api.flickr.com/services/rest/'
         
     | 
| 
      
 22 
     | 
    
         
            +
            Then we'll define our service class.
         
     | 
| 
       29 
23 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                    super(ENDPOINT)
         
     | 
| 
       32 
     | 
    
         
            -
                  end
         
     | 
| 
       33 
     | 
    
         
            -
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              class Flickr < Relax::Service
         
     | 
| 
       34 
25 
     | 
    
         
             
              end
         
     | 
| 
       35 
26 
     | 
    
         | 
| 
       36 
27 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
            ===  
     | 
| 
      
 28 
     | 
    
         
            +
            === Adding an Endpoint
         
     | 
| 
       38 
29 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
            parameter, and each response will have a "stat" attribute that will equal "ok"
         
     | 
| 
       43 
     | 
    
         
            -
            when the response comes back without any errors.
         
     | 
| 
      
 30 
     | 
    
         
            +
            An endpoint is the base URL for the service we'll be consuming. All of our
         
     | 
| 
      
 31 
     | 
    
         
            +
            actions will be nested inside of an endpoint and build on top of it to
         
     | 
| 
      
 32 
     | 
    
         
            +
            forumlate the final request URL.
         
     | 
| 
       44 
33 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
               
     | 
| 
       46 
     | 
    
         
            -
                 
     | 
| 
       47 
     | 
    
         
            -
                  parameter :method
         
     | 
| 
      
 34 
     | 
    
         
            +
              class Flickr < Relax::Service
         
     | 
| 
      
 35 
     | 
    
         
            +
                endpoint 'http://api.flickr.com/services/rest/' do
         
     | 
| 
       48 
36 
     | 
    
         
             
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
       49 
38 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            === Adding Default Parameters
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            Since Flickr requires us to always pass an API key parameter let's make that a
         
     | 
| 
      
 43 
     | 
    
         
            +
             required default parameter in our service class.
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
              class Flickr < Relax::Service
         
     | 
| 
      
 46 
     | 
    
         
            +
                defaults do
         
     | 
| 
      
 47 
     | 
    
         
            +
                  parameter :api_key, :required => true
         
     | 
| 
       54 
48 
     | 
    
         
             
                end
         
     | 
| 
       55 
     | 
    
         
            -
              end
         
     | 
| 
       56 
49 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
            our service to make sure that our Flickr API key gets passed along with each
         
     | 
| 
       59 
     | 
    
         
            -
            request as well.
         
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
              module Flickr
         
     | 
| 
       62 
     | 
    
         
            -
                class Service < Relax::Service
         
     | 
| 
       63 
     | 
    
         
            -
                  ENDPOINT = 'http://api.flickr.com/services/rest/'
         
     | 
| 
       64 
     | 
    
         
            -
                  
         
     | 
| 
       65 
     | 
    
         
            -
                  def initialize(api_key)
         
     | 
| 
       66 
     | 
    
         
            -
                    super(ENDPOINT)
         
     | 
| 
       67 
     | 
    
         
            -
                    Request[:api_key] = api_key
         
     | 
| 
       68 
     | 
    
         
            -
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
                endpoint 'http://api.flickr.com/services/rest/' do
         
     | 
| 
       69 
51 
     | 
    
         
             
                end
         
     | 
| 
       70 
52 
     | 
    
         
             
              end
         
     | 
| 
       71 
53 
     | 
    
         | 
| 
       72 
     | 
    
         
            -
            When we call our Request class as we have here, we're basically setting up a
         
     | 
| 
       73 
     | 
    
         
            -
            value on our request that acts like a template. Each request we create now will
         
     | 
| 
       74 
     | 
    
         
            -
            have the api_key property prepopulated for us.
         
     | 
| 
       75 
54 
     | 
    
         | 
| 
      
 55 
     | 
    
         
            +
            === Adding an Action
         
     | 
| 
       76 
56 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
      
 57 
     | 
    
         
            +
            So we have our service now, but we need to define an action before we can
         
     | 
| 
      
 58 
     | 
    
         
            +
            actually fetch any data from it.
         
     | 
| 
       78 
59 
     | 
    
         | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
      
 60 
     | 
    
         
            +
              class Flickr < Relax::Service
         
     | 
| 
      
 61 
     | 
    
         
            +
                defaults do
         
     | 
| 
      
 62 
     | 
    
         
            +
                  parameter :api_key, :required => true
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
       81 
64 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
      
 65 
     | 
    
         
            +
                endpoint 'http://api.flickr.com/services/rest/' do
         
     | 
| 
      
 66 
     | 
    
         
            +
                  action :search do
         
     | 
| 
      
 67 
     | 
    
         
            +
                    parameter :method, :default => 'flickr.photos.search'
         
     | 
| 
      
 68 
     | 
    
         
            +
                    parameter :per_page, :default => 5
         
     | 
| 
      
 69 
     | 
    
         
            +
                    parameter :tags
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
       86 
71 
     | 
    
         
             
                end
         
     | 
| 
       87 
72 
     | 
    
         
             
              end
         
     | 
| 
       88 
73 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
            to specify what type of data we're expecting the response to give us. The
         
     | 
| 
       94 
     | 
    
         
            -
            default type is string.
         
     | 
| 
      
 74 
     | 
    
         
            +
            We're defining an action here called <tt>:search</tt> that will create an
         
     | 
| 
      
 75 
     | 
    
         
            +
            instance method on our service with the same name. There are three parameters,
         
     | 
| 
      
 76 
     | 
    
         
            +
            <tt>:method</tt>, <tt>:per_page</tt>, and <tt>:tags</tt>, with <tt>:method</tt>
         
     | 
| 
      
 77 
     | 
    
         
            +
            and <tt>:per_page</tt> receiving default values.
         
     | 
| 
       95 
78 
     | 
    
         | 
| 
      
 79 
     | 
    
         
            +
            There are lots of other parameters for the <tt>flickr.photos.search</tt> method
         
     | 
| 
      
 80 
     | 
    
         
            +
            that we could define, but we'll just stick with these for simplicities sake.
         
     | 
| 
       96 
81 
     | 
    
         | 
| 
       97 
     | 
    
         
            -
            === Step 4
         
     | 
| 
       98 
82 
     | 
    
         | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
            things contained, a Relax best practice is to create a module for each call
         
     | 
| 
       101 
     | 
    
         
            -
            on your service. The one we're creating here is the PhotoSearch module for the
         
     | 
| 
       102 
     | 
    
         
            -
            "flickr.photos.search" call on the Flickr API.
         
     | 
| 
      
 83 
     | 
    
         
            +
            === A Small Refactoring
         
     | 
| 
       103 
84 
     | 
    
         | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
      
 85 
     | 
    
         
            +
            When adding more methods it will quickly become obvious that we have another
         
     | 
| 
      
 86 
     | 
    
         
            +
            common parameter in <tt>:method</tt>. We can refactor our service class
         
     | 
| 
      
 87 
     | 
    
         
            +
            slightly to make this a default parameter for all of the actions on our
         
     | 
| 
      
 88 
     | 
    
         
            +
            endpoint.
         
     | 
| 
       105 
89 
     | 
    
         | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
      
 90 
     | 
    
         
            +
              class Flickr < Relax::Service
         
     | 
| 
      
 91 
     | 
    
         
            +
                defaults do
         
     | 
| 
      
 92 
     | 
    
         
            +
                  parameter :api_key, :required => true
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
       109 
94 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
      
 95 
     | 
    
         
            +
                endpoint 'http://api.flickr.com/services/rest/' do
         
     | 
| 
      
 96 
     | 
    
         
            +
                  defaults do
         
     | 
| 
      
 97 
     | 
    
         
            +
                    parameter :method, :required => true
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
       111 
99 
     | 
    
         | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                    parameter :per_page
         
     | 
| 
      
 100 
     | 
    
         
            +
                  action :search do
         
     | 
| 
      
 101 
     | 
    
         
            +
                    set :method, 'flickr.photos.search'
         
     | 
| 
      
 102 
     | 
    
         
            +
                    parameter :per_page, :default => 5
         
     | 
| 
       116 
103 
     | 
    
         
             
                    parameter :tags
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
                    def initialize(options = {})
         
     | 
| 
       119 
     | 
    
         
            -
                      super
         
     | 
| 
       120 
     | 
    
         
            -
                      @method = 'flickr.photos.search'
         
     | 
| 
       121 
     | 
    
         
            -
                    end
         
     | 
| 
       122 
104 
     | 
    
         
             
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
              end
         
     | 
| 
       123 
107 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
            The <tt>set</tt> method allows us to define a default value for a default
         
     | 
| 
      
 109 
     | 
    
         
            +
            parameter so we don't have to redefine common parameters inside every action.
         
     | 
| 
       127 
110 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
                  def search(options = {})
         
     | 
| 
       129 
     | 
    
         
            -
                    call(PhotoSearchRequest.new(options), PhotoSearchResponse)
         
     | 
| 
       130 
     | 
    
         
            -
                  end
         
     | 
| 
       131 
111 
     | 
    
         | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
      
 112 
     | 
    
         
            +
            === Defining a Parser
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
            Before we can actually perform a request we need to let the service know how to
         
     | 
| 
      
 115 
     | 
    
         
            +
            handle the response. This is done by defining a parser.
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
              class Flickr < Relax::Service
         
     | 
| 
      
 118 
     | 
    
         
            +
                defaults do
         
     | 
| 
      
 119 
     | 
    
         
            +
                  parameter :api_key, :required => true
         
     | 
| 
       135 
120 
     | 
    
         
             
                end
         
     | 
| 
       136 
     | 
    
         
            -
              end
         
     | 
| 
       137 
121 
     | 
    
         | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
              
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
      
 122 
     | 
    
         
            +
                endpoint 'http://api.flickr.com/services/rest/' do
         
     | 
| 
      
 123 
     | 
    
         
            +
                  defaults do
         
     | 
| 
      
 124 
     | 
    
         
            +
                    parameter :method, :required => true
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
       142 
126 
     | 
    
         | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
      
 127 
     | 
    
         
            +
                  action :search do
         
     | 
| 
      
 128 
     | 
    
         
            +
                    set :method, 'flickr.photos.search'
         
     | 
| 
      
 129 
     | 
    
         
            +
                    parameter :per_page, :default => 5
         
     | 
| 
      
 130 
     | 
    
         
            +
                    parameter :tags
         
     | 
| 
       146 
131 
     | 
    
         | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
      
 132 
     | 
    
         
            +
                    parser :rsp do
         
     | 
| 
      
 133 
     | 
    
         
            +
                      attribute :stat, :as => :status
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                      element :photos do
         
     | 
| 
      
 136 
     | 
    
         
            +
                        attribute :page
         
     | 
| 
      
 137 
     | 
    
         
            +
                        attribute :pages
         
     | 
| 
      
 138 
     | 
    
         
            +
                        attribute :perpage, :as => :per_page
         
     | 
| 
      
 139 
     | 
    
         
            +
                        attribute :total
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                        elements :photo do
         
     | 
| 
      
 142 
     | 
    
         
            +
                          attribute :id
         
     | 
| 
      
 143 
     | 
    
         
            +
                          attribute :owner
         
     | 
| 
      
 144 
     | 
    
         
            +
                          attribute :secret
         
     | 
| 
      
 145 
     | 
    
         
            +
                          attribute :server
         
     | 
| 
      
 146 
     | 
    
         
            +
                          attribute :farm
         
     | 
| 
      
 147 
     | 
    
         
            +
                          attribute :title
         
     | 
| 
      
 148 
     | 
    
         
            +
                          attribute :ispublic, :as => :is_public
         
     | 
| 
      
 149 
     | 
    
         
            +
                          attribute :isfriend, :as => :is_friend
         
     | 
| 
      
 150 
     | 
    
         
            +
                          attribute :isfamily, :as => :is_family
         
     | 
| 
      
 151 
     | 
    
         
            +
                        end
         
     | 
| 
      
 152 
     | 
    
         
            +
                      end
         
     | 
| 
      
 153 
     | 
    
         
            +
                    end
         
     | 
| 
       152 
154 
     | 
    
         
             
                  end
         
     | 
| 
       153 
155 
     | 
    
         
             
                end
         
     | 
| 
       154 
156 
     | 
    
         
             
              end
         
     | 
| 
       155 
157 
     | 
    
         | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
      
 158 
     | 
    
         
            +
            dhe parsing is performed by the Relief gem, so you can find out more about the
         
     | 
| 
      
 159 
     | 
    
         
            +
            syntax in its own documentation.
         
     | 
| 
       157 
160 
     | 
    
         | 
| 
       158 
     | 
    
         
            -
              flickr = Flickr::Service.new(ENV['FLICKR_API_KEY'])
         
     | 
| 
       159 
     | 
    
         
            -
              relax = flickr.find_by_tag('relax', :per_page => 10)
         
     | 
| 
       160 
161 
     | 
    
         | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
             
     | 
| 
       165 
     | 
    
         
            -
               
     | 
| 
      
 162 
     | 
    
         
            +
            === Making a Call
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
            Now, we're able to create a new instance of our service with the API key set.
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
              flickr = Flickr.new(:api_key => FLICKR_API_KEY)
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
            We can now use this object to search for photos by tag.
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
              flickr.search(:tags => 'cucumbers,lemons')
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
            This will return a Ruby response hash.
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
              {
         
     | 
| 
      
 175 
     | 
    
         
            +
                :status => "ok",
         
     | 
| 
      
 176 
     | 
    
         
            +
                :photos => {
         
     | 
| 
      
 177 
     | 
    
         
            +
                  :page => "1",
         
     | 
| 
      
 178 
     | 
    
         
            +
                  :pages => "3947",
         
     | 
| 
      
 179 
     | 
    
         
            +
                  :per_page => "5",
         
     | 
| 
      
 180 
     | 
    
         
            +
                  :total => "19733",
         
     | 
| 
      
 181 
     | 
    
         
            +
                  :photo => [
         
     | 
| 
      
 182 
     | 
    
         
            +
                    { :is_public => "1", :secret => "3c196485d2", :server => "3182", :is_friend => "0", :farm => "4", :title => "lemons", :is_family => "0", :id => "3509955709", :owner => "37013676@N06" },
         
     | 
| 
      
 183 
     | 
    
         
            +
                    { :is_public => "1", :secret => "44f1306a63", :server => "3326", :is_friend => "0", :farm => "4", :title => "Peeto", :is_family => "0", :id => "3509461859", :owner => "13217824@N04" },
         
     | 
| 
      
 184 
     | 
    
         
            +
                    { :is_public => "1", :secret => "dce53bce7f", :server => "3364", :is_friend => "0", :farm => "4", :title => "Peeto Above", :is_family => "0", :id => "3509459585", :owner => "13217824@N04" },
         
     | 
| 
      
 185 
     | 
    
         
            +
                    { :is_public => "1", :secret => "12f9ba167c", :server => "3632", :is_friend => "0", :farm => "4", :title => "Lemonaid", :is_family => "0", :id => "3509415752", :owner => "35666391@N03" },
         
     | 
| 
      
 186 
     | 
    
         
            +
                    { :is_public => "1", :secret => "8caac1ff46", :server => "3320", :is_friend => "0", :farm => "4", :title => "Gardening 365 (Day 8)", :is_family => "0", :id => "3509251322", :owner => "21778017@N06" }
         
     | 
| 
      
 187 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 188 
     | 
    
         
            +
                }
         
     | 
| 
      
 189 
     | 
    
         
            +
              }
         
     | 
| 
       166 
190 
     | 
    
         | 
| 
       167 
     | 
    
         
            -
            This will output the IDs and titles for the first 10 photos on Flickr that have
         
     | 
| 
       168 
     | 
    
         
            -
            the tag "relax."
         
     | 
| 
       169 
191 
     | 
    
         | 
| 
      
 192 
     | 
    
         
            +
            == Copyright
         
     | 
| 
       170 
193 
     | 
    
         | 
| 
       171 
     | 
    
         
            -
            Copyright (c) 2007- 
     | 
| 
      
 194 
     | 
    
         
            +
            Copyright (c) 2007-2009 Tyler Hunt. See LICENSE for details.
         
     | 
    
        data/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,54 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'rubygems'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rake'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'rake/rdoctask'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'spec/rake/spectask'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            begin
         
     | 
| 
      
 7 
     | 
    
         
            +
              require 'jeweler'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              Jeweler::Tasks.new do |gem|
         
     | 
| 
      
 10 
     | 
    
         
            +
                gem.name = "relax"
         
     | 
| 
      
 11 
     | 
    
         
            +
                gem.summary = %Q{A flexible library for creating web service consumers.}
         
     | 
| 
      
 12 
     | 
    
         
            +
                gem.email = "tyler@tylerhunt.com"
         
     | 
| 
      
 13 
     | 
    
         
            +
                gem.homepage = "http://github.com/tylerhunt/relax"
         
     | 
| 
      
 14 
     | 
    
         
            +
                gem.authors = ["Tyler Hunt"]
         
     | 
| 
      
 15 
     | 
    
         
            +
                gem.rubyforge_project = 'relax'
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                gem.add_dependency('rest-client', '~> 0.9.2')
         
     | 
| 
      
 18 
     | 
    
         
            +
                gem.add_dependency('nokogiri', '~> 1.2.3')
         
     | 
| 
      
 19 
     | 
    
         
            +
                gem.add_dependency('relief', '~> 0.0.3')
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                gem.add_development_dependency('jeweler', '~> 0.11.0')
         
     | 
| 
      
 22 
     | 
    
         
            +
                gem.add_development_dependency('rspec', '~> 1.2.2')
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            rescue LoadError
         
     | 
| 
      
 25 
     | 
    
         
            +
              puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            task :default => :spec
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            Spec::Rake::SpecTask.new(:spec) do |spec|
         
     | 
| 
      
 31 
     | 
    
         
            +
              spec.libs << 'lib' << 'spec'
         
     | 
| 
      
 32 
     | 
    
         
            +
              spec.spec_files = FileList['spec/**/*_spec.rb']
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            Spec::Rake::SpecTask.new(:rcov) do |spec|
         
     | 
| 
      
 36 
     | 
    
         
            +
              spec.libs << 'lib' << 'spec'
         
     | 
| 
      
 37 
     | 
    
         
            +
              spec.pattern = 'spec/**/*_spec.rb'
         
     | 
| 
      
 38 
     | 
    
         
            +
              spec.rcov = true
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            Rake::RDocTask.new do |rdoc|
         
     | 
| 
      
 42 
     | 
    
         
            +
              if File.exist?('VERSION.yml')
         
     | 
| 
      
 43 
     | 
    
         
            +
                config = YAML.load(File.read('VERSION.yml'))
         
     | 
| 
      
 44 
     | 
    
         
            +
                version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
         
     | 
| 
      
 45 
     | 
    
         
            +
              else
         
     | 
| 
      
 46 
     | 
    
         
            +
                version = ""
         
     | 
| 
      
 47 
     | 
    
         
            +
              end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              rdoc.rdoc_dir = 'rdoc'
         
     | 
| 
      
 50 
     | 
    
         
            +
              rdoc.title = "relief #{version}"
         
     | 
| 
      
 51 
     | 
    
         
            +
              rdoc.rdoc_files.include('README*')
         
     | 
| 
      
 52 
     | 
    
         
            +
              rdoc.rdoc_files.include('LICENSE*')
         
     | 
| 
      
 53 
     | 
    
         
            +
              rdoc.rdoc_files.include('lib/**/*.rb')
         
     | 
| 
      
 54 
     | 
    
         
            +
            end
         
     | 
    
        data/VERSION.yml
    ADDED
    
    
    
        data/lib/relax/action.rb
    ADDED
    
    | 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Relax
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Action
         
     | 
| 
      
 3 
     | 
    
         
            +
                include Contextable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :name
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(endpoint, name, options, &block)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @endpoint = endpoint
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  extend_context(endpoint)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  context.evaluate(&block) if block_given?
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def execute(values, credentials, *args)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  args.unshift(values) if values
         
     | 
| 
      
 18 
     | 
    
         
            +
                  instance = Instance.new(*args)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  response = performer(instance, credentials).perform
         
     | 
| 
      
 20 
     | 
    
         
            +
                  context.parse(response)
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def method
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @options[:method] || :get
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
                private :method
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def url
         
     | 
| 
      
 29 
     | 
    
         
            +
                  [@endpoint.url, @options[:url]].join
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
                private :url
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def performer(instance, credentials)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  values = instance.values(context)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  Performer.new(method, url, values, credentials)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
                private :performer
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Relax
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Context
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :parameters
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(parameters=[]) # :nodoc:
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @parameters = parameters
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def evaluate(&block) # :nodoc:
         
     | 
| 
      
 10 
     | 
    
         
            +
                  instance_eval(&block)
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def parameter(name, options={})
         
     | 
| 
      
 14 
     | 
    
         
            +
                  unless @parameters.find { |parameter| parameter.name == name }
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @parameters << Parameter.new(name, options)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  else
         
     | 
| 
      
 17 
     | 
    
         
            +
                    raise ArgumentError.new("Duplicate parameter '#{name}'.")
         
     | 
| 
      
 18 
     | 
    
         
            +
                  end
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def set(name, value)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  if parameter = @parameters.find { |parameter| parameter.name == name }
         
     | 
| 
      
 23 
     | 
    
         
            +
                    parameter.value = value
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def parser(root, options={}, &block) # :nodoc:
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @parser ||= Relief::Parser.new(root, options, &block)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def parse(response) # :nodoc:
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @parser.parse(response)
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def clone # :nodoc:
         
     | 
| 
      
 36 
     | 
    
         
            +
                  cloned_parameters = @parameters.collect { |parameter| parameter.clone }
         
     | 
| 
      
 37 
     | 
    
         
            +
                  self.class.new(cloned_parameters)
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Relax
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Endpoint
         
     | 
| 
      
 3 
     | 
    
         
            +
                include Contextable
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :url
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(service, url, options, &block)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @service = service
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @url = url
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  extend_context(service)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  instance_eval(&block)
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def action(name, options={}, &block)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  action = Action.new(self, name, options, &block)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @service.register_action(action)
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Relax
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Instance # :nodoc:
         
     | 
| 
      
 3 
     | 
    
         
            +
                def initialize(*args)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  @values = args.inject({}) do |values, arg|
         
     | 
| 
      
 5 
     | 
    
         
            +
                    arg.is_a?(Hash) ? values.merge(arg) : values
         
     | 
| 
      
 6 
     | 
    
         
            +
                  end
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def values(context)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  context.parameters.inject({}) do |values, parameter|
         
     | 
| 
      
 11 
     | 
    
         
            +
                    name = parameter.name
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    if value = @values[parameter.name] || parameter.value
         
     | 
| 
      
 14 
     | 
    
         
            +
                      values[parameter.name] = value
         
     | 
| 
      
 15 
     | 
    
         
            +
                    elsif parameter.required?
         
     | 
| 
      
 16 
     | 
    
         
            +
                      raise ArgumentError.new("Missing value for '#{parameter.name}'.")
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    values
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Relax
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Parameter
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :name, :options
         
     | 
| 
      
 4 
     | 
    
         
            +
                attr_writer :value
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(name, options={})
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def value
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @value || @options[:default]
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def required?
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @options[:required]
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Relax
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Performer
         
     | 
| 
      
 3 
     | 
    
         
            +
                def initialize(method, url, values, credentials)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  @method = method
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @url = url
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @values = values
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @credentials = credentials
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def perform
         
     | 
| 
      
 11 
     | 
    
         
            +
                  case @method
         
     | 
| 
      
 12 
     | 
    
         
            +
                    when :delete, :get, :head then RestClient.send(@method, url)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    when :post, :put then RestClient.send(@method, url, query)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def url
         
     | 
| 
      
 18 
     | 
    
         
            +
                  uri = URI.parse(@url)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  uri.query = query unless query.nil? || query.empty?
         
     | 
| 
      
 20 
     | 
    
         
            +
                  uri.userinfo = @credentials.join(':') if @credentials
         
     | 
| 
      
 21 
     | 
    
         
            +
                  uri.to_s
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
                private :url
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def query
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @values.collect do |name, value|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    "#{name}=#{value}" if value
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end.compact.join('&')
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
                private :query
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     |