Class: Consumer::Request

Inherits:
Object
  • Object
show all
Includes:
Consumer
Defined in:
lib/consumer/request.rb

Overview

Class Attrubutes

required

Defines attributes that must be present in any instance before calling do. Anything defined here but not set in the instance will raise a RequiredFieldError on calling do

Defaluts to []

response_class

String for setting the class the request will use to parse the response.

Defaults to [Something]Request, ex. for a RateRequest, this would default to “Rate”.

Note that the instance method with the same name returns a constant rather than the string set here.

yaml_defaults

Consists of two parameters. In order:

  • The location (as a string) for a yaml file containing attribute defaults.

  • A namespace to grab the defaults out of.

    If your yaml looked like this:

    <pre> ups:

    user_id: Woody
    

    usps:

    user_id: John
    

    </pre>

    UPSRequest would want to use “ups” as the namespace value, where USPSRequest would want to use “usps”

    This is optional and has no default

error_paths

If you define this in your subclasses to return a hash for error options (see “Options” below) they will raise informative RequestError errors with xml error info in the message.

If this is left undefined and the remote server returns error xml rather than what you were expecting, you’ll get generic xml parsing errors instead of something informative.

Note: currently only handles one error.

Options

All options are xpaths. All options except :root are relative to the root (unless prefixed with “//”)

  • :root - Root element of the error(s)

  • :code - Remote API’s error code for this error

  • :message - Informative part of the error

Example

<pre>

:root => "//Error",
:code => "ErrorCode",
:message => "LongDescription"

</pre>

Anything passed in to initialize will override these, though.

Defined Under Namespace

Classes: RequestError, RequiredFieldError

Constant Summary

Constants included from Consumer

VERSION

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs = {}) ⇒ Request

First gets defaults from self.defaults, merges them with defaults from yaml_defaults, merges all that with passed in attrs, and initializes all those into instance variables for use in to_xml.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/consumer/request.rb', line 127

def initialize(attrs = {})
  # it's really handy to have all the other attrs init'd when we call
  # self.defaults 'cuz we can use them to help define conditional defaults.
  root = self.config_root
  yaml = Helper.hash_from_yaml(root, *yaml_defaults)
  yaml, attrs = symbolize_keys(yaml, attrs)

  initialize_attrs(yaml.merge(attrs)) # load yaml, but attrs will overwrite dups

  # now self.defaults has access to above stuff
  class_defaults = self.defaults
  class_defaults = symbolize_keys(class_defaults).first

  # but we wanted defaults loaded first.
  all_defaults = class_defaults.merge(yaml)
  final_attrs = all_defaults.merge(attrs)
  initialize_attrs(final_attrs)
end

Instance Attribute Details

#request_xmlObject (readonly)

Returns the value of attribute request_xml.



122
123
124
# File 'lib/consumer/request.rb', line 122

def request_xml
  @request_xml
end

#response_xmlObject (readonly)

Returns the value of attribute response_xml.



122
123
124
# File 'lib/consumer/request.rb', line 122

def response_xml
  @response_xml
end

Class Method Details

.basic_authentication(*args) ⇒ Object



73
74
75
# File 'lib/consumer/request.rb', line 73

def basic_authentication(*args)
	@request_user, @request_pass = args if !args.empty?
end

.defaults(defaults = nil) ⇒ Object



108
109
110
111
# File 'lib/consumer/request.rb', line 108

def defaults(defaults = nil)
  @defaults = defaults if defaults
  @defaults || {}
end

.do(args = {}) ⇒ Object



187
188
189
# File 'lib/consumer/request.rb', line 187

def self.do(args = {})
  self.new(args).do
end

.error_paths(options = nil) ⇒ Object



113
114
115
116
# File 'lib/consumer/request.rb', line 113

def error_paths(options = nil)
  @error_paths = options if options
  @error_paths
end

.request_pass(pass = nil) ⇒ Object



82
83
84
85
# File 'lib/consumer/request.rb', line 82

def request_pass(pass = nil)
	@request_pass = pass if pass
	@request_pass
end

.request_user(user = nil) ⇒ Object



77
78
79
80
# File 'lib/consumer/request.rb', line 77

def request_user(user = nil)
	@request_user = user if user
	@request_user
end

.required(*args) ⇒ Object



92
93
94
95
# File 'lib/consumer/request.rb', line 92

def required(*args)
  @required = args if !args.empty?
  @required || []
end

.response_class(klass = nil) ⇒ Object



97
98
99
100
101
# File 'lib/consumer/request.rb', line 97

def response_class(klass = nil)
  @response_class = klass if klass
  self.to_s =~ /(.+?)Request/
  @response_class || $1
end

.url(url = nil) ⇒ Object



87
88
89
90
# File 'lib/consumer/request.rb', line 87

def url(url = nil)
  @url = url if url
  @url
end

.yaml_defaults(*args) ⇒ Object



103
104
105
106
# File 'lib/consumer/request.rb', line 103

def yaml_defaults(*args)
  @yaml_defaults = args if !args.empty?
  @yaml_defaults
end

Instance Method Details

#config_rootObject



245
246
247
248
249
250
251
# File 'lib/consumer/request.rb', line 245

def config_root
  if defined?(RAILS_ROOT)
    RAILS_ROOT + "/config"
  else
    "config"
  end
end

#defaultsObject



241
242
243
# File 'lib/consumer/request.rb', line 241

def defaults
  self.class.defaults
end

#doObject

Sends self.to_xml to self.url and returns new object(s) created via [response_class].from_xml

Prerequisites

  • All attributes in self.required must exist; raises RequiredFieldError otherwise

  • self.to_xml must be defined; RuntimeError otherwise

  • self.url must be set; RuntimeError otherwise

  • response_class.from_xml must be defined; RuntimeError otherwise

Returns

Whatever response_class.from_xml(@response_xml) returns, which should be an object or array of objects (an array of objects if response_class is using Consumer::Mapping)



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/consumer/request.rb', line 158

def do
  return if defined?(self.abort?) && self.abort?
  raise "to_xml not defined for #{self.class}" if not defined?(self.to_xml)
  raise "url not defined for #{self.class}" if not self.url
  raise "from_xml not defined for #{response_class}" if not defined?(response_class.from_xml)
 
  @request_xml = self.to_xml_etc
  
  head = defined?(self.headers) ? self.headers : {}

resp = nil

uri = URI.parse self.url

Net::HTTP.start(uri.host,uri.port) {|http|
	req = Net::HTTP::Get.new(uri.request_uri)
	req.basic_auth request_user, request_pass if request_user and request_pass
	req.body = @request_xml
	resp = http.request(req)
}
  
  @response_xml = resp.body
  puts "\n##### Response:\n\n#{Helper.tidy(@response_xml)}\n" if $DEBUG
 
  check_request_error(@response_xml)
 
  return response_class.from_xml(@response_xml)
end

#error_pathsObject



225
226
227
# File 'lib/consumer/request.rb', line 225

def error_paths
  self.class.error_paths
end

#request_passObject



221
222
223
# File 'lib/consumer/request.rb', line 221

def request_pass
	self.class.request_pass
end

#request_userObject



217
218
219
# File 'lib/consumer/request.rb', line 217

def request_user
	self.class.request_user
end

#requiredObject



229
230
231
# File 'lib/consumer/request.rb', line 229

def required
  self.class.required
end

#response_classObject

returns self.class.response_class as a constant (not a string)

Raises a runtime error if self.class.response_class is nil



211
212
213
214
215
# File 'lib/consumer/request.rb', line 211

def response_class
  ret = self.class.response_class
  raise "Invalid response_class; see docs for naming conventions etc" if !ret
  return Object.const_get(ret)
end

#to_xml_etcObject

Gets called during do instead of just to_xml, and does a bit more than just return xml.

First, it calls before_to_xml if it has been defined. Then it calls check_required, then returns the results of to_xml sans empty nodes (see Helper.compact_xml).

You can set a COMPACT_XML constant to false to avoid the latter behavior, but most APIs complain when you send them empty nodes (even if the nodes were optional to begin with).



201
202
203
204
205
206
# File 'lib/consumer/request.rb', line 201

def to_xml_etc
  self.before_to_xml if defined?(before_to_xml)
  self.check_required
  xml = self.to_xml
  return (defined?(COMPACT_XML) && !COMPACT_XML) ? xml : Helper.compact_xml(xml)
end

#urlObject



237
238
239
# File 'lib/consumer/request.rb', line 237

def url
  self.class.url
end

#yaml_defaultsObject



233
234
235
# File 'lib/consumer/request.rb', line 233

def yaml_defaults
  self.class.yaml_defaults
end