Class: ResourceTemplate

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

Defined Under Namespace

Classes: ResourceTemplates

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash = {}, parent = nil) ⇒ ResourceTemplate

Initialize a ResourceTemplate from a hash. For example:

user_articles = ResourceTemplate.new(
                    "name"               => "user_articles",
                    "rel"                => "articles",
                    "uri_template"       => "http://example.com/users/{user_id}/articles{-prefix|.|format}",
                    "path_template"      => "/users/{user_id}/articles{-prefix|.|format}",
                    "params"             => ["user_id"],
                    "optional_params"    => ["format"],
                    "resource_templates" => [user_article, new_user_article])

resource_templates (if provided) can be a ResourceTemplates object, an array of ResourceTemplate objects or an array of hashes. This last option makes it easy to initialize a whole hierarchy directly from deserialised JSON or YAML objects, e.g.:

user_articles = ResourceTemplate.new(JSON.parse(json))
user_articles = ResourceTemplate.new(YAML.load(yaml))


52
53
54
55
56
57
# File 'lib/resource_template.rb', line 52

def initialize(hash={}, parent=nil)
  @name, @rel, @uri_template, @path_template = %w(name rel uri_template path_template).map{|attr| hash[attr]}
  @params, @optional_params, @options = %w(params optional_params options).map{|attr| hash[attr] || []}
  @resource_templates = ResourceTemplates.new(hash["resource_templates"], self)
  @parent = parent
end

Instance Attribute Details

#nameObject (readonly)

The template’s name. Optional. Making these unique across the application is helpful for clients that may wish to pick out nested templates by name.



7
8
9
# File 'lib/resource_template.rb', line 7

def name
  @name
end

#optional_paramsObject (readonly)

Optional paramaters that may be used by the path template



22
23
24
# File 'lib/resource_template.rb', line 22

def optional_params
  @optional_params
end

#optionsObject (readonly)

“options” in the sense of the HTTP option request - i.e. a list of HTTP methods. Optional.



25
26
27
# File 'lib/resource_template.rb', line 25

def options
  @options
end

#paramsObject (readonly)

The parameters required by the path template



19
20
21
# File 'lib/resource_template.rb', line 19

def params
  @params
end

#parentObject (readonly)

Inverse of resource_templates



31
32
33
# File 'lib/resource_template.rb', line 31

def parent
  @parent
end

#path_templateObject (readonly)

A template for generating paths relative to the application’s base.



16
17
18
# File 'lib/resource_template.rb', line 16

def path_template
  @path_template
end

#relObject (readonly)

Optional attribute that describes a resource’s relationship to its parent. For example:

  • a nested route to a resource’s edit page would have rel of “edit”

  • a nested collection of articles under a “user” resource would have have a rel of “articles”

Collection members generally don’t need a rel as they are identified by their params



13
14
15
# File 'lib/resource_template.rb', line 13

def rel
  @rel
end

#resource_templatesObject (readonly)

Nested resource templates, a Resources object



28
29
30
# File 'lib/resource_template.rb', line 28

def resource_templates
  @resource_templates
end

Instance Method Details

#all_postorderObject

defendants and self



212
213
214
# File 'lib/resource_template.rb', line 212

def all_postorder
  resource_templates.all_postorder + [self]
end

#all_preorderObject

self and descendants



207
208
209
# File 'lib/resource_template.rb', line 207

def all_preorder
  [self] + resource_templates.all_preorder
end

#find_by_rel(matching_rel) ⇒ Object

Find member ResourceTemplate objects matching the given rel



202
203
204
# File 'lib/resource_template.rb', line 202

def find_by_rel(matching_rel)
  resource_templates.select{|resource_template| matching_rel === resource_template.rel}
end

#list_tag(xm, collection, collection_tag, member_tag) ⇒ Object

:nodoc:



118
119
120
121
122
123
124
125
126
# File 'lib/resource_template.rb', line 118

def list_tag(xm, collection, collection_tag, member_tag) #:nodoc:
  unless collection.nil? or collection.empty?
    xm.tag!(collection_tag) do |xm|
      collection.each do |value|
        xm.tag!(member_tag, value)
      end
    end
  end
end

#partial_expand(actual_params) ⇒ Object

Return a new resource template with the path_template or uri_template partially expanded with the given params; does the same recursively descending through child resource templates.



184
185
186
187
188
189
190
191
192
193
194
# File 'lib/resource_template.rb', line 184

def partial_expand(actual_params)
  self.class.new(
      "name"               => name,
      "rel"                => rel,
      "uri_template"       => partial_expand_uri_template(uri_template, actual_params),
      "path_template"      => partial_expand_uri_template(path_template, actual_params),
      "params"             => params - actual_params.keys,
      "optional_params"    => optional_params - actual_params.keys,
      "options"            => options,
      "resource_templates" => resource_templates.partial_expand(actual_params))
end

#partial_expand_uri_template(template, params) ⇒ Object

Partially expand a URI template



197
198
199
# File 'lib/resource_template.rb', line 197

def partial_expand_uri_template(template, params)#:nodoc:
  template && Addressable::Template.new(template).partial_expand(params).pattern
end

#pathObject

Returns a path (assuming the template needs to parameters!)



178
179
180
# File 'lib/resource_template.rb', line 178

def path
  path_for({})
end

#path_for(params_hash) ⇒ Object

Returns an expanded path template with template variables filled from the given params hash. Raises ArgumentError if params doesn’t contain all mandatory params, and a RuntimeError if there is no path_template.

Raises:

  • (ArgumentError)


170
171
172
173
174
175
# File 'lib/resource_template.rb', line 170

def path_for(params_hash)
  missing_params = params - params_hash.keys
  raise ArgumentError.new("missing params #{missing_params.join(', ')}") unless missing_params.empty?
  raise RuntimeError.new("#path_for called on resource template #{name.inspect} that has no path_template") if path_template.nil?
  Addressable::Template.new(path_template).expand(params_hash).to_s
end

#positional_params(parent) ⇒ Object

Returns params and any optional_params in order, removing the parent’s params



129
130
131
132
133
134
135
136
# File 'lib/resource_template.rb', line 129

def positional_params(parent)
  all_params = params + optional_params
  if parent
    all_params - parent.params
  else
    all_params
  end
end

#to_hashObject

Convert to a hash (equivalent to its JSON or YAML representation)



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/resource_template.rb', line 60

def to_hash
  hash = {}
  
  hash["name"] = name if name && !name.empty?
  hash["rel"] = rel if rel && !rel.empty?
  hash["uri_template"] = uri_template if uri_template && !uri_template.empty?
  hash["path_template"] = path_template if path_template && !path_template.empty?

  hash["params"] = params if params && !params.empty?
  hash["optional_params"] = optional_params if optional_params && !optional_params.empty?
  hash["options"] = options if options && !options.empty?

  hash["resource_templates"] = resource_templates.to_parsed if !resource_templates.empty?
  
  hash
end

#to_jsonObject

Convert to JSON



78
79
80
# File 'lib/resource_template.rb', line 78

def to_json
  to_hash.to_json
end

#to_textObject

Text report



88
89
90
# File 'lib/resource_template.rb', line 88

def to_text
  ResourceTemplates.new([self]).to_text
end

#to_xml(xm) ⇒ Object

Produces the XML format, given an XML builder object and an array of ResourceTemplate objects



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/resource_template.rb', line 95

def to_xml(xm)
  xm.ResourceTemplate do |xm|
    value_tag(xm, "rel")
    value_tag(xm, "name")
    value_tag(xm, "path_template")
    value_tag(xm, "uri_template")

    list_tag(xm, params, "Params", "param")
    list_tag(xm, optional_params, "OptionalParams", "param")

    # could use a list of elements here, but let's follow HTTP's lead and reduce the verbosity
    xm.options(options.join(", ")) unless options.empty?

    resource_templates.to_xml(xm) unless resource_templates.empty?
  end
  xm
end

#to_yamlObject

Convert to YAML



83
84
85
# File 'lib/resource_template.rb', line 83

def to_yaml
  to_hash.to_yaml
end

#uriObject

Returns a URI (assuming the template needs to parameters!)



164
165
166
# File 'lib/resource_template.rb', line 164

def uri
  uri_for({}, nil)
end

#uri_for(params_hash, base = nil) ⇒ Object

Returns an expanded URI template with template variables filled from the given params hash. Raises ArgumentError if params doesn’t contain all mandatory params.



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/resource_template.rb', line 149

def uri_for(params_hash, base=nil)
  missing_params = params - params_hash.keys
  unless missing_params.empty?
    raise ArgumentError.new("missing params #{missing_params.join(', ')}")
  end
  
  t = uri_template(base)
  unless t
    raise RuntimeError.new("uri_template(#{base.inspect})=nil; path_template=#{path_template.inspect}")
  end
  
  Addressable::Template.new(t).expand(params_hash).to_s
end

#uri_template(base = nil) ⇒ Object

Returns this template’s URI template, or one constructed from the given base and path template.



139
140
141
142
143
144
145
# File 'lib/resource_template.rb', line 139

def uri_template(base=nil)
  if @uri_template
    @uri_template
  elsif base && path_template
    base + path_template
  end
end

#value_tag(xm, tag) ⇒ Object

:nodoc:



113
114
115
116
# File 'lib/resource_template.rb', line 113

def value_tag(xm, tag) #:nodoc:
  value = self.send(tag.to_sym)
  xm.tag!(tag, value) unless value.blank?
end