mullet 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. data/lib/mullet/container.rb +8 -4
  2. data/lib/mullet/default_model.rb +9 -9
  3. data/lib/mullet/default_nested_model.rb +7 -6
  4. data/lib/mullet/html/attribute_command.rb +8 -4
  5. data/lib/mullet/html/attributes.rb +22 -0
  6. data/lib/mullet/html/command.rb +4 -3
  7. data/lib/mullet/html/command_element_renderer.rb +19 -0
  8. data/lib/mullet/html/element.rb +41 -0
  9. data/lib/mullet/html/element_renderer.rb +261 -0
  10. data/lib/mullet/html/filtered_element_handler.rb +87 -0
  11. data/lib/mullet/html/for_element_renderer.rb +47 -0
  12. data/lib/mullet/html/if_element_renderer.rb +46 -0
  13. data/lib/mullet/html/layout.rb +48 -0
  14. data/lib/mullet/html/message.rb +55 -0
  15. data/lib/mullet/html/message_attribute_command.rb +30 -0
  16. data/lib/mullet/html/model_attribute_command.rb +30 -0
  17. data/lib/mullet/html/page_builder.rb +152 -0
  18. data/lib/mullet/html/parser/attribute.rb +8 -0
  19. data/lib/mullet/html/parser/constants.rb +1061 -0
  20. data/lib/mullet/html/parser/default_handler.rb +27 -0
  21. data/lib/mullet/html/parser/input_stream.rb +711 -0
  22. data/lib/mullet/html/parser/open_element.rb +77 -0
  23. data/lib/mullet/html/parser/simple_parser.rb +128 -0
  24. data/lib/mullet/html/parser/tokenizer.rb +1085 -0
  25. data/lib/mullet/html/remove_mode.rb +30 -0
  26. data/lib/mullet/html/static_text_renderer.rb +20 -0
  27. data/lib/mullet/html/template.rb +44 -0
  28. data/lib/mullet/html/template_builder.rb +208 -63
  29. data/lib/mullet/html/template_loader.rb +77 -39
  30. data/lib/mullet/html/template_parser.rb +48 -0
  31. data/lib/mullet/html/unless_element_renderer.rb +24 -0
  32. data/lib/mullet/model.rb +2 -5
  33. data/lib/mullet/render_context.rb +24 -18
  34. data/lib/mullet/tilt.rb +37 -0
  35. data/lib/mullet/version.rb +2 -1
  36. data/lib/mullet.rb +1 -0
  37. metadata +58 -11
@@ -0,0 +1,48 @@
1
+ require 'mullet/html/page_builder'
2
+ require 'nokogiri'
3
+
4
+ module Mullet; module HTML
5
+
6
+ # Extracts content from an HTML page and renders it in a layout. The layout
7
+ # is a template given these variables:
8
+ #
9
+ # `title`
10
+ # : content of the `title` element from the page
11
+ # `body`
12
+ # : content of the `body` element from the page
13
+ #
14
+ # The `body` variable typically contains HTML markup, so the layout must
15
+ # use the `data-escape-xml="false"` command to prevent markup characters
16
+ # being escaped when rendering the variable.
17
+ class Layout
18
+
19
+ # Constructor
20
+ #
21
+ # @param [Template] template
22
+ # layout using the template
23
+ def initialize(template)
24
+ @template = template
25
+ end
26
+
27
+ # Renders page data in a layout.
28
+ #
29
+ # @param [String] page_html
30
+ # content from this HTML page will be rendered in the layout
31
+ # @param [#<<] output
32
+ # where to write rendered output
33
+ def execute(page_html, output)
34
+ page = parse_page(page_html)
35
+ @template.execute(page, output)
36
+ end
37
+
38
+ private
39
+
40
+ def parse_page(page_html)
41
+ handler = PageBuilder.new()
42
+ parser = Nokogiri::HTML::SAX::Parser.new(handler)
43
+ parser.parse(page_html)
44
+ return handler.page
45
+ end
46
+ end
47
+
48
+ end; end
@@ -0,0 +1,55 @@
1
+ require 'i18n'
2
+ require 'mullet/html/attribute_command'
3
+
4
+ module Mullet; module HTML
5
+
6
+ # Holds the resource key and variable names that need to be resolved to
7
+ # format a localized message.
8
+ class Message
9
+ ARGUMENT_SEPARATOR = ','
10
+
11
+ # Constructor
12
+ #
13
+ # @param [String] message_arguments
14
+ # message key and arguments string
15
+ def initialize(message_arguments)
16
+ arguments = message_arguments.split(ARGUMENT_SEPARATOR)
17
+ if arguments.empty?()
18
+ raise TemplateException.new(
19
+ "incorrect syntax in message #{message_arguments}")
20
+ end
21
+
22
+ @message_key = arguments.shift().strip()
23
+ if @message_key.empty?()
24
+ raise TemplateException.new(
25
+ "empty message key in message #{message_arguments}")
26
+ end
27
+
28
+ @argument_keys = []
29
+ arguments.each do |argument_key|
30
+ argument_key = argument_key.strip()
31
+ if argument_key.empty?()
32
+ raise TemplateException.new(
33
+ "empty argument key in message #{message_arguments}")
34
+ end
35
+ @argument_keys << argument_key.to_sym()
36
+ end
37
+ end
38
+
39
+ # Formats localized message.
40
+ #
41
+ # @param [RenderContext] render_context
42
+ # render context
43
+ # @return localized message
44
+ def translate(render_context)
45
+ arguments = Hash.new()
46
+ @argument_keys.each do |argument_key|
47
+ arguments.store(
48
+ argument_key, render_context.get_display_value(argument_key))
49
+ end
50
+
51
+ return I18n.translate(@message_key, arguments)
52
+ end
53
+ end
54
+
55
+ end; end
@@ -0,0 +1,30 @@
1
+ require 'mullet/html/attribute_command'
2
+
3
+ module Mullet; module HTML
4
+
5
+ # Operation to set attribute value from translation.
6
+ class MessageAttributeCommand
7
+ include AttributeCommand
8
+
9
+ # Constructor
10
+ #
11
+ # @param [Symbol] attribute_name
12
+ # name of attribute this command sets
13
+ # @param [Message] message
14
+ # message to format to get attribute value
15
+ def initialize(attribute_name, message)
16
+ super(attribute_name)
17
+ @message = message
18
+ end
19
+
20
+ # Gets attribute value by formatting localized message.
21
+ #
22
+ # @param [RenderContext] render_context
23
+ # render context
24
+ # @return attribute value
25
+ def get_value(render_context)
26
+ return @message.translate(render_context)
27
+ end
28
+ end
29
+
30
+ end; end
@@ -0,0 +1,30 @@
1
+ require 'mullet/html/attribute_command'
2
+
3
+ module Mullet; module HTML
4
+
5
+ # Operation to set attribute value from model.
6
+ class ModelAttributeCommand
7
+ include AttributeCommand
8
+
9
+ # Constructor
10
+ #
11
+ # @param [Symbol] attribute_name
12
+ # name of attribute this command sets
13
+ # @param [Symbole] variable_name
14
+ # variable name to lookup to get value
15
+ def initialize(attribute_name, variable_name)
16
+ super(attribute_name)
17
+ @variable_name = variable_name
18
+ end
19
+
20
+ # Gets attribute value by looking up variable name.
21
+ #
22
+ # @param [RenderContext] render_context
23
+ # render context
24
+ # @return attribute value
25
+ def get_value(render_context)
26
+ return render_context.get_variable_value(@variable_name)
27
+ end
28
+ end
29
+
30
+ end; end
@@ -0,0 +1,152 @@
1
+ require 'mullet/html/attributes'
2
+ require 'mullet/html/command'
3
+ require 'mullet/html/element'
4
+ require 'mullet/html/element_renderer'
5
+ require 'mullet/html/for_element_renderer'
6
+ require 'mullet/html/if_element_renderer'
7
+ require 'mullet/html/parser/default_handler'
8
+ require 'mullet/html/parser/simple_parser'
9
+ require 'mullet/html/remove_mode'
10
+ require 'mullet/html/static_text_renderer'
11
+ require 'mullet/html/template'
12
+ require 'mullet/html/unless_element_renderer'
13
+ require 'mullet/template_error'
14
+ require 'set'
15
+
16
+ module Mullet; module HTML
17
+
18
+ # Handles SAX events to extract content from an HTML page.
19
+ class PageBuilder < Parser::DefaultHandler
20
+
21
+ HEAD = 'head'
22
+ TITLE = 'title'
23
+ BODY = 'body'
24
+ START_CDATA = '<![CDATA['
25
+ END_CDATA = ']]>'
26
+
27
+ # TODO: Write an alternative implementation where we don't have to know
28
+ # which HTML elements have an empty content model.
29
+ EMPTY_CONTENT_ELEMENTS = Set[
30
+ 'br',
31
+ 'hr',
32
+ 'img',
33
+ 'input']
34
+
35
+ attr_reader :page
36
+
37
+ def start_document()
38
+ # number of open head elements
39
+ @head_count = 0
40
+
41
+ # Count of nested open elements where this handler is extracting their
42
+ # inner HTML.
43
+ @inner_depth = 0
44
+
45
+ @page = Hash.new()
46
+ end
47
+
48
+ def start_element(name, attributes)
49
+ if name == HEAD
50
+ @head_count += 1
51
+ end
52
+
53
+ if extracting_inner_html?()
54
+ @inner_depth += 1
55
+ render_start_tag(name, attributes)
56
+ return
57
+ end
58
+
59
+ if @head_count > 0 && name == TITLE
60
+ start_extracting_inner_html()
61
+ return
62
+ end
63
+
64
+ if name == BODY
65
+ start_extracting_inner_html()
66
+ end
67
+ end
68
+
69
+ def end_element(name)
70
+ if name == HEAD
71
+ @head_count -= 1
72
+ end
73
+
74
+ if extracting_inner_html?()
75
+ @inner_depth -= 1
76
+ if extracting_inner_html?() && !EMPTY_CONTENT_ELEMENTS.include?(name)
77
+ render_end_tag(name)
78
+ end
79
+
80
+ end
81
+
82
+ if @head_count > 0 && name == TITLE
83
+ @page.store(:title, @inner_html)
84
+ return
85
+ end
86
+
87
+ if name == BODY
88
+ @page.store(:body, @inner_html)
89
+ end
90
+ end
91
+
92
+ def characters(data)
93
+ if extracting_inner_html?()
94
+ append(data)
95
+ end
96
+ end
97
+
98
+ def cdata_block(data)
99
+ if extracting_inner_html?()
100
+ append(data)
101
+ end
102
+ end
103
+
104
+ def comment(data)
105
+ if extracting_inner_html?()
106
+ append("<!--#{data}-->")
107
+ end
108
+ end
109
+
110
+ def processing_instruction(data)
111
+ if extracting_inner_html?()
112
+ append("<?#{data}?>")
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ def extracting_inner_html?()
119
+ return @inner_depth > 0
120
+ end
121
+
122
+ def start_extracting_inner_html()
123
+ @inner_depth = 1
124
+ @inner_html = ''
125
+ end
126
+
127
+ def append(data)
128
+ @inner_html << data
129
+ end
130
+
131
+ def escape_quote(value)
132
+ return value.include?('"') ? value.gsub(/"/, '&#34;') : value
133
+ end
134
+
135
+ def render_start_tag(tag_name, attributes)
136
+ append("<#{tag_name}")
137
+
138
+ attributes.each do |attribute|
139
+ qualified_name =
140
+ [attribute.prefix, attribute.localname].compact().join(':')
141
+ append(%( #{qualified_name}="#{escape_quote(attribute.value)}"))
142
+ end
143
+
144
+ append('>')
145
+ end
146
+
147
+ def render_end_tag(name)
148
+ append("</#{name}>")
149
+ end
150
+ end
151
+
152
+ end; end
@@ -0,0 +1,8 @@
1
+ require 'nokogiri'
2
+
3
+ module Mullet; module HTML; module Parser
4
+
5
+ class Attribute < Nokogiri::XML::SAX::Parser::Attribute
6
+ end
7
+
8
+ end; end; end