relax 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007 Tyler Hunt
1
+ Copyright (c) 2007-2008 Tyler Hunt
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README CHANGED
@@ -1,9 +1,10 @@
1
1
  = Relax
2
2
 
3
- Relax is a simple library for creating REST consumers.
3
+ Relax is a simple library that provides a foundation for writing REST consumer
4
+ APIs, including the logic to handle the HTTP requests, build URLs with query
5
+ parameters, and parse XML responses.
4
6
 
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
+ It provides a basic set of functionality common to most REST consumers:
7
8
 
8
9
  - building HTTP queries (Relax::Request)
9
10
  - issuing HTTP requests (Relax::Service)
@@ -106,8 +107,7 @@ There are three main pieces to every service call module:
106
107
  2. a Relax::Response object
107
108
  3. a call method that calls Relax::Service#call
108
109
 
109
- This module then gets included into the Service class, where the call method
110
- can be easily utilized.
110
+ Here's what the PhotoSearch module looks like:
111
111
 
112
112
  module Flickr
113
113
  module PhotoSearch
@@ -137,8 +137,23 @@ can be easily utilized.
137
137
 
138
138
  As you can see, we have our request (PhotoSearchRequest), response
139
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.
140
+ find_by_tag). This now needs to be included into our Flickr::Service class,
141
+ and then we'll be able to use it by calling either of the call methods.
142
+
143
+ module Flickr
144
+ class Service < Relax::Service
145
+ include Flickr::PhotoSearch
146
+
147
+ ENDPOINT = 'http://api.flickr.com/services/rest/'
148
+
149
+ def initialize(api_key)
150
+ super(ENDPOINT)
151
+ Request[:api_key] = api_key
152
+ end
153
+ end
154
+ end
155
+
156
+ Now we're ready to make a call against the API:
142
157
 
143
158
  flickr = Flickr::Service.new(ENV['FLICKR_API_KEY'])
144
159
  relax = flickr.find_by_tag('relax', :per_page => 10)
@@ -151,3 +166,6 @@ can use it by calling either of the call methods.
151
166
 
152
167
  This will output the IDs and titles for the first 10 photos on Flickr that have
153
168
  the tag "relax."
169
+
170
+
171
+ Copyright (c) 2007-2008 Tyler Hunt, released under the MIT license
data/lib/relax.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
2
 
3
3
  require 'relax/query'
4
+ require 'relax/parsers'
4
5
  require 'relax/request'
5
6
  require 'relax/response'
6
7
  require 'relax/service'
@@ -8,4 +9,5 @@ require 'relax/symbolic_hash'
8
9
 
9
10
  module Relax
10
11
  class MissingParameter < ArgumentError ; end
12
+ class UnrecognizedParser < ArgumentError ; end
11
13
  end
@@ -0,0 +1,13 @@
1
+ require 'date'
2
+ require 'time'
3
+
4
+ require 'relax/parsers/factory'
5
+ require 'relax/parsers/base'
6
+
7
+ require 'relax/parsers/hpricot'
8
+ require 'relax/parsers/rexml'
9
+
10
+ module Relax
11
+ module Parsers
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ module Relax
2
+ module Parsers
3
+
4
+ class Base
5
+
6
+ attr_reader :parent
7
+ attr_reader :parameters
8
+
9
+ def initialize(raw, parent)
10
+ @parent = parent
11
+ @parameters = parent.class.instance_variable_get('@parameters')
12
+ parse!
13
+ end
14
+
15
+ def parse!; end
16
+
17
+ def root; end
18
+ def is?(name); end
19
+ def has?(name); end
20
+ def element(name); end
21
+ def elements(name); end
22
+
23
+ def attribute(element, name); end
24
+ def value(value); end
25
+ def text_value(value); end
26
+ def integer_value(value); end
27
+ def float_value(value); end
28
+ def date_value(value); end
29
+ def time_value(value); end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ module Relax
2
+ module Parsers
3
+
4
+ ##
5
+ # Manages the Relax::Parsers in the library.
6
+ #
7
+ module Factory
8
+
9
+ class << self
10
+
11
+ ##
12
+ # Returns the parser class which has been registered for the given
13
+ # +name+.
14
+ #
15
+ def get(name)
16
+ @@parsers ||= {}
17
+ @@parsers[name] || raise(UnrecognizedParser, "Given parser name not recognized: #{name.inspect}. Expected one of: #{@@parsers.keys.inspect}")
18
+ end
19
+
20
+ ##
21
+ # Registers a new parser with the factory. The +name+ should be unique,
22
+ # but if not, it will override the previously defined parser for the
23
+ # given +name+.
24
+ #
25
+ def register(name, klass)
26
+ @@parsers ||= {}
27
+ @@parsers[:default] = klass if @@parsers.empty?
28
+ @@parsers[name] = klass
29
+ end
30
+
31
+ ##
32
+ # Removes all registered parsers from the factory.
33
+ #
34
+ def clear!
35
+ @@parsers = {}
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,145 @@
1
+ require 'rubygems'
2
+ require 'hpricot'
3
+
4
+ module Relax
5
+ module Parsers
6
+
7
+ ##
8
+ # Parses the server's raw response using the Hpricot library.
9
+ #
10
+ class Hpricot < Base
11
+
12
+ FACTORY_NAME = :hpricot
13
+
14
+ def initialize(raw, parent)
15
+ @xml = ::Hpricot.XML(raw)
16
+ super(raw, parent)
17
+ end
18
+
19
+ def parse!
20
+ if parameters
21
+ parameters.each do |parameter, options|
22
+ begin
23
+ element = options[:element] || parameter
24
+
25
+ if attribute = options[:attribute] and attribute == true
26
+ node = attribute(root, element)
27
+ elsif attribute
28
+ node = attribute(element(element), attribute)
29
+ elsif options[:collection]
30
+ node = elements(element)
31
+ else
32
+ node = element(element)
33
+ end
34
+
35
+ if options[:collection]
36
+ value = node.collect do |element|
37
+ options[:collection].new(element)
38
+ end
39
+ else
40
+ case type = options[:type]
41
+ when Response
42
+ value = type.new(node)
43
+
44
+ when :date
45
+ value = date_value(node)
46
+
47
+ when :time
48
+ value = time_value(node)
49
+
50
+ when :float
51
+ value = float_value(node)
52
+
53
+ when :integer
54
+ value = integer_value(node)
55
+
56
+ when :text
57
+ else
58
+ value = text_value(node)
59
+ end
60
+ end
61
+
62
+ parent.instance_variable_set("@#{parameter}", value)
63
+ rescue ::Hpricot::Error
64
+ raise Relax::MissingParameter if options[:required]
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # Returns the root of the XML document.
71
+ def root
72
+ @xml.root
73
+ end
74
+
75
+ # Checks the name of the root node.
76
+ def is?(name)
77
+ root.name.gsub(/.*:(.*)/, '\1') == node_name(name)
78
+ end
79
+
80
+ # Returns a set of elements matching name.
81
+ def elements(name)
82
+ root.search(root_path(name))
83
+ end
84
+
85
+ # Returns an element of the specified name.
86
+ def element(name)
87
+ root.at(root_path(name))
88
+ end
89
+ alias :has? :element
90
+
91
+ # Returns an attribute on an element.
92
+ def attribute(element, name)
93
+ element[name]
94
+ end
95
+
96
+ # Gets the value of an element or attribute.
97
+ def value(value)
98
+ value.is_a?(::Hpricot::Elem) ? value.inner_text : value.to_s
99
+ end
100
+
101
+ # Gets a text value.
102
+ def text_value(value)
103
+ value(value)
104
+ end
105
+
106
+ # Gets an integer value.
107
+ def integer_value(value)
108
+ value(value).to_i
109
+ end
110
+
111
+ # Gets a float value.
112
+ def float_value(value)
113
+ value(value).to_f
114
+ end
115
+
116
+ # Gets a date value.
117
+ def date_value(value)
118
+ Date.parse(value(value))
119
+ end
120
+
121
+ # Gets a time value.
122
+ def time_value(value)
123
+ Time.parse(value(value))
124
+ end
125
+
126
+
127
+ private
128
+
129
+
130
+ # Converts a name to a node name.
131
+ def node_name(name)
132
+ name.to_s
133
+ end
134
+
135
+ # Gets the XPath expression representing the root node.
136
+ def root_path(name)
137
+ "/#{node_name(name)}"
138
+ end
139
+
140
+ end
141
+
142
+ Factory.register(Hpricot::FACTORY_NAME, Hpricot)
143
+
144
+ end
145
+ end
@@ -0,0 +1,158 @@
1
+ require 'rubygems'
2
+ require 'rexml/document'
3
+
4
+ module Relax
5
+ module Parsers
6
+
7
+ ##
8
+ # Parsers the server's response using the REXML library.
9
+ #
10
+ # Benefits:
11
+ #
12
+ # * XML Namespace support (parameter :foo, :namespace => 'bar')
13
+ #
14
+ # Drawbacks:
15
+ #
16
+ # * Case sensitive field names (<Status>..</> != parameter :status)
17
+ #
18
+ class REXML < Base
19
+
20
+ FACTORY_NAME = :rexml
21
+
22
+ def initialize(raw, parent)
23
+ @xml = ::REXML::Document.new(raw)
24
+ super(raw, parent)
25
+ end
26
+
27
+ def parse!
28
+ if parameters
29
+ parameters.each do |parameter, options|
30
+ begin
31
+ element = options[:element] || parameter
32
+ namespace = options[:namespace]
33
+
34
+ if attribute = options[:attribute] and attribute == true
35
+ node = attribute(root, element, namespace)
36
+ elsif attribute
37
+ node = attribute(element(element), attribute, namespace)
38
+ elsif options[:collection]
39
+ node = elements(element, namespace)
40
+ else
41
+ node = element(element, namespace)
42
+ end
43
+
44
+ if options[:collection]
45
+ value = node.collect do |element|
46
+ options[:collection].new(element.deep_clone)
47
+ end
48
+ else
49
+ case type = options[:type]
50
+ when Response
51
+ value = type.new(node)
52
+
53
+ when :date
54
+ value = date_value(node)
55
+
56
+ when :time
57
+ value = time_value(node)
58
+
59
+ when :float
60
+ value = float_value(node)
61
+
62
+ when :integer
63
+ value = integer_value(node)
64
+
65
+ when :text
66
+ else
67
+ value = text_value(node)
68
+ end
69
+ end
70
+
71
+ parent.instance_variable_set("@#{parameter}", value)
72
+ rescue
73
+ raise(Relax::MissingParameter) if node.nil? && options[:required]
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ # Returns the root of the XML document.
80
+ def root
81
+ @xml.root
82
+ end
83
+
84
+ # Checks the name of the root node.
85
+ def is?(name, namespace = nil)
86
+ root.name == node_name(name, nil)
87
+ end
88
+
89
+ # Returns a set of elements matching name.
90
+ def elements(name, namespace = nil)
91
+ root.get_elements(node_path(name, namespace))
92
+ end
93
+
94
+ # Returns an element of the specified name.
95
+ def element(name, namespace = nil)
96
+ root.elements[node_path(name, namespace)]
97
+ end
98
+ alias :has? :element
99
+
100
+ # Returns an attribute on an element.
101
+ def attribute(element, name, namespace = nil)
102
+ element.attribute(name)
103
+ end
104
+
105
+ # Gets the value of an element or attribute.
106
+ def value(value)
107
+ value.is_a?(::REXML::Element) ? value.text : value.to_s
108
+ end
109
+
110
+ # Gets a text value.
111
+ def text_value(value)
112
+ value(value)
113
+ end
114
+
115
+ # Gets an integer value.
116
+ def integer_value(value)
117
+ value(value).to_i
118
+ end
119
+
120
+ # Gets a float value.
121
+ def float_value(value)
122
+ value(value).to_f
123
+ end
124
+
125
+ # Gets a date value.
126
+ def date_value(value)
127
+ Date.parse(value(value))
128
+ end
129
+
130
+ # Gets a time value.
131
+ def time_value(value)
132
+ Time.parse(value(value))
133
+ end
134
+
135
+
136
+ private
137
+
138
+
139
+ # Converts a name to a node name.
140
+ def node_name(name, namespace = nil)
141
+ "#{namespace.to_s + ':' if namespace}#{name}"
142
+ end
143
+
144
+ # Gets the XPath expression representing the root node.
145
+ def root_path(name)
146
+ "/#{node_name(name)}"
147
+ end
148
+
149
+ def node_path(name, namespace = nil)
150
+ "#{node_name(name, namespace)}"
151
+ end
152
+
153
+ end
154
+
155
+ Factory.register(REXML::FACTORY_NAME, REXML)
156
+
157
+ end
158
+ end
data/lib/relax/query.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'erb'
1
+ require 'cgi'
2
2
  require 'uri'
3
3
 
4
4
  require 'relax/symbolic_hash'
@@ -18,8 +18,8 @@ module Relax
18
18
  # Parses a URL and returns a Query with its query portion.
19
19
  def parse(uri)
20
20
  query = uri.query.split('&').inject({}) do |query, parameter|
21
- key, value = parameter.split('=')
22
- query[key] = unescape_value(value)
21
+ key, value = parameter.split('=', 2)
22
+ query[unescape_value(key)] = unescape_value(value)
23
23
  query
24
24
  end
25
25
  self.new(query)
@@ -27,12 +27,12 @@ module Relax
27
27
 
28
28
  # Escapes a query parameter value.
29
29
  def escape_value(value)
30
- ERB::Util.url_encode(value.to_s).gsub('%20', '+')
30
+ CGI.escape(value.to_s).gsub('%20', '+')
31
31
  end
32
32
 
33
33
  # Unescapes a query parameter value.
34
34
  def unescape_value(value)
35
- URI.unescape(value)
35
+ CGI.unescape(value)
36
36
  end
37
37
  end
38
38
 
data/lib/relax/request.rb CHANGED
@@ -57,7 +57,7 @@ module Relax
57
57
  # the parameter name separated by two underscores. This method can be
58
58
  # overridden by child classes.
59
59
  def convert_complex_key(key, parameter)
60
- "#{key}.#{parameter}"
60
+ "#{convert_key(key)}.#{convert_key(parameter)}"
61
61
  end
62
62
  protected :convert_complex_key
63
63
 
@@ -1,8 +1,3 @@
1
- require 'rubygems'
2
- require 'hpricot'
3
-
4
- require 'date'
5
-
6
1
  module Relax
7
2
  # Response is intended to be a parent class for responses passed to
8
3
  # Service#call.
@@ -12,122 +7,26 @@ module Relax
12
7
  # #element and #attribute.
13
8
  class Response
14
9
  attr_accessor :raw
15
- attr_accessor :xml
16
-
17
- # New takes in the XML from the response. For the initial response, this
18
- # will be the root element, but child elements may also be passed into
19
- # Response objects.
10
+
11
+ # New takes in and parses the raw response.
20
12
  #
21
13
  # This will raise a MissingParameter error if a parameterd marked as
22
- # required is not present in the XML response.
14
+ # required is not present in the parsed response.
23
15
  def initialize(xml)
24
- @raw = xml
25
- @xml = Hpricot.XML(xml.to_s)
26
-
27
- if parameters = self.class.instance_variable_get('@parameters')
28
- parameters.each do |parameter, options|
29
- begin
30
- element = options[:element] || parameter
31
-
32
- if attribute = options[:attribute] and attribute == true
33
- node = attribute(root, element)
34
- elsif attribute
35
- node = attribute(element(element), attribute)
36
- elsif options[:collection]
37
- node = elements(element)
38
- else
39
- node = element(element)
40
- end
41
-
42
- if options[:collection]
43
- value = node.collect do |element|
44
- options[:collection].new(element)
45
- end
46
- else
47
- case type = options[:type]
48
- when Response
49
- value = type.new(node)
50
-
51
- when :date
52
- value = date_value(node)
53
-
54
- when :time
55
- value = time_value(node)
56
-
57
- when :float
58
- value = float_value(node)
59
-
60
- when :integer
61
- value = integer_value(node)
62
-
63
- when :text
64
- else
65
- value = text_value(node)
66
- end
67
- end
68
-
69
- instance_variable_set("@#{parameter}", value)
70
- rescue Hpricot::Error
71
- raise MissingParameter if options[:required]
72
- end
73
- end
74
- end
75
- end
76
-
77
- # Returns the root of the XML document.
78
- def root
79
- @xml.root
16
+ @raw = xml
17
+ @parser = Relax::Parsers::Factory.get(parser_name).new(xml.to_s, self)
80
18
  end
81
-
82
- # Checks the name of the root node.
83
- def is?(name)
84
- root.name.gsub(/.*:(.*)/, '\1') == node_name(name)
85
- end
86
-
87
- # Returns an element of the specified name.
88
- def element(name)
89
- root.at(root_path(name))
90
- end
91
- alias :has? :element
92
-
93
- # Returns an attribute on an element.
94
- def attribute(element, name)
95
- element[name]
19
+
20
+ def parser_name
21
+ self.class.instance_variable_get('@parser') || :default
96
22
  end
97
-
98
- # Returns a set of elements matching name.
99
- def elements(name)
100
- root.search(root_path(name))
101
- end
102
-
103
- # Gets the value of an element or attribute.
104
- def value(value)
105
- value.is_a?(Hpricot::Elem) ? value.inner_text : value.to_s
106
- end
107
-
108
- # Gets a text value.
109
- def text_value(value)
110
- value(value)
111
- end
112
-
113
- # Gets an integer value.
114
- def integer_value(value)
115
- value(value).to_i
116
- end
117
-
118
- # Gets a float value.
119
- def float_value(value)
120
- value(value).to_f
121
- end
122
-
123
- # Gets a date value.
124
- def date_value(value)
125
- Date.parse(value(value))
126
- end
127
-
128
- # Gets a time value.
129
- def time_value(value)
130
- Time.parse(value(value))
23
+
24
+ def method_missing(method, *args) #:nodoc:
25
+ if @parser.respond_to?(method)
26
+ @parser.__send__(method, *args)
27
+ else
28
+ super
29
+ end
131
30
  end
132
31
 
133
32
  class << self
@@ -140,6 +39,7 @@ module Relax
140
39
  @parameters.each do |name, options|
141
40
  subclass.parameter(name, options)
142
41
  end if @parameters
42
+ subclass.parser(@parser) if @parser
143
43
  end
144
44
 
145
45
  # Specifes a parameter that will be automatically parsed when the
@@ -159,22 +59,20 @@ module Relax
159
59
  @parameters ||= {}
160
60
  @parameters[name] = options
161
61
  end
62
+
63
+ # Specifies the parser to use when decoding the server response. If
64
+ # no parser is specified for the response, then the default parser will
65
+ # be used.
66
+ #
67
+ # See Relax::Parsers for a list of available parsers.
68
+ def parser(name)
69
+ @parser ||= name
70
+ end
162
71
 
163
72
  def ===(response)
164
73
  response.is_a?(Class) ? response.ancestors.include?(self) : super
165
74
  end
166
75
  end
167
76
 
168
- private
169
-
170
- # Converts a name to a node name.
171
- def node_name(name)
172
- name.to_s
173
- end
174
-
175
- # Gets the XPath expression representing the root node.
176
- def root_path(name)
177
- "/#{node_name(name)}"
178
- end
179
77
  end
180
78
  end
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ require 'relax'
4
+ require 'relax/parsers/factory'
5
+
6
+ class TestParser ; end
7
+
8
+ describe 'a parser factory' do
9
+
10
+ before(:each) do
11
+ @factory = Relax::Parsers::Factory
12
+ Relax::Parsers::Factory.register(:test, TestParser)
13
+ end
14
+
15
+ it 'should raise UnrecognizedParser for un-registered names' do
16
+ lambda {
17
+ @factory.get(:bad_name)
18
+ }.should raise_error(Relax::UnrecognizedParser)
19
+ end
20
+
21
+ it 'should return a registered parser class' do
22
+ @factory.get(:test).should ==TestParser
23
+ end
24
+
25
+ it 'should register the first registered parser as the default' do
26
+ @factory.get(:default).should ==Relax::Parsers::Hpricot
27
+ end
28
+
29
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/../parser_helper'
3
+
4
+
5
+ class HpricotTestResponse < Relax::Response
6
+ class Token < Relax::Response
7
+ parser :hpricot
8
+ parameter :token_id, :element => :tokenid
9
+ parameter :status
10
+ end
11
+
12
+ class Error < Relax::Response
13
+ parser :hpricot
14
+ parameter :code, :type => :integer
15
+ parameter :message
16
+ end
17
+
18
+ parser :hpricot
19
+ parameter :status, :required => true
20
+ parameter :request_id, :element => :requestid, :type => :integer
21
+ parameter :valid_request, :element => :requestid, :attribute => :valid
22
+ parameter :tokens, :collection => Token
23
+ parameter :error, :type => Error
24
+ end
25
+
26
+
27
+ describe 'an Hpricot parser' do
28
+
29
+ before(:each) do
30
+ @response = HpricotTestResponse.new(XML)
31
+ end
32
+
33
+ it_should_behave_like 'a successfully parsed response'
34
+
35
+ end
@@ -0,0 +1,40 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require File.dirname(__FILE__) + '/../parser_helper'
3
+
4
+
5
+ class RexmlTestResponse < Relax::Response
6
+ class Token < Relax::Response
7
+ parser :rexml
8
+ parameter :token_id, :element => 'TokenId'
9
+ parameter :status, :element => 'Status'
10
+ end
11
+
12
+ class Error < Relax::Response
13
+ parser :rexml
14
+ parameter :code, :element => 'Code', :type => :integer
15
+ parameter :message, :element => 'Message'
16
+ end
17
+
18
+ parser :rexml
19
+ parameter :status, :element => 'Status', :required => true
20
+ parameter :request_id, :element => 'RequestId', :type => :integer
21
+ parameter :valid_request, :element => 'RequestId', :attribute => :valid
22
+ parameter :namespace, :element => 'Namespace', :namespace => 'ns1'
23
+ parameter :tokens, :element => 'Tokens', :collection => Token
24
+ parameter :error, :element => 'Error', :type => Error
25
+ end
26
+
27
+
28
+ describe 'a REXML parser' do
29
+
30
+ before(:each) do
31
+ @response = RexmlTestResponse.new(XML)
32
+ end
33
+
34
+ it_should_behave_like 'a successfully parsed response'
35
+
36
+ it 'should parse namespaced parameters' do
37
+ @response.namespace.should eql('Passed')
38
+ end
39
+
40
+ end
data/spec/query_spec.rb CHANGED
@@ -23,7 +23,7 @@ describe 'a query' do
23
23
  it 'should escape its values using "+" instead of "%20"' do
24
24
  Relax::Query.send(:escape_value, 'two words').should == 'two+words'
25
25
  end
26
-
26
+
27
27
  it 'should sort its parameters' do
28
28
  @query[:charlie] = 3
29
29
  @query[:alpha] = 1
@@ -42,4 +42,19 @@ describe 'a query' do
42
42
  parsed_query[:action].should eql('search')
43
43
  parsed_query[:query].should eql('keyword')
44
44
  end
45
+
46
+ it 'should parse key value pairs into only two parts' do
47
+ parsed_query = Relax::Query.parse(URI.parse("http://example.com/?action=test=&foo=bar"))
48
+ parsed_query[:action].should eql('test=')
49
+ end
50
+
51
+ it 'should unescape query string key-value pair keys' do
52
+ parsed_query = Relax::Query.parse(URI.parse("http://example.com/?action%20helper=test"))
53
+ parsed_query[:"action helper"].should eql('test')
54
+ end
55
+
56
+ it 'should unescape query string key-value pair values' do
57
+ parsed_query = Relax::Query.parse(URI.parse("http://example.com/?action=test%20action"))
58
+ parsed_query[:action].should eql('test action')
59
+ end
45
60
  end
@@ -1,26 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
- require 'relax/response'
4
-
5
- XML = <<EOF
6
- <?xml version="1.0"?>
7
- <RESTResponse>
8
- <Tokens>
9
- <TokenId>JPMQARDVJK</TokenId>
10
- <Status>Active</Status>
11
- </Tokens>
12
- <Tokens>
13
- <TokenId>RDVJKJPMQA</TokenId>
14
- <Status>Inactive</Status>
15
- </Tokens>
16
- <Status>Success</Status>
17
- <RequestId valid="true">44287</RequestId>
18
- <Error>
19
- <Code>1</Code>
20
- <Message>Failed</Message>
21
- </Error>
22
- </RESTResponse>
23
- EOF
24
3
 
25
4
  class BaseResponse < Relax::Response
26
5
  parameter :status, :required => true
@@ -34,6 +13,7 @@ class TestResponse < BaseResponse
34
13
  end
35
14
 
36
15
  class Error < Relax::Response
16
+ parser :hpricot
37
17
  parameter :code, :type => :integer
38
18
  parameter :message
39
19
  end
@@ -43,6 +23,7 @@ class TestResponse < BaseResponse
43
23
  parameter :error, :type => Error
44
24
  end
45
25
 
26
+
46
27
  describe 'a response' do
47
28
  before(:each) do
48
29
  @response = Relax::Response.new(XML)
@@ -106,4 +87,12 @@ describe 'a response' do
106
87
  it 'should raise MissingParameter if required parameters are missing' do
107
88
  proc { TestResponse.new('') }.should raise_error(Relax::MissingParameter)
108
89
  end
90
+
91
+ it 'should use the default parser when undefined' do
92
+ TestResponse::Token.new('').parser_name.should ==:default
93
+ end
94
+
95
+ it 'should use the defined parser when given' do
96
+ TestResponse::Error.new('').parser_name.should ==:hpricot
97
+ end
109
98
  end
metadata CHANGED
@@ -1,66 +1,90 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
3
- specification_version: 1
4
2
  name: relax
5
3
  version: !ruby/object:Gem::Version
6
- version: 0.0.4
7
- date: 2007-11-17 00:00:00 -05:00
8
- summary: A simple library for creating REST consumers.
9
- require_paths:
10
- - lib
11
- email: tyler@protoh.com
12
- homepage: http://protoh.com/
13
- rubyforge_project:
14
- description:
15
- autorequire: relax
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 0.0.5
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Tyler Hunt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-20 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hpricot
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0.6"
24
+ version:
25
+ description:
26
+ email: tyler@tylerhunt.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ - LICENSE
31
34
  files:
32
35
  - lib/relax
36
+ - lib/relax/parsers
37
+ - lib/relax/parsers/base.rb
38
+ - lib/relax/parsers/factory.rb
39
+ - lib/relax/parsers/hpricot.rb
40
+ - lib/relax/parsers/rexml.rb
41
+ - lib/relax/parsers.rb
33
42
  - lib/relax/query.rb
34
43
  - lib/relax/request.rb
35
44
  - lib/relax/response.rb
36
45
  - lib/relax/service.rb
37
46
  - lib/relax/symbolic_hash.rb
38
47
  - lib/relax.rb
39
- - README
40
- - LICENSE
41
- test_files:
48
+ - spec/parsers/factory_spec.rb
49
+ - spec/parsers/hpricot_spec.rb
50
+ - spec/parsers/rexml_spec.rb
42
51
  - spec/query_spec.rb
43
52
  - spec/request_spec.rb
44
53
  - spec/response_spec.rb
45
54
  - spec/symbolic_hash_spec.rb
46
- rdoc_options: []
47
-
48
- extra_rdoc_files:
49
55
  - README
50
56
  - LICENSE
51
- executables: []
52
-
53
- extensions: []
57
+ has_rdoc: true
58
+ homepage: http://tylerhunt.com/
59
+ post_install_message:
60
+ rdoc_options: []
54
61
 
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
55
76
  requirements: []
56
77
 
57
- dependencies:
58
- - !ruby/object:Gem::Dependency
59
- name: hpricot
60
- version_requirement:
61
- version_requirements: !ruby/object:Gem::Version::Requirement
62
- requirements:
63
- - - ">="
64
- - !ruby/object:Gem::Version
65
- version: "0.6"
66
- version:
78
+ rubyforge_project: relax
79
+ rubygems_version: 1.2.0
80
+ signing_key:
81
+ specification_version: 2
82
+ summary: A simple library for creating REST consumers.
83
+ test_files:
84
+ - spec/parsers/factory_spec.rb
85
+ - spec/parsers/hpricot_spec.rb
86
+ - spec/parsers/rexml_spec.rb
87
+ - spec/query_spec.rb
88
+ - spec/request_spec.rb
89
+ - spec/response_spec.rb
90
+ - spec/symbolic_hash_spec.rb