Class: PathTo::DescribedRoutes::TemplatedPath

Inherits:
Path show all
Defined in:
lib/path-to/described_routes.rb

Overview

Implements PathTo::Path, represents a resource described by a ResourceTemplate

Instance Attribute Summary collapse

Attributes inherited from WithParams

#params, #parent, #service

Instance Method Summary collapse

Methods inherited from Path

#delete, #get, #http_client, #inspect, #merge_http_options, #post, #put, #uri_template

Methods inherited from WithParams

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

Constructor Details

#initialize(parent, service, params, resource_template) ⇒ TemplatedPath

Initialize a TemplatedPath. Raises ArgumentError if params doesn’t include all mandatory params expected by the resource template.

Parameters:

[parent]   parent object path or application
[service]  unused - resource_template.name is passed to super() instead.  TODO: refactor
[params]   hash of params; will be merged with the parent's params and passed when required to the resource template's URI template
[resource_template] metadata describing the web resource


33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/path-to/described_routes.rb', line 33

def initialize(parent, service, params, resource_template)
  super(parent, resource_template.name, params)
  @resource_template = resource_template
  
  missing_params = resource_template.params - params.keys
  unless missing_params.empty?
    raise ArgumentError.new(
            "Missing params #{missing_params.join(', ')} " + 
            "(template #{resource_template.name.inspect}," +
            " path_template #{resource_template.path_template.inspect}," +
            " params #{params.inspect})")
    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 can find a resource template with rel matching the method name (direct children only)

  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!

May take a combination of positional and named parameters, e.g.

users("dojo", "format" => "json")


111
112
113
114
115
116
117
118
119
120
# File 'lib/path-to/described_routes.rb', line 111

def method_missing(method, *args)
  child_resource_template = resource_template.find_by_rel(method.to_s).first
  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(resource_template), positional_params)
    child(child_class, method, params_hash, child_resource_template)
  else
    super
  end
end

Instance Attribute Details

#resource_templateObject (readonly)

Returns the value of attribute resource_template.



21
22
23
# File 'lib/path-to/described_routes.rb', line 21

def resource_template
  @resource_template
end

Instance Method Details

#[](*args) ⇒ Object

Creates a child instance with new params, potentially finding a nested resource template that takes the additional params. May take a combination of positional and named parameters, e.g.

users["dojo", {"format" => "json"}]

Positional parameters are unsupported however if a new child template is not identified.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/path-to/described_routes.rb', line 74

def [](*args)
  positional_params, params_hash = extract_params(args, params)
  known_keys = params_hash.keys
  
  child_resource_template = resource_template.find_by_rel(nil).detect do |t|
    (t.positional_params(resource_template)[positional_params.length..-1] -  t.optional_params - known_keys).empty?
  end
  
  if child_resource_template
    # we have a new child resource template; apply any positional params to the hash
    complete_params_hash!(params_hash, child_resource_template.positional_params(resource_template), positional_params)
  else
    # we're just adding optional params, no new template identified
    unless positional_params.empty?
      raise ArgumentError.new(
            "No matching child template; only named parameters can be used here. " +
            "positional_params=#{positional_params.inspect}, params_hash=#{params_hash.inspect}")
    end
    child_resource_template = resource_template
  end
  
  child_class = child_class_for(self, nil, params_hash, child_resource_template)
  child(child_class, nil, params_hash, child_resource_template)
end

#applicationObject

Finds and caches the application in the parent hierarchy.



57
58
59
# File 'lib/path-to/described_routes.rb', line 57

def application
  @application ||= parent.application if parent
end

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

Delegated to the application



62
63
64
# File 'lib/path-to/described_routes.rb', line 62

def child_class_for(instance, method, params, template)
  application.child_class_for(instance, method, params, template)
end

#uriObject

Creates and caches the URI by filling in the resource template’s URI template with params



50
51
52
# File 'lib/path-to/described_routes.rb', line 50

def uri
  @uri ||= resource_template.uri_for(params, application.base)
end