Class: Html2fortitude::HTML

Inherits:
Object
  • Object
show all
Defined in:
lib/html2fortitude/html.rb,
lib/html2fortitude/html/erb.rb

Overview

Converts HTML documents into Fortitude templates. Depends on Nokogiri for HTML parsing. If ERB conversion is being used, also depends on Erubis to parse the ERB and ruby_parser to parse the Ruby code.

Example usage:

HTML.new("<a href='http://google.com'>Blat</a>").render
  #=> "%a{:href => 'http://google.com'} Blat"

Defined Under Namespace

Classes: ERB

Instance Method Summary collapse

Constructor Details

#initialize(template, options = {}) ⇒ HTML

Returns a new instance of HTML.

Parameters:

  • template (String, Nokogiri::Node)

    The HTML template to convert

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :class_name (String) — default: required

    The name of the class to generate

  • :superclass (String) — default: required

    The name of the superclass for this widget

  • :method (String) — default: required

    The name of the method to generate (usually 'content')

  • :assigns (Symbol) — default: required

    Can be one of +:needs_defaulted_to_nil+ (generate +needs+ declarations with defaults of +nil+), +:required_needs+ (generate +needs+ declarations with no defaults), +:instance_variables+ (generate +needs+ declarations with defaults of +nil+, but reference them using instance variables, not methods), or +:no_needs+ (omit any +needs+ declarations entirely -- requires that you have +extra_assigns :use+ set on your widget, or it won't work)

  • :do_end (Boolean) — default: false

    Use 'do ... end' rather than '{ ... }' for tag content

  • :new_style_hashes (Boolean) — default: false

    Use Ruby 1.9-style Hashes



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
264
# File 'lib/html2fortitude/html.rb', line 227

def initialize(template, options = {})
  options.assert_valid_keys(:class_name, :superclass, :method, :assigns, :do_end, :new_style_hashes)

  if template.is_a? Nokogiri::XML::Node
    @template = template
  else
    require 'html2fortitude/html/erb'
    template = ERB.compile(template)

    @class_name = options[:class_name] || raise(ArgumentError, "You must specify a class name")
    @superclass = options[:superclass] || raise(ArgumentError, "You must specify a superclass")
    @method = options[:method] || raise(ArgumentError, "You must specify a method name")
    @assigns = (options[:assigns] || raise(ArgumentError, "You must specify :assigns")).to_sym

    @do_end = options[:do_end]
    @new_style_hashes = options[:new_style_hashes]

    template = add_spacers_to(template)

    if template =~ /^\s*<!DOCTYPE|<html/i
      return @template = Nokogiri.HTML(template)
    end

    @template = Nokogiri::HTML.fragment(template)

    #detect missplaced head or body tag
    #XML_HTML_STRUCURE_ERROR : 800
    if @template.errors.any? { |e| e.code == 800 }
      return @template = Nokogiri.HTML(template).at('/html').children
    end

    #in order to support CDATA in HTML (which is invalid) try using the XML parser
    # we can detect this when libxml returns error code XML_ERR_NAME_REQUIRED : 68
    if @template.errors.any? { |e| e.code == 68 } || template =~ /CDATA/
      return @template = Nokogiri::XML.fragment(template)
    end
  end
end

Instance Method Details

#add_spacers_to(template) ⇒ Object

So: ERb that looks like this: <%= @first_name %> <%= @last_name %> is presumably intended to produce "John Doe". It gets run through our ERb filter, and comes out as this HTML: @first_name @last_name

This seems OK, except that when Nokogiri parses it, it throws away the space between the end of the first and the second , because it "knows" that whitespace between tags in HTML isn't significant. Which, you know, is true for HTML in general, but is NOT true here; we need to keep that whitespace, or we'll end up with "JohnDoe".

As a result, we look for this pattern, and insert a element there, which we explicitly just output as 'text " "'. This brings back our spacer.



277
278
279
280
281
282
283
284
285
# File 'lib/html2fortitude/html.rb', line 277

def add_spacers_to(template)
  template.gsub(%r{</fortitude_[a-z]+>\s+<fortitude_}) do |match|
    if match =~ %r{(</fortitude_[a-z]+>)\s+(<fortitude_)}
      "#{$1}<fortitude_spacer/>#{$2}"
    else
      raise "Should always match!"
    end
  end
end

#renderObject Also known as: to_fortitude

Processes the document and returns the result as a String containing the Fortitude template, including the class declaration, needs text, method declaration, content, and ends.



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/html2fortitude/html.rb', line 289

def render
  to_fortitude_options = {
    :needs => [ ],
    :assign_reference => (@assigns == :instance_variables ? :instance_variable : :method),
    :do_end => @do_end,
    :new_style_hashes => @new_style_hashes
  }

  content_text = @template.to_fortitude(2, to_fortitude_options)

  out = "class #{@class_name} < #{@superclass}\n"
  needs_text = needs_declarations(to_fortitude_options[:needs])
  out << "#{needs_text}\n  \n" if needs_text

  out << "  def #{@method}\n"
  out << "#{content_text.rstrip}\n"
  out << "  end\n"
  out << "end\n"

  out
end