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

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]  describing the web resource


25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/path-to/described_routes.rb', line 25

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")


114
115
116
117
118
119
120
121
122
123
# File 'lib/path-to/described_routes.rb', line 114

def method_missing(method, *args)
  child_resource_template = resource_template.resource_templates.detect{|t| t.rel == 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(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.



13
14
15
# File 'lib/path-to/described_routes.rb', line 13

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.



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

def [](*args)
  positional_params, params_hash = extract_params(args, params)
  known_keys = params_hash.keys
  
  child_resource_template = resource_template.resource_templates.detect do |t|
    if t.rel.nil?
      (t.positional_params(resource_template)[positional_params.length..-1] -  t.optional_params - known_keys).empty?
    end
  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 (once) the application in the parent hierarchy.



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

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

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

Delegated to the application



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

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

#uriObject

Create and cache the URI by filling in the URI template with params



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

def uri
  @uri ||= begin
    Addressable::Template.new(uri_template).expand(params).to_s
  end
end

#uri_templateObject

Get and cache the uri template from the resource tamplte



42
43
44
# File 'lib/path-to/described_routes.rb', line 42

def uri_template
  @uri_template ||= resource_template.uri_template || (application.base + resource_template.path_template)
end