Class: Restr

Inherits:
Object
  • Object
show all
Defined in:
lib/restr.rb

Overview

A very simple REST client, best explained by example:

# Retrieve a Kitten and print its name and colour
kitten = Restr.get('http://example.com/kittens/1.xml')
puts kitten['name']
puts kitten['colour']

# Create a Kitten
kitten = Restr.post('http://example.com/kittens.xml', 
  :name => 'batman', :colour => 'black')

# Update a Kitten
kitten = Restr.put('http://example.com/kittens/1.xml', 
  :age => '6 months')

# Delete a Kitten :(
kitten = Restr.delete('http://example.com/kittens/1.xml')

# Retrieve a list of Kittens
kittens = Restr.get('http://example.com/kittens.xml')

When the response to a Restr request has content type ‘text/xml’, the response body will be parsed from XML into a nested Hash (using XmlSimple – see xml-simple.rubyforge.org/). Otherwise the response is

returned untouched, as a String.

Authentication

If the remote REST resource requires authentication (Restr only supports HTTP Basic authentication, for now):

Restr.get('http://example.com/kittens/1.xml, {}, 
  {:username => 'foo', :password => 'bar'})

Logging

A standard Ruby Logger can be attached to the Restr client like so:

logger = Logger.new('restr.log')
logger.level = Logger::DEBUG
Restr.logger = logger

Restr will now log its activity to the given Logger. The default_logger can be overridden by supplying a :logger option to a client call:

kitten_logger = Logger.new('kitten.log'}
Restr.get('http://example.com/kittens/1.xml, {}, 
  {:logger => kitten_logger)

Defined Under Namespace

Modules: VERSION Classes: InvalidRequestMethod

Constant Summary collapse

@@logger =
nil
@@request_timeout =
180

Class Method Summary collapse

Class Method Details

.do(method, url, params = {}, options = {}) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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
# File 'lib/restr.rb', line 102

def self.do(method, url, params = {}, options = {})
  #puts "METHOD:  #{method.inspect}"
  #puts "URL:     #{url.inspect}"
  #puts "PARAMS:  #{params.inspect}"
  #puts "OPTIONS: #{options.inspect}"
  
  uri = URI.parse(url)
  
  params = {} unless params
  options = {} unless options
  
  logger = options[:logger] || self.logger
    
  method_mod = method.to_s.downcase.capitalize
  unless Net::HTTP.const_defined?(method_mod)
    raise InvalidRequestMethod, 
      "Callback method #{method.inspect} is not a valid HTTP request method."
  end
  
  if method_mod == 'Get'
    q = params.collect{|k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"}.join("&")
    if uri.query
      uri.query += "&#{q}"
    else
      uri.query = q
    end
  end
  
  req = Net::HTTP.const_get(method_mod).new(uri.request_uri)
  
  
  if options[:username] || options['username']
    req.basic_auth options[:username] || options['username'], options[:password] || options['password']
  end
  
  if params.kind_of?(Hash) && method_mod != 'Get' && method_mod != 'get'
    req.set_form_data(params, '&')
  end
  
  logger.debug("Sending #{method.inspect} request to #{url.inspect} "+
      (method.to_s == 'get' ? "params" : "data")+" #{params.inspect}"+
      (options.blank? ? "" : " with options #{options.inspect}}")+".") if logger
 
  client = Net::HTTP.new(uri.host, uri.port)
  client.use_ssl = (uri.scheme == 'https')
  
  timeout = Restr.request_timeout
  client.read_timeout = timeout
  
  begin
    res = client.start do |http|
      http.request(req)
    end
  rescue Timeout::Error
    res = TimeoutError, "Request timed out after #{timeout} seconds."
  end
  
  case res
  when Net::HTTPSuccess
    if res.content_type =~ /[\/+]xml$/
      logger.debug("Got XML response: \n#{res.body}") if logger
      return XmlSimple.xml_in(res.body,
        'forcearray'   => false,
        'keeproot'     => false
      )
    else
      logger.debug("Got #{res.content_type.inspect} response: \n#{res.body}") if logger
      return res.body
    end
  when TimeoutError
    logger.debug(res) if logger
    return XmlSimple.xml_in(res,
        'forcearray'   => false,
        'keeproot'     => false
      )
  else
    $LAST_ERROR_BODY = res.body # FIXME: this is dumb... need a better way of reporting errors
    $LAST_ERROR_RESPONSE = res # this is currently unused within Restr, but may be useful for debugging 
    logger.error("Got error response '#{res.message}(#{res.code})': #{res.body.blank? ? '(blank response body)' : res.body}") if logger
    res.error!
  end
end

.loggerObject



94
95
96
# File 'lib/restr.rb', line 94

def self.logger
  @@logger
end

.logger=(logger) ⇒ Object



88
89
90
91
92
# File 'lib/restr.rb', line 88

def self.logger=(logger)
  @@logger = logger.dup
  # ActiveSupport's BufferedLogger doesn't seem to support progname= :(
  @@logger.progname = self.name if @@logger.respond_to?(:progname)
end

.method_missing(method, *args) ⇒ Object



98
99
100
# File 'lib/restr.rb', line 98

def self.method_missing(method, *args)
  self.do(method, args[0], args[1] || {}, args[2])
end