undies 1.2.0 → 2.0.0

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.
data/.gitignore CHANGED
@@ -3,3 +3,5 @@ pkg/*
3
3
  *.gem
4
4
  *.log
5
5
  .rvmrc
6
+
7
+ bench/*.txt
data/Gemfile CHANGED
@@ -3,4 +3,5 @@ source "http://rubygems.org"
3
3
  # Specify dependencies in undies.gemspec
4
4
  gemspec
5
5
 
6
- gem 'rake', '~>0.9.2'
6
+ gem 'rake', '~>0.9.2'
7
+ gem 'ruby-prof'
data/Gemfile.lock CHANGED
@@ -1,21 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- undies (1.2.0)
4
+ undies (2.0.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
- leftright (0.9.1)
9
+ ansi (1.4.1)
10
+ assert (0.7.3)
11
+ assert-view (~> 0.5)
12
+ assert-view (0.5.0)
13
+ ansi (~> 1.3)
14
+ undies (~> 2.0)
10
15
  rake (0.9.2)
11
- test-belt (2.0.0)
12
- leftright (~> 0.9.0)
16
+ ruby-prof (0.10.8)
13
17
 
14
18
  PLATFORMS
15
19
  ruby
16
20
 
17
21
  DEPENDENCIES
22
+ assert (~> 0.7.3)
18
23
  bundler (~> 1.0)
19
24
  rake (~> 0.9.2)
20
- test-belt (~> 2.0)
25
+ ruby-prof
21
26
  undies!
data/Rakefile CHANGED
@@ -1,9 +1,7 @@
1
- include Rake::DSL
1
+ require 'assert/rake_tasks'
2
+ Assert::RakeTasks.for(:test)
2
3
 
3
4
  require 'bundler'
4
5
  Bundler::GemHelper.install_tasks
5
6
 
6
- require 'test_belt/rake_tasks'
7
- TestBelt::RakeTasks.for :test
8
-
9
7
  task :default => :build
@@ -0,0 +1,32 @@
1
+ _html {
2
+ _head {}
3
+ _body {
4
+
5
+ 10.times do
6
+ _ "Yo"
7
+ _ "Yo"
8
+ _ "Yo"
9
+ _ "Yo"
10
+ _ "Yo"
11
+
12
+ __ "YoYo"
13
+ __ "YoYo"
14
+ __ "YoYo"
15
+ __ "YoYo"
16
+ __ "YoYo"
17
+
18
+ _br
19
+ _br
20
+ _br
21
+ _br
22
+ _br
23
+
24
+ _div { _ "Hi" }
25
+ _div { _ "Hi" }
26
+ _div { _ "Hi" }
27
+ _div { _ "Hi" }
28
+ _div { _ "Hi" }
29
+ end
30
+
31
+ }
32
+ }
data/bench/procs.rb ADDED
@@ -0,0 +1,106 @@
1
+ @small_proc = Proc.new do
2
+ _html {
3
+ _head {}
4
+ _body {
5
+
6
+ 1.times do
7
+ _ "Yo"
8
+ _ "Yo"
9
+ _ "Yo"
10
+ _ "Yo"
11
+ _ "Yo"
12
+
13
+ __ "YoYo"
14
+ __ "YoYo"
15
+ __ "YoYo"
16
+ __ "YoYo"
17
+ __ "YoYo"
18
+
19
+ _br
20
+ _br
21
+ _br
22
+ _br
23
+ _br
24
+
25
+ _div { _ "Hi" }
26
+ _div { _ "Hi" }
27
+ _div { _ "Hi" }
28
+ _div { _ "Hi" }
29
+ _div { _ "Hi" }
30
+ end
31
+
32
+ }
33
+ }
34
+ end
35
+
36
+
37
+ @large_proc = Proc.new do
38
+ _html {
39
+ _head {}
40
+ _body {
41
+
42
+ 10.times do
43
+ _ "Yo"
44
+ _ "Yo"
45
+ _ "Yo"
46
+ _ "Yo"
47
+ _ "Yo"
48
+
49
+ __ "YoYo"
50
+ __ "YoYo"
51
+ __ "YoYo"
52
+ __ "YoYo"
53
+ __ "YoYo"
54
+
55
+ _br
56
+ _br
57
+ _br
58
+ _br
59
+ _br
60
+
61
+ _div { _ "Hi" }
62
+ _div { _ "Hi" }
63
+ _div { _ "Hi" }
64
+ _div { _ "Hi" }
65
+ _div { _ "Hi" }
66
+ end
67
+
68
+ }
69
+ }
70
+ end
71
+
72
+
73
+ @verylarge_proc = Proc.new do
74
+ _html {
75
+ _head {}
76
+ _body {
77
+
78
+ 1000.times do
79
+ _ "Yo"
80
+ _ "Yo"
81
+ _ "Yo"
82
+ _ "Yo"
83
+ _ "Yo"
84
+
85
+ __ "YoYo"
86
+ __ "YoYo"
87
+ __ "YoYo"
88
+ __ "YoYo"
89
+ __ "YoYo"
90
+
91
+ _br
92
+ _br
93
+ _br
94
+ _br
95
+ _br
96
+
97
+ _div { _ "Hi" }
98
+ _div { _ "Hi" }
99
+ _div { _ "Hi" }
100
+ _div { _ "Hi" }
101
+ _div { _ "Hi" }
102
+ end
103
+
104
+ }
105
+ }
106
+ end
data/bench/profiler.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'ruby-prof'
2
+ require 'undies'
3
+ require 'bench/procs'
4
+
5
+ file = "bench/#{ARGV[0] || 'large'}.html.rb"
6
+ proc = eval("@#{ARGV[0] || 'large'}_proc")
7
+ result = nil
8
+
9
+ File.open('bench/output.txt', 'a+') do |outstream|
10
+
11
+ output = Undies::Output.new(outstream, :pp => 2)
12
+
13
+ result = RubyProf.profile do
14
+ 50.times do
15
+ Undies::Template.new(Undies::Source.new(File.expand_path(file)), {}, output)
16
+ end
17
+ 50.times do
18
+ Undies::Template.new(Undies::Source.new(proc), {}, output)
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ printer = RubyProf::FlatPrinter.new(result).print(STDOUT, :min_percent => 1)
25
+ # printer = RubyProf::GraphPrinter.new(result).print(STDOUT)
@@ -0,0 +1,32 @@
1
+ _html {
2
+ _head {}
3
+ _body {
4
+
5
+ 1.times do
6
+ _ "Yo"
7
+ _ "Yo"
8
+ _ "Yo"
9
+ _ "Yo"
10
+ _ "Yo"
11
+
12
+ __ "YoYo"
13
+ __ "YoYo"
14
+ __ "YoYo"
15
+ __ "YoYo"
16
+ __ "YoYo"
17
+
18
+ _br
19
+ _br
20
+ _br
21
+ _br
22
+ _br
23
+
24
+ _div { _ "Hi" }
25
+ _div { _ "Hi" }
26
+ _div { _ "Hi" }
27
+ _div { _ "Hi" }
28
+ _div { _ "Hi" }
29
+ end
30
+
31
+ }
32
+ }
@@ -0,0 +1,32 @@
1
+ _html {
2
+ _head {}
3
+ _body {
4
+
5
+ 1000.times do
6
+ _ "Yo"
7
+ _ "Yo"
8
+ _ "Yo"
9
+ _ "Yo"
10
+ _ "Yo"
11
+
12
+ __ "YoYo"
13
+ __ "YoYo"
14
+ __ "YoYo"
15
+ __ "YoYo"
16
+ __ "YoYo"
17
+
18
+ _br
19
+ _br
20
+ _br
21
+ _br
22
+ _br
23
+
24
+ _div { _ "Hi" }
25
+ _div { _ "Hi" }
26
+ _div { _ "Hi" }
27
+ _div { _ "Hi" }
28
+ _div { _ "Hi" }
29
+ end
30
+
31
+ }
32
+ }
@@ -1,34 +1,53 @@
1
1
  require 'undies/node'
2
- require 'undies/node_list'
3
- require 'undies/element_stack'
4
2
 
5
3
  module Undies
6
4
  class Element < Node
7
5
 
8
- attr_reader :___name, :___attrs
9
- attr_accessor :___nodes
6
+ # have as many methods to the class level as possilbe to keep from
7
+ # polluting the public instance methods and to maximize the effectiveness
8
+ # of the Element#method_missing logic
9
+
10
+ def self.html_attrs(attrs="", ns=nil)
11
+ if attrs.kind_of? ::Hash
12
+ attrs.empty? ? '' : (attrs.keys.map(&:to_s).sort.map(&:to_sym).inject('') do |html, key|
13
+ ak = ns ? "#{ns}_#{key}" : key.to_s
14
+ av = attrs[key]
15
+ html + (av.kind_of?(::Hash) ? html_attrs(av, ak) : " #{ak}=\"#{av}\"")
16
+ end)
17
+ else
18
+ attrs.to_s
19
+ end
20
+ end
10
21
 
11
- def initialize(stack, name, attrs={}, &block)
12
- if !stack.kind_of?(ElementStack)
13
- raise ArgumentError, "stack must be an Undies::ElementStack"
22
+ def self.content(element)
23
+ nil
24
+ end
25
+
26
+ def self.flush(output, element)
27
+ output.pp_use_indent = true
28
+ output << element.instance_variable_get("@start_tag")
29
+ if (cbs = element.instance_variable_get("@content_blocks")).size > 0
30
+ output.pp_level += 1
31
+ output.pp_use_indent = false
32
+ cbs.each{ |content| content.call }
33
+ output.flush
34
+ output.pp_level -= 1
14
35
  end
36
+ output << element.instance_variable_get("@end_tag") if element.instance_variable_get("@end_tag")
37
+ output.pp_use_indent = true
38
+ end
39
+
40
+ def initialize(name, attrs={}, &block)
15
41
  if !attrs.kind_of?(::Hash)
16
42
  raise ArgumentError, "#{name.inspect} attrs must be provided as a Hash."
17
43
  end
18
- super(@___nodes = NodeList.new)
19
- @stack = stack
20
- @content_writes = 0
21
- @___name = name.to_s
22
- @___attrs = attrs
23
- self.___content = block
24
- end
25
-
26
- def start_tag
27
- "<#{@___name}#{html_attrs(@___attrs)}" + (@content_writes > 0 ? ">" : " />")
28
- end
29
44
 
30
- def end_tag
31
- @content_writes > 0 ? "</#{@___name}>" : nil
45
+ @name = name.to_s
46
+ @attrs = attrs
47
+ @content_blocks = []
48
+ @content_blocks << block if block
49
+ @start_tag = start_tag
50
+ @end_tag = end_tag
32
51
  end
33
52
 
34
53
  # CSS proxy methods ============================================
@@ -37,9 +56,13 @@ module Undies
37
56
 
38
57
  def method_missing(meth, *args, &block)
39
58
  if meth.to_s =~ ID_METH_REGEX
40
- proxy_id_attr($1, *args, &block)
59
+ proxy($1, args.first || {}, block) do |value|
60
+ @attrs.merge!(:id => value)
61
+ end
41
62
  elsif meth.to_s =~ CLASS_METH_REGEX
42
- proxy_class_attr($1, *args, &block)
63
+ proxy($1, args.first || {}, block) do |value|
64
+ @attrs[:class] = [@attrs[:class], value].compact.join(' ')
65
+ end
43
66
  else
44
67
  super
45
68
  end
@@ -55,59 +78,36 @@ module Undies
55
78
  # ==============================================================
56
79
 
57
80
  def ==(other)
58
- other.___name == self.___name &&
59
- other.___attrs == self.___attrs &&
60
- other.___nodes == self.___nodes
81
+ other.instance_variable_get("@name") == @name &&
82
+ other.instance_variable_get("@attrs") == @attrs &&
83
+ other.instance_variable_get("@nodes") == @nodes
61
84
  end
62
85
 
86
+ # overriding this because the base Node class defines a 'to_s' method that
87
+ # needs to be honored
63
88
  def to_str(*args)
64
89
  "Undies::Element:#{self.object_id} " +
65
- "@name=#{self.___name.inspect}, @attrs=#{self.___attrs.inspect}, @nodes=#{self.___nodes.inspect}"
90
+ "@name=#{@name.inspect}, @attrs=#{@attrs.inspect}, @nodes=#{@nodes.inspect}"
66
91
  end
67
92
  alias_method :inspect, :to_str
68
93
 
69
- def to_ary(*args)
70
- self.___nodes
71
- end
72
-
73
- protected
74
-
75
- def html_attrs(attrs={})
76
- raise ArgumentError unless attrs.kind_of? ::Hash
77
- if attrs.empty?
78
- ''
79
- else
80
- ' '+attrs.
81
- sort {|a,b| a[0].to_s <=> b[0].to_s}.
82
- collect {|k_v| "#{k_v[0]}=\"#{k_v[1]}\""}.
83
- join(' ').
84
- strip
85
- end
86
- end
87
-
88
94
  private
89
95
 
90
- def proxy_id_attr(value, attrs={}, &block)
91
- self.___attrs.merge!(:id => value)
92
- self.___attrs.merge!(attrs)
93
- self.___content = block
96
+ def proxy(value, attrs, content_block)
97
+ yield value if block_given?
98
+ @attrs.merge!(attrs)
99
+ @content_blocks << content_block if content_block
100
+ @start_tag = start_tag
101
+ @end_tag = end_tag
94
102
  self
95
103
  end
96
104
 
97
- def proxy_class_attr(value, attrs={}, &block)
98
- self.___attrs[:class] = [self.___attrs[:class], value].compact.join(' ')
99
- self.___attrs.merge!(attrs)
100
- self.___content = block
101
- self
105
+ def start_tag
106
+ "<#{@name}#{self.class.html_attrs(@attrs)}" + (@content_blocks.size > 0 ? ">" : " />")
102
107
  end
103
108
 
104
- def ___content=(block)
105
- if block
106
- @content_writes += 1
107
- @stack.push(self)
108
- block.call
109
- @stack.pop
110
- end
109
+ def end_tag
110
+ @content_blocks.size > 0 ? "</#{@name}>" : nil
111
111
  end
112
112
 
113
113
  end
@@ -0,0 +1,54 @@
1
+ module Undies
2
+
3
+ class Source; end
4
+
5
+ class NamedSource
6
+
7
+ attr_accessor :file, :opts, :proc
8
+
9
+ def initialize(*args, &block)
10
+ args << block if block
11
+ self.args = args
12
+ end
13
+
14
+ def ==(other_named_source)
15
+ self.file == other_named_source.file &&
16
+ self.opts == other_named_source.opts &&
17
+ self.proc == other_named_source.proc
18
+ end
19
+
20
+ def args=(values)
21
+ self.proc, self.opts, self.file = [
22
+ values.last.kind_of?(::Proc) ? values.pop : nil,
23
+ values.last.kind_of?(::Hash) ? values.pop : {},
24
+ values.last.kind_of?(::String) ? values.pop : nil
25
+ ]
26
+ end
27
+
28
+ def args
29
+ [self.file, self.opts, self.proc]
30
+ end
31
+
32
+ end
33
+
34
+ # singleton accessors for named sources
35
+
36
+ def self.named_sources
37
+ @@sources ||= {}
38
+ end
39
+
40
+ def self.named_source(name, *args, &block)
41
+ if args.empty? && block.nil?
42
+ self.named_sources[name]
43
+ else
44
+ self.named_sources[name] = Undies::NamedSource.new(*args, &block)
45
+ end
46
+ end
47
+
48
+ def self.source(name)
49
+ if ns = self.named_source(name)
50
+ Undies::Source.new(ns)
51
+ end
52
+ end
53
+
54
+ end
data/lib/undies/node.rb CHANGED
@@ -1,37 +1,29 @@
1
1
  module Undies
2
- class Node
3
2
 
4
- attr_reader :___content
3
+ class Node
5
4
 
6
- def initialize(content)
7
- @___content = content
8
- end
5
+ # have as many methods to the class level as possilbe to keep from
6
+ # polluting the public instance methods and to maximize the effectiveness
7
+ # of the Element#method_missing logic
9
8
 
10
- def start_tag
11
- nil
9
+ def self.content(node)
10
+ node.instance_variable_get("@content")
12
11
  end
13
12
 
14
- def end_tag
15
- nil
13
+ def self.flush(output, node)
14
+ output << self.content(node)
16
15
  end
17
16
 
18
- def to_s(pp_level=0, pp_indent=nil)
19
- [ self.start_tag,
20
- self.___content,
21
- self.end_tag
22
- ].compact.collect do |item|
23
- pretty_print(item, pp_level, pp_indent)
24
- end.join
17
+ def initialize(content)
18
+ @start_tag = nil
19
+ @end_tag = nil
20
+ @content = content
25
21
  end
26
22
 
27
- private
28
-
29
- def pretty_print(data, level, indent)
30
- if data.kind_of? NodeList
31
- data.to_s(level+1, indent)
32
- else
33
- indent ? "#{' '*level*indent}#{data}\n" : data
34
- end
23
+ def ==(other_node)
24
+ self.class.content(self) == other_node.class.content(other_node) &&
25
+ self.instance_variable_get("@start_tag") == other_node.instance_variable_get("@start_tag") &&
26
+ self.instance_variable_get("@end_tag") == other_node.instance_variable_get("@end_tag")
35
27
  end
36
28
 
37
29
  end
@@ -0,0 +1,35 @@
1
+ module Undies
2
+
3
+ class NodeBuffer < ::Array
4
+
5
+ # a node buffer is a FIFO used to, well, buffer node data for output.
6
+ # nodes are pushed on the stack for processing and flushed to output
7
+ # when pulled. empty the buffer by flushing it to output.
8
+
9
+ # the buffer allows for dynamic node handling while limiting memory
10
+ # bloat. the Output handler ensures that only 1 node is buffered
11
+ # in memory at any given time by pulling the previous node to output
12
+ # before pushing the next node for processing.
13
+
14
+ def initialize(*args)
15
+ # always initialize empty
16
+ super()
17
+ end
18
+
19
+ def push(node)
20
+ super
21
+ node
22
+ end
23
+
24
+ def pull(output)
25
+ if (node = self.shift)
26
+ node.class.flush(output, node)
27
+ end
28
+ end
29
+
30
+ def flush(output)
31
+ self.pull(output) while self.size > 0
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,64 @@
1
+ require 'undies/node'
2
+ require 'undies/element'
3
+ require 'undies/node_buffer'
4
+
5
+ module Undies
6
+ class Output
7
+
8
+ attr_reader :io, :options, :pp, :node_buffer
9
+
10
+ # the output class wraps an IO stream, gathers pretty printing options,
11
+ # and handles buffering nodes and pretty printing to the stream
12
+
13
+ def initialize(io, opts={})
14
+ @io = io
15
+ self.options = opts
16
+ @node_buffer = NodeBuffer.new
17
+ end
18
+
19
+ def options=(opts)
20
+ if !opts.kind_of?(::Hash)
21
+ raise ArgumentError, "please provide a hash to set options with"
22
+ end
23
+
24
+ # setup any pretty printing
25
+ @pp = opts[:pp]
26
+ self.pp_level = 0
27
+ self.pp_use_indent = true
28
+
29
+ @options = opts
30
+ end
31
+
32
+ def pp_level
33
+ @pp_level
34
+ end
35
+
36
+ def pp_level=(value)
37
+ @pp_indent = @pp ? "\n#{' '*value*@pp}" : ""
38
+ @pp_level = value
39
+ end
40
+
41
+ def pp_use_indent=(value)
42
+ @pp_use_indent = value
43
+ end
44
+
45
+ def <<(data)
46
+ @io << (@pp_use_indent ? "#{@pp_indent}#{data}" : data.to_s)
47
+ end
48
+
49
+ def node(data="")
50
+ self.node_buffer.pull(self)
51
+ self.node_buffer.push(Node.new(data))
52
+ end
53
+
54
+ def element(name, attrs={}, &block)
55
+ self.node_buffer.pull(self)
56
+ self.node_buffer.push(Element.new(name, attrs, &block))
57
+ end
58
+
59
+ def flush
60
+ self.node_buffer.flush(self)
61
+ end
62
+
63
+ end
64
+ end