Class: PathTo::DescribedRoutes::Application

Inherits:
WithParams
  • Object
show all
Defined in:
lib/path-to/described_routes.rb

Overview

DescribedRoutes implementation of PathTo::Application.

Instance Attribute Summary collapse

Attributes inherited from WithParams

#params, #parent, #service

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from WithParams

#child, #complete_params_hash!, #extract_params, #respond_to?

Constructor Details

#initialize(options) ⇒ Application



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/path-to/described_routes.rb', line 190

def initialize(options)
  super(options[:parent], options[:service], options[:params])

  @base = options[:base]
  @base.sub(/\/$/, '') if base
  @default_type = options[:default_type] || TemplatedPath
  @http_client = options[:http_client] || HTTPClient
  @http_options = options[:http_options]
  
  @resource_templates = options[:resource_templates]
  unless @resource_templates
    if (json = options[:json])
      @resource_templates = ResourceTemplate::ResourceTemplates.new(JSON.parse(json))
    elsif (yaml = options[:yaml])
      @resource_templates = ResourceTemplate::ResourceTemplates.new(YAML.load(yaml))
    elsif (xml = options[:xml])
      @resource_templates = ResourceTemplate::ResourceTemplates.parse_xml(xml)
    end
  end
  
  if parent
    @base ||= parent.base
    @default_type ||= parent.default_type
    @http_client ||= parent.http_client
    @resource_templates ||= parent.resource_templates
    @http_options ||= parent.http_options
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object

Tries to respond to a missing method. We can do so if

  1. we have a resource template matching the method name
  2. #child_class_for returns a class or other factory object capable of creating a new child instance

Otherwise we invoke super in the hope of avoiding any hard-to-debug behaviour!



234
235
236
237
238
239
240
241
242
243
# File 'lib/path-to/described_routes.rb', line 234

def method_missing(method, *args)
  child_resource_template = resource_templates_by_name[method.to_s]
  if child_resource_template && (child_class = child_class_for(self, method, params, child_resource_template))
    positional_params, params_hash = extract_params(args, params)
    complete_params_hash!(params_hash, child_resource_template.positional_params(nil), positional_params)
    child(child_class, method, params_hash, child_resource_template)
  else
    super
  end
end

Instance Attribute Details

#baseObject (readonly)

Base URI of the application



138
139
140
# File 'lib/path-to/described_routes.rb', line 138

def base
  @base
end

#default_typeObject (readonly)

A Class (or at least something with a #new method) from which child objects will be created



132
133
134
# File 'lib/path-to/described_routes.rb', line 132

def default_type
  @default_type
end

#http_clientObject (readonly)

An HTTParty or similar



135
136
137
# File 'lib/path-to/described_routes.rb', line 135

def http_client
  @http_client
end

#http_optionsObject (readonly)

Hash of options to be included in HTTP method calls



141
142
143
# File 'lib/path-to/described_routes.rb', line 141

def http_options
  @http_options
end

#resource_templatesObject (readonly)

An Array of DescribedRoutes::Resource objects



129
130
131
# File 'lib/path-to/described_routes.rb', line 129

def resource_templates
  @resource_templates
end

Class Method Details

.discover(url, options = {}) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/path-to/described_routes.rb', line 143

def self.discover(url, options={})
  http_options = options[:http_options] || nil
  default_type = options[:default_type] || Path
  http_client  = options[:http_client]  || HTTPClient
  
   = self.(url, http_client)
  unless 
    raise ProtocolError.new("no metadata link found")
  end

  json = http_client.get(.href, :format => :text, :headers => {"Accept" => "application/json"})
  unless json
    raise ProtocolError.new("no json found")
  end

  self.new(options.merge(:json => json))
end


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
186
187
188
# File 'lib/path-to/described_routes.rb', line 161

def self.(url, http_client)
  response = http_client.head(url, {"Accept" => "application/json"})
  unless response.kind_of?(Net::HTTPOK)
    raise ProtocolError.new("got response #{response.inspect} from #{url}")
  end
  link_header = LinkHeader.parse(response["Link"])

  app_templates_link = link_header.find_link(["rel", "describedby"], ["meta", "ResourceTemplates"])
  unless app_templates_link
    resource_template_link = link_header.find_link(["rel", "describedby"], ["meta", "ResourceTemplate"])
    if resource_template_link
      response = http_client.head(resource_template_link.href, {"Accept" => "application/json"})
      unless response.kind_of?(Net::HTTPOK)
        raise ProtocolError.new("got response #{response.inspect} from #{url}")
      end
      link_header = LinkHeader.parse(response["Link"])
      app_templates_link = link_header.find_link(["rel", "index"], ["meta", "ResourceTemplates"])
      unless app_templates_link
        raise ProtocolError.new("(2) couldn't find link with rel=\"index\" and meta=\"ResourceTemplates\" at #{resource_template_link.href}")
      end
    else
      unless app_templates_link
        raise ProtocolError.new("(1) couldn't find link with rel=\"described_by\" and meta=\"ResourceTemplates\" or meta=\"ResourceTemplate\" at #{url}")
      end
    end
  end
  app_templates_link
end

Instance Method Details

#[](params) ⇒ Object

Creates a copy of self with additional params



222
223
224
# File 'lib/path-to/described_routes.rb', line 222

def [](params)
  self.class.new(:parent => self, :params => params)
end

#applicationObject

Returns self. See TemplatedPath#application.



270
271
272
# File 'lib/path-to/described_routes.rb', line 270

def application
  self
end

#child_class_for(instance, method, params, template) ⇒ Object

Determines whether this application &/or its child objects should respond to the given method, and if so returns a class from which a new child instance (typically Path or a subclass thereof) will be created. This implementation (easily overridden) returns #default_type if there is a URI template defined for the method.

Parameters:

[instance] This application or (presumably) one of its child objects [method] The method invoked on the instance that has (presumably) been intercepted by instance#method_missing [params] The instance's params



256
257
258
# File 'lib/path-to/described_routes.rb', line 256

def child_class_for(instance, method, params, template)
  default_type
end

#resource_templates_by_nameObject

Returns a hash of all ResourceTemplates (the tree flattened) keyed by name



263
264
265
# File 'lib/path-to/described_routes.rb', line 263

def resource_templates_by_name
  @resource_templates_by_name ||= resource_templates.all_by_name
end