tumblargh 0.2.0 → 0.2.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.
@@ -12,11 +12,23 @@ grammar Tumblr
12
12
  end
13
13
 
14
14
  rule block_start
15
- '{block:' [^\s}:;]+ '}' space? <Tumblargh::Node::BlockStart>
15
+ '{block:' block_name space? block_arguments? '}' space? <Tumblargh::Node::BlockStart>
16
16
  end
17
17
 
18
18
  rule block_end
19
- '{/block:' [^\s}]+ '}' space? <Tumblargh::Node::BlockEnd>
19
+ '{/block:' block_name '}' space? <Tumblargh::Node::BlockEnd>
20
+ end
21
+
22
+ rule block_name
23
+ [^\s}:;]+
24
+ end
25
+
26
+ rule block_arguments
27
+ block_argument+
28
+ end
29
+
30
+ rule block_argument
31
+ (([a-zA-Z0-9]+) '=' '"' ([a-zA-Z0-9]+) '"') space?
20
32
  end
21
33
 
22
34
  rule tag
@@ -1,6 +1,5 @@
1
1
  module Tumblargh
2
2
  module Node
3
-
4
3
  class Block < Root
5
4
 
6
5
  def name
@@ -8,8 +7,12 @@ module Tumblargh
8
7
  elements.first.name
9
8
  end
10
9
 
10
+ def options
11
+ elements.first.options
12
+ end
13
+
11
14
  def to_tree
12
- ary = [type, name]
15
+ ary = [type, name, options]
13
16
 
14
17
  # Second node is a Treetop SyntaxNode which holds
15
18
  # the rest of the block contents. Extra parse node
@@ -22,10 +25,9 @@ module Tumblargh
22
25
  end
23
26
  end
24
27
 
25
- return ary
28
+ ary
26
29
  end
27
30
 
28
31
  end
29
-
30
32
  end
31
33
  end
@@ -1,6 +1,5 @@
1
1
  module Tumblargh
2
2
  module Node
3
-
4
3
  class BlockStart < Base
5
4
 
6
5
  def name
@@ -8,15 +7,28 @@ module Tumblargh
8
7
  "#{str[0].upcase}#{str[1..str.size]}"
9
8
  end
10
9
 
10
+ def options
11
+ arguments = elements[3]
12
+ return {} if arguments.nil? || arguments.elements.nil?
13
+
14
+ arguments.elements.inject({}) do |memo, node|
15
+ nodes = node.elements.first.elements
16
+ k = nodes[0].text_value
17
+ v = nodes[3].text_value
18
+
19
+ memo[k] = v
20
+ memo
21
+ end
22
+ end
23
+
11
24
  def matching_end
12
25
  "{/block:#{name}}"
13
26
  end
14
27
 
15
28
  def to_tree
16
- return [type, name]
29
+ return [type, name, options]
17
30
  end
18
31
 
19
32
  end
20
-
21
33
  end
22
34
  end
@@ -3,6 +3,8 @@ require 'open-uri'
3
3
  require 'nokogiri'
4
4
 
5
5
  module Tumblargh
6
+ class ParserError < StandardError
7
+ end
6
8
 
7
9
  class Parser
8
10
  grammar_file = File.join(File.dirname(__FILE__), 'grammar')
@@ -56,10 +58,8 @@ module Tumblargh
56
58
  def parse
57
59
  @structure = @@parser.parse(html)
58
60
 
59
- if(@structure.nil?)
60
- puts @@parser.failure_reason
61
- puts "#{@@parser.failure_line}:#{@@parser.failure_column}"
62
- raise ParserError, "Parse error at offset: #{@@parser.index}"
61
+ if @structure.nil?
62
+ raise ParserError, @@parser.failure_reason
63
63
  end
64
64
 
65
65
  @tree = @structure.to_tree
@@ -77,7 +77,7 @@ module Tumblargh
77
77
 
78
78
  default = case type
79
79
  when "if"
80
- default == "1" ? true : false
80
+ default == "1"
81
81
  else
82
82
  default
83
83
  end
@@ -89,8 +89,4 @@ module Tumblargh
89
89
  opts
90
90
  end
91
91
  end
92
-
93
- class ParserError < StandardError
94
- end
95
-
96
92
  end
@@ -9,7 +9,7 @@ require 'tumblargh/renderer/tag'
9
9
  module Tumblargh
10
10
  module Renderer
11
11
 
12
- def self.factory(node, context)
12
+ def self.factory(node, context, options = {})
13
13
  args = []
14
14
  base = node.first.to_s
15
15
 
@@ -35,7 +35,7 @@ module Tumblargh
35
35
  klass_name = "Tumblargh::Renderer::#{base}"
36
36
  klass = klass_name.constantize
37
37
 
38
- klass.new(node, context, *args)
38
+ klass.new(node, context, options, *args)
39
39
  end
40
40
 
41
41
  end
@@ -1,15 +1,14 @@
1
1
  module Tumblargh
2
2
  module Renderer
3
-
4
3
  class Base
5
4
 
6
5
  class << self
7
6
 
8
7
  # Define a simple tag on the block.
9
- # Name being tag name, and optionally the attibute/method to call
8
+ # Name being tag name, and optionally the attibute/method to call
10
9
  # on the context. If the second argument is left off, it'll just use the tag name.
11
10
  def contextual_tag(name, attribute=nil)
12
- class_eval do
11
+ class_eval do
13
12
  define_method name do
14
13
  context.send(attribute || name)
15
14
  end
@@ -18,12 +17,15 @@ module Tumblargh
18
17
 
19
18
  end
20
19
 
21
- attr_reader :node
20
+ attr_reader :node, :options
22
21
  attr_accessor :context
23
22
 
24
- def initialize(node, context)
23
+ alias_method :config, :options # Backwards compatibility with old Document rendere
24
+
25
+ def initialize(node, context, options = {})
25
26
  @node = node
26
27
  @context = context
28
+ @options = options.with_indifferent_access
27
29
  end
28
30
 
29
31
  def context_post
@@ -91,9 +91,9 @@ module Tumblargh
91
91
  class Boolean < Base
92
92
  attr_reader :variable
93
93
 
94
- def initialize(node, context, *args)
94
+ def initialize(node, context, options = {}, *args)
95
95
  @variable = args[0]
96
- super(node, context)
96
+ super(node, context, options)
97
97
  end
98
98
 
99
99
  def should_render?
@@ -120,7 +120,7 @@ module Tumblargh
120
120
  end
121
121
  end
122
122
 
123
- # Identical to {PostTitle}, but will automatically generate a summary
123
+ # Identical to {PostTitle}, but will automatically generate a summary
124
124
  # if a title doesn't exist.
125
125
  class PostSummary < PostTitle
126
126
  def post_summary
@@ -22,10 +22,10 @@ module Tumblargh
22
22
  def render
23
23
  return '' unless should_render?
24
24
 
25
- sig, type, *nodes = node
25
+ _, type, options, *nodes = node
26
26
 
27
27
  res = nodes.map do |n|
28
- Renderer.factory(n, self).render
28
+ Renderer.factory(n, self, options).render
29
29
  end
30
30
 
31
31
  " #{res.join('')} "
@@ -1,18 +1,10 @@
1
- module Tumblargh
1
+ module Tumblargh
2
2
  module Renderer
3
-
4
3
  class Document < Base
5
4
 
6
- attr_accessor :config
7
-
8
- def initialize(node, context, config)
9
- @config = config.with_indifferent_access
10
- super(node, context)
11
- end
12
-
13
5
  # Are we rendering a permalink page?
14
6
  def permalink?
15
- @config[:permalink] == true
7
+ options[:permalink] == true
16
8
  end
17
9
 
18
10
  # TAGS ----------
@@ -21,6 +13,7 @@
21
13
 
22
14
  def meta_description
23
15
  strip_html(description)
16
+ strip_html(description)
24
17
  end
25
18
 
26
19
  def favicon
@@ -32,7 +25,7 @@
32
25
  "#{context.url}rss"
33
26
  end
34
27
 
35
- # Appearance options
28
+ # Appearance options
36
29
  # http://www.tumblr.com/docs/en/custom_themes#appearance-options
37
30
  def color(key)
38
31
  custom_value_for_type :color, key
@@ -0,0 +1,3 @@
1
+ module Tumblargh
2
+ VERSION = '0.2.1'.freeze
3
+ end
@@ -0,0 +1,168 @@
1
+ <!DOCTYPE html>
2
+ <html lang="{block:English}en{/block:English}{block:French}fr{/block:French}{block:German}de{/block:German}{block:Japanese}ja{/block:Japanese}{block:Italian}it{/block:Italian}{block:Spanish}es{/block:Spanish}{block:Polish}pl{/block:Polish}">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
6
+ <meta name="viewport" content="width=device-width">
7
+ <title>{Title}{block:PostSummary} &mdash; {PostSummary}{/block:PostSummary}</title>
8
+ {block:Description}<meta name="description" content="{MetaDescription}">{/block:Description}
9
+
10
+ <link rel="apple-touch-icon" href="{PortraitURL-128}">
11
+ <link rel="shortcut icon" href="{Favicon}">
12
+ <link rel="alternate" type="application/rss+xml" href="{RSS}">
13
+ <!--Normalize 2.1.0 CSS-->
14
+ <link rel="stylesheet" href="http://static.tumblr.com/cqczc5x/kYdmihjuj/normalize.css" type="text/css" media="all">
15
+ <style type="text/css">
16
+
17
+ img, video, object, embed {
18
+ max-width: 100%;
19
+ height: auto !important;
20
+ }
21
+
22
+ .clearfix {zoom:1;}
23
+ .clearfix:before, .clearfix:after {
24
+ content: "\0020";
25
+ display: block;
26
+ height: 0;
27
+ overflow: hidden; }
28
+ .clearfix:after {clear: both;}
29
+
30
+ /*Tumblr inherited */
31
+ iframe#tumblr_controls {}
32
+ .search_query {}
33
+ .read_more_container a {}
34
+
35
+ </style>
36
+ <!--[if IE]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
37
+ </head>
38
+ <body>
39
+ <header id="header">
40
+ <h1 id="logo"><a href="/">{Title}</a></h1>
41
+ {block:Description}<div class="description">{Description}</div>{/block:Description}
42
+ <ul id="nav">
43
+ {block:HasPages}{block:Pages}<li><a href="{URL}">{Label}</a></li>{/block:Pages}{/block:HasPages}
44
+ <li><a href="/archive">{lang:Archive}</a></li>
45
+ {block:AskEnabled}<li><a href="/ask">{AskLabel}</a></li>{/block:AskEnabled}
46
+ </ul>
47
+ <form id="search" action="/search" method="get">
48
+ <input type="text" placeholder="{lang:Search}" name="q" value="{SearchQuery}">
49
+ <input type="submit" value="{lang:Search}">
50
+ </form>
51
+ </header>
52
+ <section class="content">
53
+ {block:SearchPage}
54
+ <div class="result">
55
+ <p>{lang:Found SearchResultCount results for SearchQuery 2}</p>
56
+ {block:NoSearchResults}<p>{lang:No results for SearchQuery 2}</p>{/block:NoSearchResults}
57
+ </div>
58
+ {/block:SearchPage}
59
+ {block:TagPage}<div class="result"><p>{lang:TagResultCount posts tagged Tag 3}</p></div>{/block:TagPage}
60
+
61
+ {block:Posts}
62
+ <article class="post {TagsAsClasses}">
63
+ {block:Date}
64
+ {block:NewDayDate}<p>{DayOfWeek}, {Month} {DayOfMonth}, {Year}</p>{/block:NewDayDate}
65
+ {block:SameDayDate}<p>{DayOfWeek}, {Month} {DayOfMonth}, {Year}</p>{/block:SameDayDate}
66
+ {/block:Date}
67
+ {block:Text}
68
+ <div class="text">
69
+ {block:Title}<h3>{Title}</h3>{/block:Title}
70
+ {Body}
71
+ </div>
72
+ {/block:Text}
73
+ {block:Quote}
74
+ <div class="quote">
75
+ <blockquote class="words {Length}">&#8220;{Quote}&#8221;</blockquote>
76
+ {block:Source}<p class="source">&mdash; {Source}</p>{/block:Source}
77
+ </div>
78
+ {/block:Quote}
79
+ {block:Link}
80
+ <div class="link">
81
+ <h3><a href="{URL}" {Target}>{Name}</a></h3>
82
+ {block:Description}<div class="caption">{Description}</div>{/block:Description}
83
+ </div>
84
+ {/block:Link}
85
+ {block:Video}
86
+ <div class="video">
87
+ {Video-500}
88
+ {block:Caption}<div class="caption">{Caption}</div>{/block:Caption}
89
+ </div>
90
+ {/block:Video}
91
+ {block:Audio}
92
+ <div class="audio">
93
+ {block:AlbumArt}<img src="{AlbumArtURL}" alt="">{/block:AlbumArt}
94
+ {AudioPlayerBlack}
95
+ {block:Caption}<div class="caption">{Caption}</div>{/block:Caption}
96
+ </div>
97
+ {/block:Audio}
98
+ {block:Photo}
99
+ <div class="photo">
100
+ {LinkOpenTag}<img src="{PhotoURL-HighRes}" alt="{PhotoAlt}"/>{LinkCloseTag}
101
+ {block:Caption}<div class="caption">{Caption}</div>{/block:Caption}
102
+ </div>
103
+ {/block:Photo}
104
+ {block:Photoset}
105
+ <div class="photoset">
106
+ {block:Photos}
107
+ <img src="{PhotoURL-HighRes}" alt="{PhotoAlt}"/>
108
+ {block:Caption}<div class="caption">{Caption}</div>{/block:Caption}
109
+ {/block:Photos}
110
+ {block:Caption}<div class="caption">{Caption}</div>{/block:Caption}
111
+ </div>
112
+ {/block:Photoset}
113
+
114
+ {block:Panorama}
115
+ <div class="panorama">
116
+ {LinkOpenTag}<img src="{PhotoURL-Panorama}" alt="{PhotoAlt}" />{LinkCloseTag}
117
+ {block:Caption}<div class="caption">{Caption}</div>{/block:Caption}
118
+ </div>
119
+ {/block:Panorama}
120
+
121
+ {block:Chat}
122
+ <div class="chat">
123
+ {block:Title}<h3>{Title}</h3>{/block:Title}
124
+ <ul class="conversation">
125
+ {block:Lines}
126
+ <li class="line {Alt}">
127
+ {block:Label}<span class="person">{Label}</span>{/block:Label}
128
+ <span class="person-said">{Line}</span>
129
+ </li>
130
+ {/block:Lines}
131
+ </ul>
132
+ </div>
133
+ {/block:Chat}
134
+ {block:Answer}
135
+ <div class="answer">
136
+ <img src="{AskerPortraitURL-40}">
137
+ {Asker}
138
+ {Question}
139
+ {Answer}
140
+ </div>
141
+ {/block:Answer}
142
+ {block:IndexPage}<p><a href="{Permalink}" class="permalink">{lang:Permalink}</a></p>{/block:IndexPage}
143
+ </article>
144
+ {/block:Posts}
145
+
146
+ {block:PermalinkPagination}
147
+ <div class="pagination">
148
+ {block:PreviousPost}<a class="prev"href="{PreviousPost}">{lang:Previous post}</a>{/block:PreviousPost}
149
+ {block:NextPost}<a class="next" href="{NextPost}">{lang:Next post}</a>{/block:NextPost}
150
+ </div>
151
+ {/block:PermalinkPagination}
152
+
153
+ {block:Pagination}
154
+ <div class="pagination">
155
+ {block:PreviousPage}<a class="prev" href="{PreviousPage}">{lang:Previous}</a>{/block:PreviousPage}
156
+
157
+ {block:JumpPagination length="5"}
158
+ {block:CurrentPage}<span class="current-page">{PageNumber}</span>{/block:CurrentPage}
159
+ {block:JumpPage}<a class="jump-page" href="{URL}">{PageNumber}</a>{/block:JumpPage}
160
+ {/block:JumpPagination}
161
+
162
+ {block:NextPage}<a class="next" href="{NextPage}">{lang:Next}</a>{/block:NextPage}
163
+ </div>
164
+ {/block:Pagination}
165
+
166
+ </section><!--close content-->
167
+ </body>
168
+ </html>
data/spec/parser_spec.rb CHANGED
@@ -1,52 +1,40 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
 
4
4
  describe Tumblargh::Parser do
5
-
6
- before do
7
- @parser = Tumblargh::Parser.new
8
- end
9
-
10
- it "should exist" do
11
- defined?(Tumblargh::Parser).should be_true
12
- end
5
+ subject { Tumblargh::Parser.new }
13
6
 
14
7
  describe "given invalid input" do
15
-
16
8
  it "should throw an error if there is an unclosed block" do
17
- @parser.html = <<-eos
9
+ subject.html = <<-eos
18
10
  {block:Text}
19
11
  {block:Title}<h1>{Title}</h1>{block:Title}
20
12
  <p>{Body}</p>
21
13
  {/block:text}
22
14
  eos
23
15
 
24
- lambda { @parser.tree }.should raise_error Tumblargh::ParserError
16
+ lambda { subject.tree }.should raise_error Tumblargh::ParserError
25
17
  end
26
-
27
18
  end
28
19
 
29
20
  describe "given something that kinda looks like a tag but isn't" do
30
-
31
21
  before do
32
- @parser.html = "<div>{CustomCSS</div>"
22
+ subject.html = "<div>{CustomCSS</div>"
33
23
  end
34
24
 
35
25
  it "should NOT throw an error if there is a malformed tag" do
36
- lambda { @parser.tree }.should_not raise_error Tumblargh::ParserError
26
+ lambda { subject.tree }.should_not raise_error Tumblargh::ParserError
37
27
  end
38
28
 
39
29
  it "should generate the following tree" do
40
- @parser.tree.should == [[:Literal, "<div>"], [:Literal, "{"], [:Literal, "CustomCSS</div>"]]
30
+ subject.tree.should == [[:Literal, "<div>"], [:Literal, "{"], [:Literal, "CustomCSS</div>"]]
41
31
  end
42
-
43
32
  end
44
33
 
45
34
  describe "when given a bunch of css" do
46
-
47
35
  before do
48
36
  @input = <<-eos
49
- .container{*zoom:1;margin:auto;width:960px;max-width:100%;position:relative}.container:after{content:"";display:table;clear:both}.dot-sprite,#clients .nav .dots li,#clients .nav .dots li.active,#fullscreen_nav .nav .dots li,#fullscreen_nav .nav .dots li.active{background:url('../images/dot-sa49b299bc4.png') no-repeat}body.index{min-width:1000px}body.fullscreen{overflow-x:hidden}#clients{height:461px;overflow:visible;background:#fff url('../images/homepage-clients-bg.png') repeat-x 0 0px}#clients .frame{width:958px;height:448px;margin:0 auto;position:relative;-moz-border-radius:7px;-webkit-border-radius:7px;-o-border-radius:7px;-ms-border-radius:7px;-khtml-border-radius:7px;border-radius:7px;-moz-box-shadow:#000 0px 1px 2px;-webkit-box-shadow:#000 0px 1px 2px;-o-box-shadow:#000 0px 1px 2px;box-shadow:#000 0px 1px 2px;background:#262626}
37
+ .container{*zoom:1;margin:auto;width:960px;max-width:100%;position:relative}.container:after{content:"";display:table;clear:both}.dot-sprite,#clients .nav .dots li,#clients .nav .dots li.active,#fullscreen_nav .nav .dots li,#fullscreen_nav .nav .dots li.active{background:url("../images/dot-sa49b299bc4.png") no-repeat}body.index{min-width:1000px}body.fullscreen{overflow-x:hidden}#clients{height:461px;overflow:visible;background:#fff url("../images/homepage-clients-bg.png") repeat-x 0 0px}#clients .frame{width:958px;height:448px;margin:0 auto;position:relative;-moz-border-radius:7px;-webkit-border-radius:7px;-o-border-radius:7px;-ms-border-radius:7px;-khtml-border-radius:7px;border-radius:7px;-moz-box-shadow:#000 0px 1px 2px;-webkit-box-shadow:#000 0px 1px 2px;-o-box-shadow:#000 0px 1px 2px;box-shadow:#000 0px 1px 2px;background:#262626}
50
38
 
51
39
  #title a{
52
40
  text-decoration:none;
@@ -70,24 +58,21 @@ describe Tumblargh::Parser do
70
58
  #title b.right {right:-36px;}
71
59
  eos
72
60
 
73
- @parser.html = @input
61
+ subject.html = @input
74
62
  end
75
63
 
76
64
  it "should NOT throw an error" do
77
- lambda { @parser.tree }.should_not raise_error Tumblargh::ParserError
65
+ lambda { subject.tree }.should_not raise_error Tumblargh::ParserError
78
66
  end
79
67
 
80
68
  it "its output should match its input" do
81
- @parser.to_s.should eql @input
69
+ subject.to_s.should eql @input
82
70
  end
83
-
84
71
  end
85
72
 
86
-
87
73
  describe "given a simple partial" do
88
-
89
74
  before do
90
- @parser.html = <<-eos
75
+ subject.html = <<-eos
91
76
  {block:Text}
92
77
  {block:Title}<h1>{Title}</h1>{/block:Title}
93
78
  <p>{Body}</p>
@@ -96,48 +81,46 @@ describe Tumblargh::Parser do
96
81
  end
97
82
 
98
83
  it "should not contain any custom appearance options" do
99
- @parser.options.empty?.should be_true
84
+ subject.options.empty?.should be_true
100
85
  end
101
86
 
102
87
  it "should contain the following tree" do
103
88
  expected = [
104
89
  [:Literal, " "],
105
- [:Block, "Text",
106
- [:Block, "Title",
107
- [:Literal, "<h1>"],
108
- [:Tag, "Title"],
90
+ [:Block, "Text", {},
91
+ [:Block, "Title", {},
92
+ [:Literal, "<h1>"],
93
+ [:Tag, "Title"],
109
94
  [:Literal, "</h1>"]
110
- ],
111
- [:Literal, "<p>"],
112
- [:Tag, "Body"],
95
+ ],
96
+ [:Literal, "<p>"],
97
+ [:Tag, "Body"],
113
98
  [:Literal, "</p>\n "]
114
99
  ]
115
100
  ]
116
101
 
117
102
  # Yes, Array.== Array is a deep value based comparison
118
- @parser.tree.should == expected
103
+ subject.tree.should == expected
119
104
  end
120
-
121
105
  end
122
106
 
123
107
  describe "given jake's solstice theme" do
124
-
125
108
  before do
126
- @parser.html = open(File.join(FIXTURE_PATH, 'themes', 'solstice.html')).read
109
+ subject.html = open(File.join(FIXTURE_PATH, "themes", "solstice.html")).read
127
110
  end
128
-
111
+
129
112
  it "should contain the correct appearance options" do
130
- @parser.options.should == {
113
+ subject.options.should == {
131
114
  "image" => {
132
115
  "background" => ""
133
- },
116
+ },
134
117
  "if" => {
135
118
  "showpeopleifollow" => true,
136
- "showmyportraitphoto" => true,
137
- "showvialinkswithrebloggedposts" => true,
138
- "useclassicpaging" => false,
119
+ "showmyportraitphoto" => true,
120
+ "showvialinkswithrebloggedposts" => true,
121
+ "useclassicpaging" => false,
139
122
  "hidedisquscommentcountifzero" => false
140
- },
123
+ },
141
124
  "text" => {
142
125
  "disqusshortname" => "",
143
126
  "googleanalyticsid" => ""
@@ -145,15 +128,71 @@ describe Tumblargh::Parser do
145
128
  }
146
129
  end
147
130
 
148
- it "should contain exacty one 'posts' block" do
149
- nodes = @parser.tree
150
- matches = nodes.select do |n|
131
+ it "should contain exacty one Posts block" do
132
+ matches = subject.tree.select do |n|
151
133
  n[0] == :Block && n[1] == "Posts"
152
134
  end
153
135
 
154
136
  matches.size.should eql 1
155
137
  end
156
-
157
138
  end
158
139
 
140
+ describe "block arguments" do
141
+ context "with a single argument" do
142
+ before do
143
+ subject.html = <<-eos
144
+ {block:JumpPagination length="5"}
145
+ {block:CurrentPage}<span class="current-page">{PageNumber}</span>{/block:CurrentPage}
146
+ {/block:JumpPagination}
147
+ eos
148
+ end
149
+
150
+ it "should contain the following tree" do
151
+ expected = [
152
+ [:Literal, " "],
153
+ [:Block, "JumpPagination", { "length" => "5" },
154
+ [:Block, "CurrentPage", {},
155
+ [:Literal, "<span class=\"current-page\">"],
156
+ [:Tag, "PageNumber"],
157
+ [:Literal, "</span>"]
158
+ ]
159
+ ]
160
+ ]
161
+
162
+ subject.tree.should == expected
163
+ end
164
+ end
165
+
166
+ context "with multiple arguments" do
167
+ before do
168
+ subject.html = <<-eos
169
+ {block:Something length="5" offset="2" foo="bar"}{/block:Something}
170
+ eos
171
+ end
172
+
173
+ it "should contain the following tree" do
174
+ expected = [
175
+ [:Literal, " "],
176
+ [:Block, "Something", { "length" => "5", "offset" => "2", "foo" => "bar" }]
177
+ ]
178
+
179
+ subject.tree.should == expected
180
+ end
181
+ end
182
+ end
183
+
184
+ describe "tumblr-boilerplate" do
185
+ before do
186
+ # https://github.com/davesantos/tumblr-boilerplate/master/tumblr.html
187
+ subject.html = open(File.join(FIXTURE_PATH, "themes", "tumblr-boilerplate.html")).read
188
+ end
189
+
190
+ it "should contain exacty one Posts block" do
191
+ matches = subject.tree.select do |n|
192
+ n[0] == :Block && n[1] == "Posts"
193
+ end
194
+
195
+ matches.size.should eql 1
196
+ end
197
+ end
159
198
  end