undies 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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