Class: ForgetPasswords::Template

Inherits:
Object
  • Object
show all
Includes:
XML::Mixup
Defined in:
lib/forget-passwords/template.rb

Defined Under Namespace

Classes: Mapper

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(mapper, name, content, modified = Time.now) ⇒ Template

Returns a new instance of Template.



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/forget-passwords/template.rb', line 186

def initialize mapper, name, content, modified = Time.now
  # boring members
  @mapper   = mapper
  @name     = name
  @modified = modified

  # resolve content
  @doc = case content
         when Nokogiri::XML::Document then content
         when IO, Pathname
           content = mapper.path + content
           fh = content.respond_to?(:open) ? content.open : content
           Nokogiri::XML.parse fh
         when String
           Nokogiri::XML.parse content
         else
           raise ArgumentError, "Not sure what to do with #{content.class}"
         end
end

Instance Attribute Details

#docObject (readonly)

Returns the value of attribute doc.



184
185
186
# File 'lib/forget-passwords/template.rb', line 184

def doc
  @doc
end

#mapperObject (readonly)

Returns the value of attribute mapper.



184
185
186
# File 'lib/forget-passwords/template.rb', line 184

def mapper
  @mapper
end

#modifiedObject (readonly)

Returns the value of attribute modified.



184
185
186
# File 'lib/forget-passwords/template.rb', line 184

def modified
  @modified
end

#nameObject (readonly)

Returns the value of attribute name.



184
185
186
# File 'lib/forget-passwords/template.rb', line 184

def name
  @name
end

Instance Method Details

#populate(resp, headers = {}, vars = {}, base: nil) ⇒ Rack::Response

Give us the Rack::Response object and we’ll populate the headers and body for you.

Parameters:

  • resp (Rack::Response)

    the response to populate

  • headers (#to_h) (defaults to: {})

    the header set

  • vars (#to_h) (defaults to: {})

    the variable bindings

Returns:

  • (Rack::Response)

    the response object, updated in place



305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/forget-passwords/template.rb', line 305

def populate resp, headers = {}, vars = {}, base: nil

  if (body, type = serialize(
    process(vars: vars, base: base), headers, full: true))
    #resp.length = body.bytesize # not sure if necessary
    resp.write body
    resp.content_type = type
  else
    # otherwise 406 lol, the client didn't like any of our responses
    resp.status = 406
  end

  resp
end

#process(vars: {}, base: nil, transform: nil) ⇒ Nokogiri::XML::Document

Perform the variable substitution on the associated document and return it.

Parameters:

  • vars (#to_h) (defaults to: {})

    a hash-like object of variables.

Returns:

  • (Nokogiri::XML::Document)

    the altered document



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/forget-passwords/template.rb', line 213

def process vars: {}, base: nil, transform: nil
  # sub all the placeholders for variables
  doc = @doc.dup

  # add doctype if missing
  doc.create_internal_subset('html', nil, nil) unless doc.internal_subset

  # set the base URI
  if base ||= mapper.base
    if b = doc.at_xpath('(/html:html/html:head/html:base)[1]', XPATHNS)
      # check for a <base href="..."/> already
      b['href'] = base.to_s
    elsif t = doc.at_xpath('(/html:html/html:head/html:title)[1]', XPATHNS)
      # otherwise check for a <title>, after which we'll plunk it
      markup spec: { nil => :base, href: base.to_s }, after: t
    elsif h = doc.at_xpath('/html:html/html:head[1]', XPATHNS)
      # otherwise check for <head>, to which we will prepend
      markup spec: { nil => :base, href: base.to_s }, parent: h
    end
  end

  # add xsl transform if present
  if transform ||= mapper.transform
    pi = { '#pi' => 'xml-stylesheet',
        type: 'text/xsl', href: transform.to_s }
    if t = doc.at_xpath("/processing-instruction('xml-stylesheet')[1]")
      t = markup spec: pi, replace: t
    else
      t = markup spec: pi, before: doc.children.first
    end
  end

  # do the processing instructions
  doc.xpath("/*//processing-instruction('var')").each do |pi|
    key = pi.content.delete_prefix(?$).delete_suffix(??).to_sym
    if vars[key]
      text = pi.document.create_text_node vars[key].to_s
      pi.replace text
    end
  end

  # do the attributes
  doc.xpath(ATTRS_XPATH).each do |attr|
    attr.content = attr.content.gsub(/\$([A-Z_][0-9A-Z_]*)/) do |key|
      key = key.delete_prefix ?$
      vars[key.to_sym] || "$#{key}"
    end
  end

  doc
end

#serialize(doc, headers = {}, full: false) ⇒ String, ...

Given a document, perform rudimentary content negotiation. Return the resulting string, or nil if no variant was chosen.

Parameters:

  • doc (Nokogiri::XML::Document)

    the document

  • headers (#to_h) (defaults to: {})

    the header set for content negotiation

  • full (false, true) (defaults to: false)

    whether to return a content-header pair

Returns:

  • (String, Array<(String, String)>, nil)

    the serialized document (maybe, or maybe the Content-Type header too).



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/forget-passwords/template.rb', line 275

def serialize doc, headers = {}, full: false
  # XXX TODO go back and make it possible for this method to
  # return a hash with all the headers etc so i don't have to do
  # this dumb hack
  method, type = HTTP::Negotiate.negotiate(headers, {
    [TO_XML, 'application/xhtml+xml'] => {
      weight: 1.0, type: 'application/xhtml+xml' },
    [TO_HTML, 'text/html'] => { weight: 0.8, type: 'text/html' },
    [TO_TEXT, 'text/plain'] => { weight: 0.5, type: 'text/plain' },
  })

  # no type selected
  return unless method

  # warn method.inspect

  out = [doc.instance_exec(&method), type]

  full ? out : out.first
end