relax 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +152 -3
- data/lib/relax/query.rb +7 -0
- data/lib/relax/request.rb +16 -3
- data/lib/relax/response.rb +43 -4
- data/lib/relax/service.rb +101 -0
- data/lib/relax/symbolic_hash.rb +20 -6
- data/lib/relax.rb +2 -2
- data/spec/response_spec.rb +18 -2
- metadata +4 -4
- data/lib/relax/api.rb +0 -49
data/README
CHANGED
@@ -1,4 +1,153 @@
|
|
1
|
-
Relax
|
2
|
-
=====
|
1
|
+
= Relax
|
3
2
|
|
4
|
-
|
3
|
+
Relax is a simple library for creating REST consumers.
|
4
|
+
|
5
|
+
When used as a basis for writing REST consumer APIs, it provides a set of
|
6
|
+
functionality common to all REST consumers, including:
|
7
|
+
|
8
|
+
- building HTTP queries (Relax::Request)
|
9
|
+
- issuing HTTP requests (Relax::Service)
|
10
|
+
- parsing XML responses (Relax::Response)
|
11
|
+
|
12
|
+
|
13
|
+
== Tutorial
|
14
|
+
|
15
|
+
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.
|
16
|
+
|
17
|
+
=== Step 1
|
18
|
+
|
19
|
+
In the first step we're going to simply include Relax, and define the basis for
|
20
|
+
our Service class.
|
21
|
+
|
22
|
+
require 'rubygems'
|
23
|
+
require 'relax'
|
24
|
+
|
25
|
+
module Flickr
|
26
|
+
class Service < Relax::Service
|
27
|
+
ENDPOINT = 'http://api.flickr.com/services/rest/'
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
super(ENDPOINT)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
=== Step 2
|
37
|
+
|
38
|
+
Next we're going to define common Request and Response classes for use
|
39
|
+
throughout our API. This gives us a single point to add any shared
|
40
|
+
functionality. For Flickr, this means that each request will have a "method"
|
41
|
+
parameter, and each response will have a "stat" attribute that will equal "ok"
|
42
|
+
when the response comes back without any errors.
|
43
|
+
|
44
|
+
module Flickr
|
45
|
+
class Request < Relax::Request
|
46
|
+
parameter :method
|
47
|
+
end
|
48
|
+
|
49
|
+
class Response < Relax::Response
|
50
|
+
def successful?
|
51
|
+
root[:stat] == 'ok'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
While we're at it, we're also going to add a new line to the constructor from
|
57
|
+
our service to make sure that our Flickr API key gets passed along with each
|
58
|
+
request as well.
|
59
|
+
|
60
|
+
module Flickr
|
61
|
+
class Service < Relax::Service
|
62
|
+
ENDPOINT = 'http://api.flickr.com/services/rest/'
|
63
|
+
|
64
|
+
def initialize(api_key)
|
65
|
+
super(ENDPOINT)
|
66
|
+
Request[:api_key] = api_key
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
When we call our Request class as we have here, we're basically setting up a
|
72
|
+
value on our request that acts like a template. Each request we create now will
|
73
|
+
have the api_key property prepopulated for us.
|
74
|
+
|
75
|
+
|
76
|
+
=== Step 3
|
77
|
+
|
78
|
+
Next, we're going to need a basic Photo class to represent photos that Flickr
|
79
|
+
returns to us.
|
80
|
+
|
81
|
+
module Flickr
|
82
|
+
class Photo < Response
|
83
|
+
parameter :id, :attribute => true, :type => :integer
|
84
|
+
parameter :title, :attribute => true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
Here we're creating a Response class that extends our Flickr::Response, which
|
89
|
+
has two parameters: "id" and "title." By setting the attribute option to true,
|
90
|
+
we're telling Relax to look for an attribute by that name on the XML root
|
91
|
+
instead of checking for an element by that name. The type options can be used
|
92
|
+
to specify what type of data we're expecting the response to give us. The
|
93
|
+
default type is string.
|
94
|
+
|
95
|
+
|
96
|
+
=== Step 4
|
97
|
+
|
98
|
+
Now we arrive at the final piece of the puzzle: a service call module. To keep
|
99
|
+
things contained, a Relax best practice is to create a module for each call
|
100
|
+
on your service. The one we're creating here is the PhotoSearch module for the
|
101
|
+
"flickr.photos.search" call on the Flickr API.
|
102
|
+
|
103
|
+
There are three main pieces to every service call module:
|
104
|
+
|
105
|
+
1. a Relax::Request object
|
106
|
+
2. a Relax::Response object
|
107
|
+
3. a call method that calls Relax::Service#call
|
108
|
+
|
109
|
+
This module then gets included into the Service class, where the call method
|
110
|
+
can be easily utilized.
|
111
|
+
|
112
|
+
module Flickr
|
113
|
+
module PhotoSearch
|
114
|
+
class PhotoSearchRequest < Flickr::Request
|
115
|
+
parameter :per_page
|
116
|
+
parameter :tags
|
117
|
+
|
118
|
+
def initialize(options = {})
|
119
|
+
super
|
120
|
+
@method = 'flickr.photos.search'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class PhotoSearchResponse < Flickr::Response
|
125
|
+
parameter :photos, :element => 'photos/photo', :collection => Photo
|
126
|
+
end
|
127
|
+
|
128
|
+
def search(options = {})
|
129
|
+
call(PhotoSearchRequest.new(options), PhotoSearchResponse)
|
130
|
+
end
|
131
|
+
|
132
|
+
def find_by_tag(tags, options = {})
|
133
|
+
search(options.merge(:tags => tags))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
As you can see, we have our request (PhotoSearchRequest), response
|
139
|
+
(PhotoSearchResponse), and call method (actually, two in this case: search and
|
140
|
+
find_by_tag). This will get included into our Flickr::Service class, and we
|
141
|
+
can use it by calling either of the call methods.
|
142
|
+
|
143
|
+
flickr = Flickr::Service.new(ENV['FLICKR_API_KEY'])
|
144
|
+
relax = flickr.find_by_tag('relax', :per_page => 10)
|
145
|
+
|
146
|
+
if relax.successful?
|
147
|
+
relax.photos.each do |photo|
|
148
|
+
puts "[#{photo.id}] #{photo.title}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
This will output the IDs and titles for the first 10 photos on Flickr that have
|
153
|
+
the tag "relax."
|
data/lib/relax/query.rb
CHANGED
@@ -4,7 +4,10 @@ require 'uri'
|
|
4
4
|
require 'relax/symbolic_hash'
|
5
5
|
|
6
6
|
module Relax
|
7
|
+
# Query is used to represent the query portion of a URL. It's basically just
|
8
|
+
# a hash, where each key/value pair is a query parameter.
|
7
9
|
class Query < SymbolicHash
|
10
|
+
# Converts the Query to a query string for use in a URL.
|
8
11
|
def to_s
|
9
12
|
keys.sort { |a, b| a.to_s <=> b.to_s }.collect do |key|
|
10
13
|
"#{key.to_s}=#{self.class.escape_value(fetch(key))}"
|
@@ -12,6 +15,7 @@ module Relax
|
|
12
15
|
end
|
13
16
|
|
14
17
|
class << self
|
18
|
+
# Parses a URL and returns a Query with its query portion.
|
15
19
|
def parse(uri)
|
16
20
|
query = uri.query.split('&').inject({}) do |query, parameter|
|
17
21
|
key, value = parameter.split('=')
|
@@ -21,10 +25,12 @@ module Relax
|
|
21
25
|
self.new(query)
|
22
26
|
end
|
23
27
|
|
28
|
+
# Escapes a query parameter value.
|
24
29
|
def escape_value(value)
|
25
30
|
ERB::Util.url_encode(value.to_s).gsub('%20', '+')
|
26
31
|
end
|
27
32
|
|
33
|
+
# Unescapes a query parameter value.
|
28
34
|
def unescape_value(value)
|
29
35
|
URI.unescape(value)
|
30
36
|
end
|
@@ -32,6 +38,7 @@ module Relax
|
|
32
38
|
|
33
39
|
protected
|
34
40
|
|
41
|
+
# Converts each value of the Query to a string as it's added.
|
35
42
|
def convert_value(value)
|
36
43
|
value.to_s
|
37
44
|
end
|
data/lib/relax/request.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'relax/query'
|
2
2
|
|
3
3
|
module Relax
|
4
|
+
# Request is intended to be a parent class for requests passed to
|
5
|
+
# Service#call.
|
4
6
|
class Request
|
5
7
|
def initialize(options = {})
|
6
8
|
self.class.class_variables.each do |variable|
|
@@ -12,6 +14,7 @@ module Relax
|
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
17
|
+
# Converts this request into a Query object.
|
15
18
|
def to_query
|
16
19
|
keys.inject(Query.new) do |parameters, key|
|
17
20
|
parameters[convert_key(key)] = send(key)
|
@@ -19,18 +22,23 @@ module Relax
|
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
25
|
+
# Converts this request into a query string for use in a URL.
|
22
26
|
def to_s
|
23
|
-
|
24
|
-
"#{key.to_s}=#{ERB::Util.url_encode(send('[]', key).to_s)}"
|
25
|
-
end.join('&')
|
27
|
+
to_query.to_s
|
26
28
|
end
|
27
29
|
|
28
30
|
class << self
|
31
|
+
# Specifies a parameter to create on the request class.
|
32
|
+
#
|
33
|
+
# Options:
|
34
|
+
# - <tt>:value</tt>: The default value for this parameter.
|
29
35
|
def parameter(name, options = {})
|
30
36
|
attr_accessor name
|
31
37
|
class_variable_set("@@#{name}", options.delete(:value)) if options[:value]
|
32
38
|
end
|
33
39
|
|
40
|
+
# Adds a template value to a request class. Equivalent to creating a
|
41
|
+
# parameter with a default value.
|
34
42
|
def []=(key, value)
|
35
43
|
parameter(key, {:value => value})
|
36
44
|
end
|
@@ -38,10 +46,15 @@ module Relax
|
|
38
46
|
|
39
47
|
protected
|
40
48
|
|
49
|
+
# Returns an array of the parameter names for this request.
|
41
50
|
def keys
|
42
51
|
instance_variables.collect { |v| v.sub('@', '') }
|
43
52
|
end
|
44
53
|
|
54
|
+
# Converts a key when the Request is converted to a query. By default, no
|
55
|
+
# conversion actually takes place, but this method can be overridden by
|
56
|
+
# child classes to perform standard manipulations, such as replacing
|
57
|
+
# underscores.
|
45
58
|
def convert_key(key)
|
46
59
|
key
|
47
60
|
end
|
data/lib/relax/response.rb
CHANGED
@@ -4,9 +4,18 @@ require 'hpricot'
|
|
4
4
|
require 'date'
|
5
5
|
|
6
6
|
module Relax
|
7
|
+
# Response is intended to be a parent class for responses passed to
|
8
|
+
# Service#call.
|
9
|
+
#
|
10
|
+
# A response is in essence an object used to facilitate XML parsing. It
|
11
|
+
# stores an XML document, and provides access to it through methods like
|
12
|
+
# #element and #attribute.
|
7
13
|
class Response
|
8
14
|
attr_accessor :xml
|
9
15
|
|
16
|
+
# New takes in the XML from the response. For the initial response, this
|
17
|
+
# will be the root element, but child elements may also be passed into
|
18
|
+
# Response objects.
|
10
19
|
def initialize(xml)
|
11
20
|
@xml = Hpricot.XML(xml.to_s)
|
12
21
|
|
@@ -29,7 +38,10 @@ module Relax
|
|
29
38
|
options[:collection].new(element)
|
30
39
|
end
|
31
40
|
else
|
32
|
-
case options[:type]
|
41
|
+
case type = options[:type]
|
42
|
+
when Response
|
43
|
+
value = type.new(node)
|
44
|
+
|
33
45
|
when :float
|
34
46
|
value = float_value(node)
|
35
47
|
|
@@ -47,62 +59,89 @@ module Relax
|
|
47
59
|
end
|
48
60
|
end
|
49
61
|
|
62
|
+
# Returns the root of the XML document.
|
50
63
|
def root
|
51
64
|
@xml.root
|
52
65
|
end
|
53
66
|
|
67
|
+
# Checks the name of the root node.
|
54
68
|
def is?(name)
|
55
69
|
root.name.gsub(/.*:(.*)/, '\1') == node_name(name)
|
56
70
|
end
|
57
71
|
|
72
|
+
# Returns an element of the specified name.
|
58
73
|
def element(name)
|
59
74
|
root.at(root_path(name))
|
60
75
|
end
|
76
|
+
alias :has? :element
|
61
77
|
|
78
|
+
# Returns an attribute on an element.
|
62
79
|
def attribute(element, name)
|
63
80
|
element[name]
|
64
81
|
end
|
65
82
|
|
83
|
+
# Returns a set of elements matching name.
|
66
84
|
def elements(name)
|
67
85
|
root.search(root_path(name))
|
68
86
|
end
|
69
87
|
|
88
|
+
# Gets the value of an element or attribute.
|
70
89
|
def value(value)
|
71
90
|
value.is_a?(Hpricot::Elem) ? value.inner_text : value.to_s
|
72
91
|
end
|
73
92
|
|
93
|
+
# Gets a text value.
|
74
94
|
def text_value(value)
|
75
95
|
value(value)
|
76
96
|
end
|
77
97
|
|
98
|
+
# Gets an integer value.
|
78
99
|
def integer_value(value)
|
79
100
|
value(value).to_i
|
80
101
|
end
|
81
102
|
|
103
|
+
# Gets a float value.
|
82
104
|
def float_value(value)
|
83
105
|
value(value).to_f
|
84
106
|
end
|
85
107
|
|
108
|
+
# Gets a date value.
|
86
109
|
def date_value(value)
|
87
110
|
Date.parse(value(value))
|
88
111
|
end
|
89
112
|
|
90
|
-
alias :has? :element
|
91
|
-
|
92
113
|
class << self
|
114
|
+
# Specifes a parameter that will be automatically parsed when the
|
115
|
+
# Response is instantiated.
|
116
|
+
#
|
117
|
+
# Options:
|
118
|
+
# - <tt>:attribute</tt>: An attribute name to use, or <tt>true</tt> to
|
119
|
+
# use the <tt>:element</tt> value as the attribute name on the root.
|
120
|
+
# - <tt>:collection</tt>: A class used to instantiate each item when
|
121
|
+
# selecting a collection of elements.
|
122
|
+
# - <tt>:element</tt>: The XML element name.
|
123
|
+
# - <tt>:object</tt>: A class used to instantiate an element.
|
124
|
+
# - <tt>:type</tt>: The type of the parameter. Should be one of
|
125
|
+
# <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>, or <tt>:date</tt>.
|
93
126
|
def parameter(name, options = {})
|
94
127
|
attr_accessor name
|
95
128
|
@parameters ||= {}
|
96
129
|
@parameters[name] = options
|
97
130
|
end
|
131
|
+
|
132
|
+
def ===(response)
|
133
|
+
response.is_a?(Class) ? response.ancestors.include?(self) : super
|
134
|
+
end
|
98
135
|
end
|
99
136
|
|
100
137
|
private
|
101
138
|
|
139
|
+
# Converts a name to a node name.
|
102
140
|
def node_name(name)
|
103
|
-
name.to_s
|
141
|
+
name.to_s
|
104
142
|
end
|
105
143
|
|
144
|
+
# Gets the XPath expression representing the root node.
|
106
145
|
def root_path(name)
|
107
146
|
"/#{node_name(name)}"
|
108
147
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
require 'date'
|
5
|
+
require 'base64'
|
6
|
+
require 'erb'
|
7
|
+
|
8
|
+
module Relax
|
9
|
+
# Service is the starting point for any REST consumer written with Relax. It
|
10
|
+
# is responsible for setting up the endpoint for the service, and issuing the
|
11
|
+
# HTTP requests for each call made.
|
12
|
+
#
|
13
|
+
# == Extending Service
|
14
|
+
#
|
15
|
+
# When writing consumers, you should start by extending Service by inheriting
|
16
|
+
# from it and calling its constructor with the endpoint for the service.
|
17
|
+
#
|
18
|
+
# === Example
|
19
|
+
#
|
20
|
+
# class Service < Relax::Service
|
21
|
+
# ENDPOINT = 'http://example.com/services/rest/'
|
22
|
+
#
|
23
|
+
# def initialize
|
24
|
+
# super(ENDPOINT)
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# == Calling a Service
|
29
|
+
#
|
30
|
+
# Each call made to the service goes through the #call method of Service,
|
31
|
+
# which takes in a Request object and a Response class. The Request object is
|
32
|
+
# used to generate the query string that will be passed to the endpoint. The
|
33
|
+
# Reponse class is instantiated with the body of the response from the HTTP
|
34
|
+
# request.
|
35
|
+
#
|
36
|
+
# === Example
|
37
|
+
#
|
38
|
+
# This example show how to create a barebones call. This module can be then
|
39
|
+
# included into your Service class.
|
40
|
+
#
|
41
|
+
# module Search
|
42
|
+
# class SearchRequest < Relax::Request
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# class SearchResponse < Relax::Response
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# def search(options = {})
|
49
|
+
# call(SearchRequest.new(options), SearchResponse)
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
class Service
|
54
|
+
attr_reader :endpoint
|
55
|
+
|
56
|
+
# This constructor should be called from your Service with the endpoint URL
|
57
|
+
# for the REST service.
|
58
|
+
def initialize(endpoint)
|
59
|
+
@endpoint = URI::parse(endpoint)
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
# Calls the service using a query built from the Request object passed in
|
65
|
+
# as its first parameter. Once the response comes back from the service,
|
66
|
+
# the body of the response is used to instantiate the response class, and
|
67
|
+
# this response object is returned.
|
68
|
+
def call(request, response_class)
|
69
|
+
uri = @endpoint.clone
|
70
|
+
uri.query = query(request).to_s
|
71
|
+
response = request(uri)
|
72
|
+
puts "Response:\n#{response.body}\n\n" if $DEBUG
|
73
|
+
response_class.new(response.body)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def default_query
|
79
|
+
Query.new
|
80
|
+
end
|
81
|
+
|
82
|
+
def query(request)
|
83
|
+
Query.new(default_query.merge(request.to_query))
|
84
|
+
end
|
85
|
+
|
86
|
+
def request(uri)
|
87
|
+
puts "Request:\n#{uri.to_s}\n\n" if $DEBUG
|
88
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
89
|
+
|
90
|
+
if uri.scheme == 'https'
|
91
|
+
http.use_ssl = true
|
92
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
93
|
+
end
|
94
|
+
|
95
|
+
http.start do |http|
|
96
|
+
request = Net::HTTP::Get.new("#{uri.path}?#{uri.query}")
|
97
|
+
http.request(request)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/relax/symbolic_hash.rb
CHANGED
@@ -1,4 +1,20 @@
|
|
1
1
|
module Relax
|
2
|
+
# SymbolicHash provides an extension of Hash, but one that only supports keys
|
3
|
+
# that are symbols. This has been done in an effort to prevent the case where
|
4
|
+
# both a string key and a symbol key are set on the same hash, and espcially
|
5
|
+
# for dealing with this particular case when convert the hash to a string.
|
6
|
+
#
|
7
|
+
# === Example
|
8
|
+
#
|
9
|
+
# hash = Relax::SymbolicHash.new
|
10
|
+
# hash[:one] = 1
|
11
|
+
# hash['one'] = 2
|
12
|
+
# puts hash[:one] # => 2
|
13
|
+
#
|
14
|
+
# === Credits
|
15
|
+
#
|
16
|
+
# Some of the inspiration (and code) for this class comes from the
|
17
|
+
# HashWithIndifferentAccess that ships with Rails.
|
2
18
|
class SymbolicHash < Hash
|
3
19
|
def initialize(constructor = {})
|
4
20
|
if constructor.is_a?(Hash)
|
@@ -21,8 +37,7 @@ module Relax
|
|
21
37
|
other_hash.each_pair { |key, value| store(convert_key(key), convert_value(value)) }
|
22
38
|
self
|
23
39
|
end
|
24
|
-
|
25
|
-
alias_method :merge!, :update
|
40
|
+
alias :merge! :update
|
26
41
|
|
27
42
|
def fetch(key, *extras)
|
28
43
|
super(convert_key(key), *extras)
|
@@ -47,10 +62,9 @@ module Relax
|
|
47
62
|
def key?(key)
|
48
63
|
super(convert_key(key))
|
49
64
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
alias_method :member?, :key?
|
65
|
+
alias :include? :key?
|
66
|
+
alias :has_key? :key?
|
67
|
+
alias :member? :key?
|
54
68
|
|
55
69
|
protected
|
56
70
|
|
data/lib/relax.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
2
|
|
3
|
-
require 'relax/api'
|
4
|
-
require 'relax/symbolic_hash'
|
5
3
|
require 'relax/query'
|
6
4
|
require 'relax/request'
|
7
5
|
require 'relax/response'
|
6
|
+
require 'relax/service'
|
7
|
+
require 'relax/symbolic_hash'
|
data/spec/response_spec.rb
CHANGED
@@ -15,6 +15,10 @@ XML = <<EOF
|
|
15
15
|
</Tokens>
|
16
16
|
<Status>Success</Status>
|
17
17
|
<RequestId valid="true">44287</RequestId>
|
18
|
+
<Error>
|
19
|
+
<Code>1</Code>
|
20
|
+
<Message>Failed</Message>
|
21
|
+
</Error>
|
18
22
|
</RESTResponse>
|
19
23
|
EOF
|
20
24
|
|
@@ -24,10 +28,16 @@ class TestResponse < Relax::Response
|
|
24
28
|
parameter :status
|
25
29
|
end
|
26
30
|
|
31
|
+
class Error < Relax::Response
|
32
|
+
parameter :code, :type => :integer
|
33
|
+
parameter :message
|
34
|
+
end
|
35
|
+
|
27
36
|
parameter :status
|
28
37
|
parameter :request_id, :element => :requestid, :type => :integer
|
29
38
|
parameter :valid_request, :element => :requestid, :attribute => :valid
|
30
39
|
parameter :tokens, :collection => Token
|
40
|
+
parameter :error, :type => Error
|
31
41
|
end
|
32
42
|
|
33
43
|
describe 'a response' do
|
@@ -38,7 +48,7 @@ describe 'a response' do
|
|
38
48
|
it 'should allow access to the root' do
|
39
49
|
root = @response.root
|
40
50
|
root.should be_an_instance_of(Hpricot::Elem)
|
41
|
-
root.name.should eql('
|
51
|
+
root.name.should eql('RESTResponse')
|
42
52
|
end
|
43
53
|
|
44
54
|
it 'should be checkable by the name of its root' do
|
@@ -66,7 +76,7 @@ describe 'a response' do
|
|
66
76
|
@response.has?(:Errors).should be_nil
|
67
77
|
end
|
68
78
|
|
69
|
-
it 'should be able to define children of
|
79
|
+
it 'should be able to define children of Response without modifying parent' do
|
70
80
|
Relax::Response.new(XML).respond_to?(:status).should be_false
|
71
81
|
TestResponse.new(XML).respond_to?(:status).should be_true
|
72
82
|
end
|
@@ -78,5 +88,11 @@ describe 'a response' do
|
|
78
88
|
response.valid_request.should eql('true')
|
79
89
|
response.tokens.length.should eql(2)
|
80
90
|
response.tokens.first.status.should eql('Active')
|
91
|
+
response.error.code.should eql(1)
|
92
|
+
response.error.message.should eql('Failed')
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should be relationally equivalent to its children' do
|
96
|
+
(Relax::Response === TestResponse).should be_true
|
81
97
|
end
|
82
98
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: relax
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.0.2
|
7
|
+
date: 2007-10-10 00:00:00 -04:00
|
8
8
|
summary: A simple library for creating REST consumers.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -32,8 +32,8 @@ files:
|
|
32
32
|
- lib/relax.rb
|
33
33
|
- lib/relax
|
34
34
|
- lib/relax/response.rb
|
35
|
-
- lib/relax/api.rb
|
36
35
|
- lib/relax/request.rb
|
36
|
+
- lib/relax/service.rb
|
37
37
|
- lib/relax/query.rb
|
38
38
|
- lib/relax/symbolic_hash.rb
|
39
39
|
- README
|
@@ -62,5 +62,5 @@ dependencies:
|
|
62
62
|
requirements:
|
63
63
|
- - ">="
|
64
64
|
- !ruby/object:Gem::Version
|
65
|
-
version: "0.
|
65
|
+
version: "0.6"
|
66
66
|
version:
|
data/lib/relax/api.rb
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
require 'net/https'
|
3
|
-
require 'uri'
|
4
|
-
require 'date'
|
5
|
-
require 'base64'
|
6
|
-
require 'erb'
|
7
|
-
|
8
|
-
module Relax
|
9
|
-
class API
|
10
|
-
attr_reader :endpoint
|
11
|
-
|
12
|
-
def initialize(endpoint)
|
13
|
-
@endpoint = URI::parse(endpoint)
|
14
|
-
end
|
15
|
-
|
16
|
-
private
|
17
|
-
|
18
|
-
def default_query
|
19
|
-
Query.new
|
20
|
-
end
|
21
|
-
|
22
|
-
def query(request)
|
23
|
-
Query.new(default_query.merge(request.to_query))
|
24
|
-
end
|
25
|
-
|
26
|
-
def call(request, response_class)
|
27
|
-
uri = @endpoint.clone
|
28
|
-
uri.query = query(request).to_s
|
29
|
-
response = request(uri)
|
30
|
-
puts "Response:\n#{response.body}\n\n" if $DEBUG
|
31
|
-
response_class.new(response.body)
|
32
|
-
end
|
33
|
-
|
34
|
-
def request(uri)
|
35
|
-
puts "Request:\n#{uri.to_s}\n\n" if $DEBUG
|
36
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
37
|
-
|
38
|
-
if uri.scheme == 'https'
|
39
|
-
http.use_ssl = true
|
40
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
41
|
-
end
|
42
|
-
|
43
|
-
http.start do |http|
|
44
|
-
request = Net::HTTP::Get.new("#{uri.path}?#{uri.query}")
|
45
|
-
http.request(request)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|