Class: RESTHome
Defined Under Namespace
Classes: Error, InvalidResponse, MethodMissing
Instance Attribute Summary collapse
-
#base_uri ⇒ Object
Returns the value of attribute base_uri.
-
#basic_auth ⇒ Object
Returns the value of attribute basic_auth.
-
#cookies ⇒ Object
Returns the value of attribute cookies.
-
#request_method ⇒ Object
readonly
Returns the value of attribute request_method.
-
#request_options ⇒ Object
readonly
Returns the value of attribute request_options.
-
#request_url ⇒ Object
readonly
Returns the value of attribute request_url.
-
#response ⇒ Object
readonly
Returns the value of attribute response.
Class Method Summary collapse
- .namespace(path_prefix) ⇒ Object
-
.register_route_block(route, proc) ⇒ Object
:nodoc:.
-
.rest(resource_name, collection_name, base_path, options = {}) ⇒ Object
Creates routes for a RESTful API.
-
.route(name, path, options = {}, &block) ⇒ Object
Defines a web service route.
-
.route_blocks ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#build_options!(options) ⇒ Object
Adds the basic_auth and cookie options This method should be overwritten as needed.
-
#build_url(path) ⇒ Object
Creates the url.
-
#method_missing(method, *args, &block) ⇒ Object
:nodoc:.
-
#parse_response! ⇒ Object
Called after every valid request.
-
#request(method, path, options) ⇒ Object
Makes the request using HTTParty.
-
#route(name, path, options = {}) ⇒ Object
Adds a route to the current object.
-
#save(name, body, options = {}) ⇒ Object
Will either call edit_<name> or add_<name> based on wether or not the body exists.
-
#save_cookies(data) ⇒ Object
Parse an array of Set-cookie headers.
-
#save_cookies! ⇒ Object
Convenience method for saving all cookies by default called from parse_response!.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
:nodoc:
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/resthome.rb', line 265 def method_missing(method, *args, &block) #:nodoc: if method.to_s =~ /^find_(.*?)_by_(.*)$/ find_method = "find_#{$1}" find_args = $2.split '_and_' raise MethodMissing.new "Missing method #{find_method}" unless self.class.method_defined?(find_method) start = (self.method(find_method).arity + 1).abs = args[-1].is_a?(Hash) ? args[-1] : {} [:query] ||= {} find_args.each_with_index do |find_arg, idx| [:query][find_arg] = args[start+idx] end if start > 0 send_args = args[0..(start-1)] send_args << return self.send(find_method, *send_args, &block) else return self.send(find_method, , &block) end else super end end |
Instance Attribute Details
#base_uri ⇒ Object
Returns the value of attribute base_uri.
12 13 14 |
# File 'lib/resthome.rb', line 12 def base_uri @base_uri end |
#basic_auth ⇒ Object
Returns the value of attribute basic_auth.
12 13 14 |
# File 'lib/resthome.rb', line 12 def basic_auth @basic_auth end |
#cookies ⇒ Object
Returns the value of attribute cookies.
12 13 14 |
# File 'lib/resthome.rb', line 12 def @cookies end |
#request_method ⇒ Object (readonly)
Returns the value of attribute request_method.
13 14 15 |
# File 'lib/resthome.rb', line 13 def request_method @request_method end |
#request_options ⇒ Object (readonly)
Returns the value of attribute request_options.
13 14 15 |
# File 'lib/resthome.rb', line 13 def @request_options end |
#request_url ⇒ Object (readonly)
Returns the value of attribute request_url.
13 14 15 |
# File 'lib/resthome.rb', line 13 def request_url @request_url end |
#response ⇒ Object (readonly)
Returns the value of attribute response.
13 14 15 |
# File 'lib/resthome.rb', line 13 def response @response end |
Class Method Details
.namespace(path_prefix) ⇒ Object
173 174 175 176 177 178 |
# File 'lib/resthome.rb', line 173 def self.namespace(path_prefix) @path_prefix ||= [] @path_prefix.push path_prefix yield @path_prefix.pop end |
.register_route_block(route, proc) ⇒ Object
:nodoc:
313 314 315 316 317 318 319 320 321 |
# File 'lib/resthome.rb', line 313 def self.register_route_block(route, proc) #:nodoc: blocks = self.route_blocks blocks[route.to_s] = proc sing = class << self; self; end sing.send :define_method, :route_blocks do blocks end end |
.rest(resource_name, collection_name, base_path, options = {}) ⇒ Object
Creates routes for a RESTful API
resource_name is the name of the items returned by the API, collection_name is the plural name of the items, base_path is the path to the collection
Sets up 5 most common RESTful routes
Example
/customers.json GET list of customers, POST to create a customer
/customers/1.json GET a customers, PUT to edit a customer, DELETE to delete a customer
JSON response returns {'customer': {'id':1, 'name':'Joe', ...}}
Setup the RESTful routes
rest :customer, :customers, '/customers.json'
# same as
route :customers, '/customers.json', :resource => :customer
route :create_customer, '/customers.json', :resource => :customer
route :customer, '/customers/:customer_id.json', :resource => :customer
route :edit_customer, '/customers/:customer_id.json', :resource => :customer
route :delete_customer, '/customers/:customer_id.json', :resource => :customer
Following methods are created
customers # return an array of customers
create_customer :name => 'Smith' # returns {'id' => 2, 'name' => 'Smith'}
customer 1 # return data for customer 1
edit_customer 1, :name => 'Joesph'
delete_customer 1
208 209 210 211 212 213 214 215 |
# File 'lib/resthome.rb', line 208 def self.rest(resource_name, collection_name, base_path, ={}) [:resource] ||= resource_name self.route collection_name, base_path, self.route resource_name, base_path.sub(/(\.[a-zA-Z0-9]+)$/, "/:#{resource_name}_id\\1"), self.route "edit_#{resource_name}", base_path.sub(/(\.[a-zA-Z0-9]+)$/, "/:#{resource_name}_id\\1"), self.route "create_#{resource_name}", base_path, self.route "delete_#{resource_name}", base_path.sub(/(\.[a-zA-Z0-9]+)$/, "/:#{resource_name}_id\\1"), end |
.route(name, path, options = {}, &block) ⇒ Object
Defines a web service route
Arguments
name of the method to create name has special meaning.
-
If starts with create or add the method will be set to POST.
-
If starts with edit or update the method will be set to PUT.
-
If starts with delete the method will be set to DELETE.
-
Else by default the method is GET.
path is the path to the web service
Options
- :method
-
The request method get/post/put/delete. Default is get.
- :expected_status
-
Expected status code of the response, will raise InvalidResponse. Can be an array of codes.
- :return
-
The method to call, the class to create or a Proc to call before method returns.
- :resource
-
The name of the element to return from the response.
- :no_body
-
Removes the body argument from a post/put route
- :query
-
Default set of query arguments
- :body
-
Default set of body arguments
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 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 |
# File 'lib/resthome.rb', line 43 def self.route(name, path, ={}, &block) args = path.scan /:[a-z_]+/ path = "#{@path_prefix.join if @path_prefix}#{path}" function_args = args.collect{ |arg| arg[1..-1] } body_args = get_function_args , :body function_args += body_args.map { |a| a.downcase.gsub(/[^a-z0-9_]/, '_').sub(/^\d+/, '') } query_args = get_function_args , :query function_args += query_args.map { |a| a.downcase.gsub(/[^a-z0-9_]/, '_').sub(/^\d+/, '') } method = [:method] expected_status = [:expected_status] if method.nil? if name.to_s =~ /^(create|add|edit|update|delete)_/ case $1 when 'create' method = 'post' expected_status ||= [200, 201] when 'add' method = 'post' expected_status ||= [200, 201] when 'edit' method = 'put' expected_status ||= 200 when 'update' method = 'put' expected_status ||= 200 when 'delete' method = 'delete' expected_status ||= [200, 204] end else method = 'get' expected_status ||= 200 end end method = method.to_s function_args << 'body' if (method == 'post' || method == 'put') && [:no_body].nil? function_args << 'options={}, &block' method_src = <<-METHOD def #{name}(#{function_args.join(',')}) path = "#{path}" METHOD args.each_with_index do |arg, idx| method_src << "path.sub! '#{arg}', URI.escape(#{function_args[idx]}.to_s)\n" end if [:no_body] if [:body] method_src << "options[:body] = #{[:body].inspect}.merge(options[:body] || {})\n" elsif body_args.size > 0 method_src << "options[:body] ||= {}\n" end else if method == 'post' || method == 'put' if [:resource] method_src << "options[:body] = {'#{[:resource].to_s}' => body}\n" elsif [:body] method_src << "options[:body] = #{[:body].inspect}.merge(body || {})\n" else method_src << "options[:body] = body\n" end end end if [:query] method_src << "options[:query] = #{[:query].inspect}.merge(options[:query] || {})\n" elsif query_args.size > 0 method_src << "options[:query] ||= {}\n" end body_args.each_with_index do |arg, idx| idx += args.size method_src << "options[:body]['#{arg}'] = #{function_args[idx]}\n" end query_args.each_with_index do |arg, idx| idx += body_args.size + args.size method_src << "options[:query]['#{arg}'] = #{function_args[idx]}\n" end method_src << "request :#{method}, path, options\n" if expected_status if expected_status.is_a?(Array) method_src << 'raise InvalidResponse.new "Invalid response code #{response.code}" if ! [' + expected_status.join(',') + "].include?(response.code)\n" else method_src << 'raise InvalidResponse.new "Invalid response code #{response.code}" if response.code != ' + expected_status.to_s + "\n" end end return_method = 'nil' if [:return].nil? || [:return].is_a?(Proc) block ||= [:return] if block register_route_block name, block return_method = "self.class.route_blocks['#{name}']" end elsif [:return].is_a?(Class) return_method = [:return].to_s else return_method = ":#{[:return]}" end resource = [:resource] ? "'#{[:resource]}'" : 'nil' method_src << "parse_response!\n" method_src << "_handle_response response, :resource => #{resource}, :return => #{return_method}, &block\n" method_src << "end\n" if [:instance] [:instance].instance_eval method_src, __FILE__, __LINE__ elsif [:class] [:class].class_eval method_src, __FILE__, __LINE__ else self.class_eval method_src, __FILE__, __LINE__ end end |
.route_blocks ⇒ Object
:nodoc:
309 310 311 |
# File 'lib/resthome.rb', line 309 def self.route_blocks #:nodoc: {} end |
Instance Method Details
#build_options!(options) ⇒ Object
Adds the basic_auth and cookie options This method should be overwritten as needed.
224 225 226 227 228 229 230 |
# File 'lib/resthome.rb', line 224 def () [:basic_auth] = self.basic_auth if self.basic_auth if @cookies [:headers] ||= {} [:headers]['cookie'] = @cookies.to_a.collect{|c| "#{c[0]}=#{c[1]}"}.join('; ') + ';' end end |
#build_url(path) ⇒ Object
Creates the url
218 219 220 |
# File 'lib/resthome.rb', line 218 def build_url(path) "#{self.base_uri || self.class.base_uri}#{path}" end |
#parse_response! ⇒ Object
Called after every valid request. Useful for parsing response headers. This method should be overwritten as needed.
305 306 307 |
# File 'lib/resthome.rb', line 305 def parse_response! end |
#request(method, path, options) ⇒ Object
Makes the request using HTTParty. Saves the method, path and options used.
233 234 235 236 237 238 239 240 241 |
# File 'lib/resthome.rb', line 233 def request(method, path, ) url = build_url path @request_method = method @request_url = url @request_options = @response = self.class.send(method, url, ) end |
#route(name, path, options = {}) ⇒ Object
Adds a route to the current object
169 170 171 |
# File 'lib/resthome.rb', line 169 def route(name, path, ={}) self.class.route name, path, .merge(:instance => self) end |
#save(name, body, options = {}) ⇒ Object
Will either call edit_<name> or add_<name> based on wether or not the body exists.
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/resthome.rb', line 244 def save(name, body, ={}) id = body[:id] || body['id'] if id if self.class.method_defined?("edit_#{name}") self.send("edit_#{name}", id, body, ) elsif self.class.method_defined?("update_#{name}") self.send("update_#{name}", id, body, ) else raise MethodMissing.new "No edit/update method found for #{name}" end else if self.class.method_defined?("add_#{name}") self.send("add_#{name}", body, ) elsif self.class.method_defined?("create_#{name}") self.send("create_#{name}", body, ) else raise MethodMissing.new "No add/create method found for #{name}" end end end |
#save_cookies(data) ⇒ Object
Parse an array of Set-cookie headers
296 297 298 299 300 301 |
# File 'lib/resthome.rb', line 296 def (data) @cookies ||= {} data.delete_if{ |c| c.blank? }.collect { || parts = .split("\; "); parts[0] ? parts[0].split('=') : nil }.each do |c| @cookies[c[0].strip] = c[1].strip if c && c[0] && c[1] end end |
#save_cookies! ⇒ Object
Convenience method for saving all cookies by default called from parse_response!.
290 291 292 293 |
# File 'lib/resthome.rb', line 290 def return unless @response.headers.to_hash['set-cookie'] @response.headers.to_hash['set-cookie'] end |