jenner 0.0.3 → 0.2.6

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/README.md CHANGED
@@ -34,30 +34,58 @@ Your generated site will be in `./public`.
34
34
 
35
35
  ### Items
36
36
 
37
- Here's an example (example.html):
37
+ Items can be created as HTML or [Haml](http://haml.info/) files.
38
+
39
+ Here's an example HTML item:
38
40
 
39
41
  ---
40
42
  title: 'Example Page'
41
43
  date: 2014-01-23 17:02:00 -6
42
- template: 'page'
44
+ template: 'page.html'
43
45
  ---
44
46
 
47
+ <h1>{{self.title}}</h1>
45
48
  <p>This is an example page.</p>
46
49
 
50
+ Here's the same example using Haml + Liquid:
51
+
52
+ ---
53
+ title: 'Example Page'
54
+ date: 2014-01-23 17:02:00 -6
55
+ template: 'page.html'
56
+ ---
57
+
58
+ %h1 {{self.title}}
59
+ %p This is an example page.
60
+
61
+ Here's the same example using Haml directly, not Liquid:
62
+
63
+ ---
64
+ title: 'Example Page'
65
+ date: 2014-01-23 17:02:00 -6
66
+ template: 'page.html'
67
+ ---
68
+
69
+ %h1= self.title
70
+ %p This is an example page.
71
+
72
+ It's your choice: HTML, HTML + Liquid, Haml, Haml+ Liquid.
73
+
47
74
  This item will be rendered using the `page` template. The `page`
48
75
  template can use the following data:
49
76
 
50
77
  {{item.title}} => "Example Page"
51
78
  {{item.data}} => 2014-01-23 17:02:00 -6
52
- {{item.template_name}} => "page"
79
+ {{item.template_path}} => "page.html"
53
80
  {{item.body}} => "<p>This is an example page.</p>
81
+ {{item.url}} => "/example.html"
54
82
 
55
83
  You can define additional pieces of data in the item header like this:
56
84
 
57
85
  ---
58
86
  title: 'Example Page'
59
87
  date: 2014-01-23 17:02:00 -6
60
- template: 'page'
88
+ template: 'page.html'
61
89
  foo: 'bar'
62
90
  answer: 42
63
91
  ---
@@ -74,31 +102,150 @@ they will be processed as Markdown.
74
102
  ---
75
103
  title: 'Example Markdown Page'
76
104
  date: 2014-01-23 17:02:00 -6
77
- template: 'page'
105
+ template: 'page.html'
78
106
  ---
79
107
 
80
108
  # This is an example page
81
109
 
82
-
83
110
  ### Templates
84
111
 
85
- Templates are just HTML files that use Liquid markup. Every item you
86
- create is rendered with a template that you specify in the item's
87
- header.
112
+ Templates are just HTML or [Haml](http://haml.info/) files that use
113
+ Liquid markup. Every item you create is rendered with a template that
114
+ you specify in the item's header via the `template` attribute.
115
+
116
+ Haml templates can use Liquid, but they don't have to. You could simply
117
+ use the template's context in Haml:
118
+
119
+ %h1= item.title
120
+ = item.body
121
+
122
+ instead of:
123
+
124
+ %h1 {{item.title}}
125
+ {{item.body}}
88
126
 
89
127
  Every item provides the following data at minimum:
90
128
 
91
129
  {{item.title}}
92
130
  {{item.date}}
93
- {{item.template_name}}
131
+ {{item.template_path}}
94
132
  {{item.body}}
133
+ {{item.url}}
134
+ {{site}}
95
135
 
96
136
  Additional pieces of data are available within `{{item.data}}` if they
97
137
  are defined in the item's YAML header.
98
138
 
99
139
  You can include other templates with the `{% include %}` tag.
100
140
 
101
- {% include 'some_other_template' %}
141
+ {% include 'header.html' %}
142
+ {{item.body}}
143
+ {% include 'footer.html' %}
144
+
145
+ ### Assets
146
+
147
+ All your other files/subdirectories will be copied over as-is with two
148
+ exceptions:
149
+
150
+ 1. [Sass](http://sass-lang.com/) `.scss` files will be processed and copied over as `.css`
151
+ 2. Filenames starting with _ will be ignored (e.g. _hidden.html)
152
+
153
+ ### Tags
154
+
155
+ Tags are a special addition to the YAML header. You can specify them as
156
+ a YAML string array e.g.:
157
+
158
+ ---
159
+ title: 'Example Page'
160
+ date: 2014-01-23 17:02:00 -6
161
+ template: 'page.html'
162
+ tags: [one, two, three]
163
+ ---
164
+
165
+ You will have access to the tag names in {{item.tags}}. You can also get
166
+ a tag by name with a Liquid filter.
167
+
168
+ {{ 'one' | tag | assign_to: my_tag }}
169
+
170
+ {% for item in my_tag.items %}
171
+ {{item.body}}
172
+ {% endfor %}
173
+
174
+
175
+ ### Other Liquid Filters
176
+
177
+ I personally dislike having to write specialized plugins or generators
178
+ to generate my site. By simply adding a couple of filters to Liquid, we
179
+ can easily do just about anything on the item/page level without having
180
+ to write anymore outside Ruby.
181
+
182
+ <h1>Page rendering two items</h1>
183
+ {{ 'item_one.html' | item_from_path | assign_to: item_one }}
184
+ {{ 'item_two.html' | item_from_path | assign_to: item_two }}
185
+
186
+ <table>
187
+ <tr>
188
+ <th>{{item_one.title}}</th>
189
+ <th>{{item_two.title}}</th>
190
+ </tr>
191
+ <tr>
192
+ <td>{{item_one.body}}</td>
193
+ <td>{{item_two.body}}</td>
194
+ </tr>
195
+ <table>
196
+
197
+ <h1>Bunch of items</h1>
198
+ {{ 'blog\/' | items_from_path | assign_to: blog_posts }}
199
+ {% for blog_post in blog_posts %}
200
+ do something with {{blog_post}} here
201
+ {% endfor %}
202
+
203
+ Some other useful helpers:
204
+
205
+ {{ 'test.css' | stylesheet_tag }}
206
+ {{ 'test.js' | javascript_tag }}
207
+ {{ 'page_one.html' | item_from_path | link_to }}
208
+ {{ 'test.gif' | asset_from_path | assign_to: my_image }}
209
+ <img src="{{my_image.url}}" />
210
+
211
+ You can also use a couple of filters to grab items by custom data or
212
+ custom data and value combination. For example, say you have a few items
213
+ with an author attribute.
214
+
215
+ {{'author' | items_with_data | assign_to: items_with_author_defined}}
216
+
217
+ Or maybe you just want a certain author:
218
+
219
+ {{'author' | items_with_data: 'Mark Twain' | assign_to: items_twain_wrote }}
220
+
221
+ If the custom data you created can be an array, it works on that too:
222
+
223
+ {{'favorite_colors' | items_with_data: 'blue' | assign_to: items_who_like_blue_and_possibly_other_colors }}
224
+
225
+ ### Custom URL Formatting
226
+
227
+ By default, all items will be copied over as-is e.g. if your item
228
+ resides in /blog/2014/0128_test_post.html it will end up in
229
+ /public/blog/2014/0128_test_post.html.
230
+
231
+ You can specify a custom url_format parameter in your YAML item header.
232
+
233
+ ---
234
+ title: 'test post'
235
+ date: 2014-01-28 15:23:00 -6
236
+ template: 'post.html'
237
+ url_format: '%Y/%m/%d/:title'
238
+ ---
239
+
240
+ The formatting will be relative to whatever directory the file resides
241
+ in, and will be processed by using [Ruby's Time#strftime](http://www.ruby-doc.org/core-1.9.3/Time.html#method-i-strftime)
242
+ method on the item's date, and `:title` will be replaced with an
243
+ underscored version of your site's title.
244
+
245
+ If the above example post resides in /blog, the output file will go to:
246
+ /public/blog/2014/01/28/test-post/index.html. The `{{item.url}}`
247
+ parameter will not include the index.html to keep things pretty. It will
248
+ be: `/blog/2014/01/28/test-post`.
102
249
 
103
250
 
104
251
  ## Contributing
data/bin/jenner CHANGED
@@ -17,4 +17,13 @@ Mercenary.program(:jenner) do |p|
17
17
  Jenner.build(args, options)
18
18
  end
19
19
  end
20
+
21
+ p.command(:serve) do |c|
22
+ c.syntax "jenner serve"
23
+ c.description "Serve your Jenner site"
24
+
25
+ c.action do |args, options|
26
+ Jenner.serve(args, options)
27
+ end
28
+ end
20
29
  end
@@ -17,6 +17,7 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
+ gem.add_dependency "haml", "~> 4.0.5"
20
21
  gem.add_dependency "liquid", "~> 2.6.1"
21
22
  gem.add_dependency "maruku", "~> 0.7.1"
22
23
  gem.add_dependency "mercenary", "~> 0.2.1"
@@ -1,10 +1,16 @@
1
1
  require "bundler/setup"
2
+ require "haml"
2
3
  require "liquid"
3
4
  require "maruku"
5
+ require "ostruct"
4
6
  require "sass"
7
+ require "webrick"
5
8
 
9
+ require "jenner/asset"
6
10
  require "jenner/item"
11
+ require "jenner/liquid_filters"
7
12
  require "jenner/site"
13
+ require "jenner/tag"
8
14
  require "jenner/template"
9
15
  require "jenner/template_file_system"
10
16
  require "jenner/version"
@@ -15,4 +21,33 @@ module Jenner
15
21
  puts "Building a site at #{@site.root}"
16
22
  @site.generate!
17
23
  end
24
+
25
+ def self.serve(args, options={})
26
+ root = File.expand_path("./public")
27
+ if Dir.exists?(root)
28
+ puts "Starting server on port 9191"
29
+ server = WEBrick::HTTPServer.new :Port => 9191, :DocumentRoot => root
30
+ trap 'INT' do server.shutdown end
31
+
32
+ server.start
33
+ else
34
+ puts "Site does not appear to be built. Run 'jenner build' first"
35
+ end
36
+ end
37
+
38
+ def self.deep_struct(obj)
39
+ case obj
40
+ when Hash
41
+ obj = obj.clone
42
+ obj.each do |key,value|
43
+ obj[key] = Jenner.deep_struct(value)
44
+ end
45
+ OpenStruct.new(obj)
46
+ when Array
47
+ obj = obj.clone
48
+ obj.map! {|i| Jenner.deep_struct(i) }
49
+ else
50
+ obj
51
+ end
52
+ end
18
53
  end
@@ -0,0 +1,52 @@
1
+ module Jenner
2
+ class Asset
3
+ attr_reader :path
4
+ def initialize(path, site)
5
+ @path = path
6
+ @site = site
7
+ end
8
+
9
+ def filename
10
+ File.basename(@path)
11
+ end
12
+
13
+ def sass?
14
+ File.extname(filename) == ".scss"
15
+ end
16
+
17
+ def output_filename
18
+ sass? ? filename.sub(".scss",".css") : filename
19
+ end
20
+
21
+ def source_path
22
+ File.join(@site.root,"_site",@path)
23
+ end
24
+
25
+ def output_path
26
+ @path.sub(filename, output_filename)
27
+ end
28
+
29
+ def url
30
+ "/#{output_path}"
31
+ end
32
+
33
+ def public_path
34
+ File.join(@site.root,"public", output_path)
35
+ end
36
+
37
+ def to_liquid
38
+ {
39
+ 'url' => url
40
+ }
41
+ end
42
+
43
+ def generate!
44
+ if sass?
45
+ engine = Sass::Engine.new(File.read(source_path), syntax: :scss)
46
+ File.write(public_path, engine.render)
47
+ else
48
+ FileUtils.cp source_path, public_path
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,64 +1,131 @@
1
+ # encoding: utf-8
1
2
  module Jenner
2
3
  class Item
3
- attr_reader :body, :title, :date, :template_name, :data
4
- def initialize(filename, site)
5
- @filename = filename
4
+ attr_reader :title, :date, :template_path, :data, :tags
5
+ def initialize(path, site)
6
+ @path = path
6
7
  @site = site
7
8
 
8
- @body = File.read(File.join(@site.root,'_site',@filename), encoding: "US-ASCII")
9
+ @body = File.read(File.join(@site.root,'_site',@path))
9
10
 
10
11
  if @body =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
11
12
  @body = $'
12
13
  @header = YAML.load($1)
13
14
  end
14
15
 
15
- @title = @header.delete("title")
16
- @date = @header.delete("date")
17
- @template_name = @header.delete("template")
18
- @tags = @header.delete("tags")
19
- @data = @header
16
+ begin
17
+ @title = @header.delete("title")
18
+ @date = @header.delete("date")
19
+ @template_path = @header.delete("template")
20
+ @tags = @header.delete("tags") || []
21
+ @url_format = @header.delete("url_format")
22
+ @data = @header
23
+ rescue Exception => e
24
+ raise "Invalid header data on item at path: #{@path}"
25
+ end
26
+ end
27
+
28
+ def relative_path
29
+ path = File.dirname(@path)
30
+ return "" if path == "."
31
+
32
+ "#{path}/"
33
+ end
34
+
35
+ def local_path
36
+ @path
37
+ end
38
+
39
+ def path
40
+ return @path if @url_format.nil?
41
+
42
+ if File.extname(url) == ""
43
+ "#{url}/index.html"
44
+ else
45
+ url
46
+ end
47
+ end
48
+
49
+ def output_filename
50
+ return "index.html" if File.extname(url) == ""
51
+
52
+ File.basename(@path).sub(".markdown",".html")
53
+ end
54
+
55
+ def input_filename
56
+ File.extname(@path)
20
57
  end
21
58
 
22
59
  def url
23
- "/#{@filename}"
60
+ @url_format.nil? ? "/#{@path.sub(".markdown",".html")}" : formatted_url
61
+ end
62
+
63
+ def underscored_title
64
+ @title.gsub(/[^\w]+/,"-").downcase
65
+ end
66
+
67
+ def formatted_url
68
+ "/#{relative_path}#{@date.strftime(@url_format).gsub(":title", underscored_title)}"
24
69
  end
25
70
 
26
71
  def template
27
- Jenner::Template.from_file(File.join(@site.root,'_templates',"#{@template_name}.html"), @site)
72
+ Jenner::Template.from_file(File.join(@site.root,'_templates',"#{@template_path}"), @site)
28
73
  end
29
74
 
30
- def to_liquid
75
+ def to_liquid_without_body
31
76
  {
32
77
  'title' => @title,
33
78
  'date' => @date,
34
- 'template_name' => @template_name,
79
+ 'template_path' => @template_path,
35
80
  'tags' => @tags,
36
81
  'data' => @data,
37
- 'url' => url
82
+ 'url' => url,
83
+ 'site' => @site
38
84
  }
39
85
  end
40
86
 
41
- def markdown(s)
42
- return s unless @filename.split('.').last == "markdown"
87
+ def to_liquid
88
+ to_liquid_without_body.merge(
89
+ 'body' => body
90
+ )
91
+ end
92
+
93
+ def markdown?
94
+ File.extname(@path) == ".markdown"
95
+ end
43
96
 
44
- Maruku.new(s).to_html
97
+ def haml?
98
+ File.extname(@path) == ".haml"
99
+ end
100
+
101
+ def liquid_body
102
+ Liquid::Template.parse(@body).render({'self' => self.to_liquid_without_body}, registers: { site: @site })
45
103
  end
46
104
 
47
105
  def body
48
- markdown(Liquid::Template.parse(@body).render('self' => self))
106
+ if haml?
107
+ Haml::Engine.new(liquid_body).render(Jenner.deep_struct(self.to_liquid_without_body), :site => Jenner.deep_struct(@site.to_liquid))
108
+ elsif markdown?
109
+ Maruku.new(liquid_body).to_html
110
+ else
111
+ liquid_body
112
+ end
49
113
  end
50
114
 
51
115
  def render
52
116
  template.render(
53
- 'item' => self.to_liquid.merge('body' => body)
117
+ 'item' => self.to_liquid
54
118
  )
55
119
  end
56
120
 
57
121
  def public_path
58
- File.join(@site.root,'public',@filename)
122
+ File.join(@site.root,'public',path.sub('.markdown','.html').sub(".haml",".html"))
59
123
  end
60
124
 
61
125
  def generate!
126
+ return if File.basename(public_path)[0] == "_"
127
+
128
+ FileUtils.mkdir_p(File.dirname(public_path))
62
129
  File.write(public_path,render)
63
130
  end
64
131
  end
@@ -0,0 +1,56 @@
1
+ module Jenner
2
+ module LiquidFilters
3
+
4
+ def asset_from_path(path)
5
+ @context.registers[:site].assets.find { |asset| asset.path == path } || "Asset with path '#{path}' not found"
6
+ end
7
+
8
+ def item_from_path(path)
9
+ @context.registers[:site].items.find { |item| item.local_path == path } || "Item with path '#{path}' not found"
10
+ end
11
+
12
+ def items_from_path(path)
13
+ @context.registers[:site].items.select {|item| item.local_path =~ /^#{path}/ } || []
14
+ end
15
+
16
+ def tag(name)
17
+ @context.registers[:site].tags.find { |tag| tag.name == name } || "Tag with name '#{name}' not found"
18
+ end
19
+
20
+ def items_with_data(key, value=nil)
21
+ key_matches = @context.registers[:site].items.select { |item|
22
+ item.data.keys.include?(key)
23
+ }
24
+ return key_matches if value.nil?
25
+
26
+ # value was provided
27
+ key_matches.select do |item|
28
+ if item.data[key].is_a?(Array)
29
+ item.data[key].include?(value)
30
+ else
31
+ item.data[key] == value
32
+ end
33
+ end
34
+ end
35
+
36
+ def assign_to(value, name)
37
+ @context[name] = value
38
+ nil
39
+ end
40
+
41
+ def stylesheet_tag(path)
42
+ %(<link href="#{path}" media="all" rel="stylesheet" type="text/css" />)
43
+ end
44
+
45
+ def javascript_tag(path)
46
+ %(<script type="text/javascript" src="#{path}"></script>)
47
+ end
48
+
49
+ def link_to(item)
50
+ %(<a href="#{item.url}">#{item.title}</a>)
51
+ end
52
+
53
+ end
54
+ end
55
+
56
+ Liquid::Template.register_filter(Jenner::LiquidFilters)
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  module Jenner
2
3
  class Site
3
4
  attr_reader :root
@@ -7,22 +8,45 @@ module Jenner
7
8
  Liquid::Template.file_system = Jenner::TemplateFileSystem.new(File.join(@root,'_templates'))
8
9
  end
9
10
 
11
+ def to_liquid
12
+ {
13
+ 'root' => @root,
14
+ 'assets' => assets,
15
+ 'items' => items,
16
+ 'tag_names' => tag_names,
17
+ 'tags' => tags
18
+ }
19
+ end
20
+
10
21
  def site_path
11
22
  File.join(@root,"_site")
12
23
  end
13
24
 
14
- def files
15
- Dir.glob(File.join(site_path,"[^_]*.{html,markdown}")) +
16
- Dir.glob(File.join(site_path,"**","[^_]*.{html,markdown}"))
25
+ def all_files
26
+ @all_files ||= Dir.glob(File.join(site_path,"**","*.*"))
27
+ end
28
+
29
+ def item_files
30
+ @item_files ||= all_files.select {|f| ['.html','.markdown'].include? File.extname(f) }
31
+ end
32
+
33
+ def asset_files
34
+ @asset_files ||= all_files - item_files
17
35
  end
18
36
 
19
37
  def relative_path(item)
20
38
  (item.split("/") - site_path.split("/")).join("/")
21
39
  end
22
40
 
41
+ def assets
42
+ @assets ||= asset_files.inject([]) { |a,asset|
43
+ a << Jenner::Asset.new(relative_path(asset), self)
44
+ }
45
+ end
46
+
23
47
  def items
24
- files.inject([]) { |a,i|
25
- a << Jenner::Item.new(relative_path(i), self)
48
+ @items ||= item_files.inject([]) { |a, item|
49
+ a << Jenner::Item.new(relative_path(item), self)
26
50
  }
27
51
  end
28
52
 
@@ -38,30 +62,37 @@ module Jenner
38
62
  site_dir.split("/")
39
63
  end
40
64
 
65
+ def tag_names
66
+ @tag_names ||= items.inject([]) {|a,i|
67
+ a << i.tags
68
+ }.flatten.uniq
69
+ end
70
+
71
+ def tags
72
+ @tags ||= tag_names.inject([]) { |a,tag|
73
+ a << Jenner::Tag.new(tag, self)
74
+ }
75
+ end
76
+
41
77
  def relative_path_to_public(item)
42
78
  (item.split("/") - site_dirs).join("/")
43
79
  end
44
80
 
81
+ def create_directories!
82
+ Dir.glob(File.join(@root,"_site","**")).each do |dir|
83
+ next unless File.directory?(dir)
84
+
85
+ FileUtils.mkdir_p(File.join(public_dir,relative_path_to_public(dir)))
86
+ end
87
+ end
88
+
45
89
  def generate!
46
90
  FileUtils.rm_rf(public_dir)
47
91
  FileUtils.mkdir(public_dir)
48
92
 
49
- base_dirs = File.join(site_dir).split("/")
50
- Dir.glob(File.join(@root,"_site","**","*")).each do |item|
51
- if File.directory?(item)
52
- FileUtils.mkdir_p(File.join(public_dir,relative_path_to_public(item)))
53
- else
54
- next if [".html",".markdown"].include? File.extname(item)
55
- if File.extname(item) == ".scss"
56
- engine = Sass::Engine.new(File.read(item), :syntax => :scss)
57
- item = item.sub(".scss",".css")
58
- File.write(File.join(public_dir,relative_path_to_public(item)),engine.render)
59
- else
60
- destination = File.join(public_dir,relative_path_to_public(item))
61
- FileUtils.cp item, destination
62
- end
63
- end
64
- end
93
+ create_directories!
94
+
95
+ assets.map(&:generate!)
65
96
  items.map(&:generate!)
66
97
  end
67
98
  end
@@ -0,0 +1,24 @@
1
+ module Jenner
2
+ class Tag
3
+ attr_reader :name
4
+ def initialize(name, site)
5
+ @name = name
6
+ @site = site
7
+ end
8
+
9
+ def items
10
+ @items ||= @site.items.select { |item|
11
+ item.tags.include?(@name)
12
+ }.inject([]) {|a, item|
13
+ a << item
14
+ }
15
+ end
16
+
17
+ def to_liquid
18
+ {
19
+ "name" => @name,
20
+ "items" => items
21
+ }
22
+ end
23
+ end
24
+ end
@@ -1,17 +1,34 @@
1
+ # encoding: UTF-8
1
2
  module Jenner
2
3
  class Template
3
- attr_reader :body
4
- def initialize(body, site)
4
+ def initialize(body, site, options={})
5
5
  @body = body
6
6
  @site = site
7
+ @haml = options[:haml]
7
8
  end
8
9
 
9
- def render(context)
10
- Liquid::Template.parse(@body).render(context)
10
+ def haml?
11
+ @haml
12
+ end
13
+
14
+ def body(context={})
15
+ haml? ? Haml::Engine.new(@body).render(Jenner.deep_struct(context)) : @body
16
+ end
17
+
18
+ def render(context={})
19
+ Liquid::Template.parse(body(context.merge('site' => @site))).render(context.merge('site' => @site), registers: { site: @site }).to_s.encode("utf-8")
11
20
  end
12
21
 
13
22
  def self.from_file(file_path, site)
14
- new(File.read(file_path, encoding: 'US-ASCII'), site)
23
+ if File.extname(file_path) != ".haml"
24
+ new(File.read(file_path), site)
25
+ else
26
+ from_haml(File.read(file_path), site)
27
+ end
28
+ end
29
+
30
+ def self.from_haml(haml_body, site)
31
+ new(haml_body, site, haml: true)
15
32
  end
16
33
  end
17
34
  end
@@ -1,20 +1,25 @@
1
+ # encoding: utf-8
1
2
  module Jenner
2
3
  class TemplateFileSystem
3
4
  attr_reader :root
4
5
  def initialize(root)
5
6
  @root = root
6
- @pattern = "%s.html"
7
+ @pattern = "%s"
7
8
  end
8
9
 
9
10
  def read_template_file(template_path, context)
10
11
  full_path = full_path(template_path)
11
12
  raise FileSystemError, "No such template '#{template_path}'" unless File.exists?(full_path)
12
13
 
13
- File.read(full_path)
14
+ if File.extname(template_path) == ".haml"
15
+ Haml::Engine.new(File.read(full_path)).render(Jenner.deep_struct(context.environments.first))
16
+ else
17
+ File.read(full_path)
18
+ end
14
19
  end
15
20
 
16
21
  def full_path(template_path)
17
- raise FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /^[^.\/][a-zA-Z0-9_\/]+$/
22
+ #raise Liquid::FileSystemError, "Illegal template name '#{template_path}'" unless template_path =~ /^[^.\/][a-zA-Z0-9_\/]+$/
18
23
 
19
24
  full_path = if template_path.include?('/')
20
25
  File.join(root, File.dirname(template_path), @pattern % File.basename(template_path))
@@ -22,7 +27,7 @@ module Jenner
22
27
  File.join(root, @pattern % template_path)
23
28
  end
24
29
 
25
- raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /^#{File.expand_path(root)}/
30
+ raise Liquid::FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /^#{File.expand_path(root)}/
26
31
 
27
32
  full_path
28
33
  end
@@ -1,3 +1,3 @@
1
1
  module Jenner
2
- VERSION = "0.0.3"
2
+ VERSION = "0.2.6"
3
3
  end
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: 'hidden'
3
+ date: 2014-01-23 17:02:00 -6
4
+ template: 'wrapper.html'
5
+ foo: 'bar'
6
+ bar: [2]
7
+ ---
8
+ 42
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: 'TEST post'
3
+ date: 2014-01-28 15:23:00 -6
4
+ template: 'post.html'
5
+ url_format: '%Y/%m/%d/:title'
6
+ ---
7
+
8
+ this is a test post.
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: 'embed test'
3
+ date: 2014-01-23 17:02:00 -6
4
+ template: 'simple.html'
5
+ ---
6
+ {{ '_hidden.html' | item_from_path | assign_to: 'foo' }}
7
+ secret: {{foo.body}}
File without changes
@@ -0,0 +1,9 @@
1
+ ---
2
+ title: 'haml test'
3
+ date: 2014-01-23 17:02:00 -6
4
+ template: 'post.html'
5
+ foo: 'bar'
6
+ ---
7
+
8
+ %p liquid: {{self.title}} {{self.data.foo}}
9
+ %p haml: #{self.title} #{self.data.foo}
@@ -1,7 +1,9 @@
1
1
  ---
2
2
  title: 'markdown test'
3
3
  date: 2014-01-23 17:02:00 -6
4
- template: 'wrapper'
4
+ template: 'wrapper.html'
5
+ foo: 'baz'
6
+ bar: [1,2]
5
7
  ---
6
8
 
7
9
  # {{self.title}}
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  title: 'subfile'
3
3
  date: 2014-01-23 17:02:00 -6
4
- template: 'simple'
4
+ template: 'simple.html'
5
5
  ---
6
-
7
6
  subfile
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: 'test'
3
3
  date: 2014-01-23 17:02:00 -6
4
- template: 'wrapper'
4
+ template: 'wrapper.html'
5
5
  tags: [one, two, three]
6
6
  foo: 'bar'
7
7
  ---
@@ -0,0 +1 @@
1
+ %p #{item.body}
@@ -0,0 +1 @@
1
+ {{item.body}}
@@ -1 +1 @@
1
- item: {{item.title}}
1
+ item: {{item.body}}
@@ -1,6 +1,6 @@
1
1
  top
2
2
  {{item.title}}
3
3
  {{item.date}}
4
- {{item.template_name}}
4
+ {{item.template_path}}
5
5
  {{item.body}}
6
6
  bottom
@@ -0,0 +1,50 @@
1
+ require 'helper'
2
+
3
+ class TestAsset < Test::Unit::TestCase
4
+ def setup
5
+ super
6
+ @asset = Jenner::Asset.new('test.scss', @site)
7
+ end
8
+
9
+ def test_filename
10
+ assert_equal "test.scss", @asset.filename
11
+ end
12
+
13
+ def test_sass
14
+ assert @asset.sass?
15
+ end
16
+
17
+ def test_sass_on_an_image
18
+ @asset = Jenner::Asset.new("subdirectory/test.png", @site)
19
+ assert !@asset.sass?
20
+ end
21
+
22
+ def test_output_filename
23
+ assert_equal "test.css", @asset.output_filename
24
+ end
25
+
26
+ def test_url
27
+ assert_equal "/test.css", @asset.url
28
+ end
29
+
30
+ def test_public_path
31
+ assert_equal site_file("public/test.css"), @asset.public_path
32
+ end
33
+
34
+ def test_generate
35
+ FileUtils.mkdir(File.join(@site.root,"public"))
36
+ @asset.generate!
37
+ assert File.exists?(File.join(@site.root,"public","test.css"))
38
+ end
39
+
40
+ def test_generate_on_non_sass
41
+ FileUtils.mkdir(File.join(@site.root,"public"))
42
+ @asset = Jenner::Asset.new("foo.txt", @site)
43
+ @asset.generate!
44
+ assert File.exists?(File.join(@site.root,"public","foo.txt"))
45
+ end
46
+
47
+ def test_to_liquid
48
+ assert_equal({'url' => @asset.url}, @asset.to_liquid)
49
+ end
50
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  require 'helper'
2
3
 
3
4
  class TestItem < Test::Unit::TestCase
@@ -5,7 +6,7 @@ class TestItem < Test::Unit::TestCase
5
6
  item = Jenner::Item.new('test.html',@site)
6
7
  assert_equal "test", item.title
7
8
  assert_equal Time.new(2014,1,23,17,2,0,"-06:00"), item.date
8
- assert_equal "wrapper", item.template_name
9
+ assert_equal "wrapper.html", item.template_path
9
10
  assert_equal({ "foo" => "bar" }, item.data)
10
11
  end
11
12
 
@@ -16,7 +17,7 @@ class TestItem < Test::Unit::TestCase
16
17
 
17
18
  def test_render
18
19
  item = Jenner::Item.new('test.html',@site)
19
- assert_equal "top\ntest\n2014-01-23 17:02:00 -0600\nwrapper\ntest\nbar\nitem content\n\nbottom\n", item.render
20
+ assert_equal "top\ntest\n2014-01-23 17:02:00 -0600\nwrapper.html\ntest\nbar\nitem content\n\nbottom\n", item.render
20
21
  end
21
22
 
22
23
  def test_public_path
@@ -26,16 +27,19 @@ class TestItem < Test::Unit::TestCase
26
27
 
27
28
  def test_generate!
28
29
  item = Jenner::Item.new('test.html',@site)
29
- # make the public dir for the item, since @site normally does it
30
- FileUtils.mkdir(File.join(@site.root,'public'))
31
30
  item.generate!
32
31
  assert File.exists?(File.join(@site.root,'public','test.html'))
33
- assert_equal item.render, File.read(File.join(@site.root,'public','test.html'), encoding: "US-ASCII")
32
+ assert_equal item.render, File.read(File.join(@site.root,'public','test.html'))
34
33
  end
35
34
 
36
35
  def test_markdown_template
37
36
  item = Jenner::Item.new('markdown_test.markdown', @site)
38
- assert_equal "top\nmarkdown test\n2014-01-23 17:02:00 -0600\nwrapper\n\n<h1 id=\"markdown_test\">markdown test</h1>\n\n<p>item content</p>\n\nbottom\n", item.render
37
+ assert_equal "top\nmarkdown test\n2014-01-23 17:02:00 -0600\nwrapper.html\n\n<h1 id=\"markdown_test\">markdown test</h1>\n\n<p>item content</p>\n\nbottom\n", item.render
38
+ end
39
+
40
+ def test_markdown_template_public_path
41
+ item = Jenner::Item.new('markdown_test.markdown', @site)
42
+ assert_equal site_file('public/markdown_test.html'), item.public_path
39
43
  end
40
44
 
41
45
  def test_public_path_on_subdir_item
@@ -45,10 +49,9 @@ class TestItem < Test::Unit::TestCase
45
49
 
46
50
  def test_generate_on_subdir_item
47
51
  item = Jenner::Item.new("subdirectory/subfile.html",@site)
48
- FileUtils.mkdir_p(File.join(@site.root,'public','subdirectory'))
49
52
  item.generate!
50
53
  assert File.exists?(File.join(@site.root,'public','subdirectory','subfile.html'))
51
- assert_equal "item: subfile\n", File.read(File.join(@site.root,'public','subdirectory','subfile.html'))
54
+ assert_equal "item: subfile\n\n", File.read(File.join(@site.root,'public','subdirectory','subfile.html'))
52
55
  end
53
56
 
54
57
  def test_url
@@ -60,4 +63,56 @@ class TestItem < Test::Unit::TestCase
60
63
  item = Jenner::Item.new('subdirectory/subfile.html',@site)
61
64
  assert_equal "/subdirectory/subfile.html", item.url
62
65
  end
66
+
67
+ def test_underscored_title
68
+ item = Jenner::Item.new('blog/20140128_TEST_post.markdown', @site)
69
+ assert_equal "test-post", item.underscored_title
70
+ end
71
+
72
+ def test_relative_path
73
+ item = Jenner::Item.new('blog/20140128_TEST_post.markdown', @site)
74
+ assert_equal "blog/", item.relative_path
75
+ end
76
+
77
+ def test_output_filename_on_custom_url
78
+ item = Jenner::Item.new('blog/20140128_TEST_post.markdown', @site)
79
+ assert_equal "index.html", item.output_filename
80
+ end
81
+
82
+ def test_output_filename_on_regular_markdown_file
83
+ item = Jenner::Item.new('markdown_test.markdown', @site)
84
+ assert_equal "markdown_test.html", item.output_filename
85
+ end
86
+
87
+ def test_custom_url
88
+ item = Jenner::Item.new('blog/20140128_TEST_post.markdown', @site)
89
+ assert_equal "/blog/2014/01/28/test-post", item.url
90
+ end
91
+
92
+ def test_public_path_on_custom_url
93
+ item = Jenner::Item.new('blog/20140128_TEST_post.markdown', @site)
94
+ assert_equal site_file("public/blog/2014/01/28/test-post/index.html"), item.public_path
95
+ end
96
+
97
+ def test_generate_on_custom_url
98
+ item = Jenner::Item.new('blog/20140128_TEST_post.markdown', @site)
99
+ item.generate!
100
+ assert File.exists?(site_file("public/blog/2014/01/28/test-post/index.html"))
101
+ end
102
+
103
+ def test_item_from_path
104
+ item = Jenner::Item.new('embed_test.html', @site)
105
+ assert_equal "item: \nsecret: 42\n\n\n", item.render
106
+ end
107
+
108
+ def test_haml_item
109
+ item = Jenner::Item.new("haml_test.haml", @site)
110
+ assert_equal "<p>liquid: haml test bar</p>\n<p>haml: haml test bar</p>\n\n", item.render
111
+ end
112
+
113
+ def test_haml_generation
114
+ item = Jenner::Item.new("haml_test.haml", @site)
115
+ item.generate!
116
+ assert File.exists?(site_file("public/haml_test.html"))
117
+ end
63
118
  end
@@ -0,0 +1,76 @@
1
+ require 'helper'
2
+
3
+ class TestLiquidFilters < Test::Unit::TestCase
4
+ include Liquid
5
+
6
+ def setup
7
+ super
8
+
9
+ @context = Liquid::Context.new({},{},site: @site)
10
+ @context.add_filters(Jenner::LiquidFilters)
11
+ end
12
+
13
+ def test_item_from_path
14
+ assert Variable.new("'test.html' | item_from_path").render(@context).is_a?(Jenner::Item)
15
+ end
16
+
17
+ def test_items_from_path
18
+ a = Variable.new("'[^\/]+\.html' | items_from_path").render(@context)
19
+ assert a.is_a?(Array)
20
+ assert_equal 3, a.length
21
+ end
22
+
23
+ def test_asset_from_path
24
+ assert Variable.new("'foo.txt' | asset_from_path").render(@context).is_a?(Jenner::Asset)
25
+ end
26
+
27
+ def test_assign_to(value, name)
28
+ assert_nil Variable.new("'bar' | assign_to: 'foo'").render(@context)
29
+ assert_equal "bar", @context['foo']
30
+ end
31
+
32
+ def test_stylesheet_tag
33
+ assert_equal %(<link href="test.css" media="all" rel="stylesheet" type="text/css" />),
34
+ Variable.new("'test.css' | stylesheet_tag").render(@context)
35
+ end
36
+
37
+ def test_javascript_tag
38
+ assert_equal %(<script type="text/javascript" src="test.js"></script>),
39
+ Variable.new("'test.js' | javascript_tag").render(@context)
40
+ end
41
+
42
+ def test_link_to
43
+ assert_equal %(<a href="/test.html">test</a>),
44
+ Variable.new("'test.html' | item_from_path | link_to").render(@context)
45
+ end
46
+
47
+ def test_tag
48
+ assert Variable.new("'one' | tag").render(@context).is_a?(Jenner::Tag)
49
+ end
50
+
51
+ def test_items_with_data
52
+ items = Variable.new("'foo' | items_with_data").render(@context)
53
+ paths = items.map(&:path)
54
+ assert paths.include?("test.html")
55
+ assert paths.include?("_hidden.html")
56
+ assert paths.include?("markdown_test.markdown")
57
+ assert !paths.include?("foo.txt")
58
+ end
59
+
60
+ def test_items_with_data_and_value
61
+ items = Variable.new(%('foo' | items_with_data: "bar")).render(@context)
62
+ paths = items.map(&:path)
63
+ assert paths.include?("test.html")
64
+ assert paths.include?("_hidden.html")
65
+ assert !paths.include?("markdown_test.markdown")
66
+ end
67
+
68
+ def test_items_with_data_and_value_on_array
69
+ items = Variable.new(%('bar' | items_with_data: 2)).render(@context)
70
+ paths = items.map(&:path)
71
+ assert !paths.include?("test.html")
72
+ assert paths.include?("_hidden.html")
73
+ assert paths.include?("markdown_test.markdown")
74
+ end
75
+
76
+ end
@@ -36,4 +36,16 @@ class TestSite < Test::Unit::TestCase
36
36
  assert File.exists?(site_file("public/test.css"))
37
37
  assert_equal "body {\n background-color: blue; }\n", File.read(site_file("public/test.css"), encoding: "US-ASCII")
38
38
  end
39
+
40
+ def test_asset_files
41
+ assert @site.asset_files.include? site_file('_site/test.scss')
42
+ end
43
+
44
+ def test_tag_names
45
+ assert_equal ['one',"three","two"], @site.tag_names.sort
46
+ end
47
+
48
+ def test_tags
49
+ assert @site.tags.first.is_a? Jenner::Tag
50
+ end
39
51
  end
@@ -0,0 +1,22 @@
1
+ require 'helper'
2
+
3
+ class TestTag < Test::Unit::TestCase
4
+ def setup
5
+ super
6
+
7
+ @tag = Jenner::Tag.new('one', @site)
8
+ end
9
+
10
+ def test_name
11
+ assert_equal "one", @tag.name
12
+ end
13
+
14
+ def test_items
15
+ assert_equal "/test.html", @tag.items.first.url
16
+ end
17
+
18
+ def test_to_liquid
19
+ assert_equal "one", @tag.to_liquid["name"]
20
+ assert_equal "/test.html", @tag.to_liquid["items"].first.url
21
+ end
22
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  require 'helper'
2
3
 
3
4
  class TestTemplate < Test::Unit::TestCase
@@ -16,8 +17,44 @@ class TestTemplate < Test::Unit::TestCase
16
17
  assert_equal "hi, {{name}}\n", template.body
17
18
  end
18
19
 
20
+ def test_has_access_to_site_as_liquid
21
+ template = Jenner::Template.new("{{site.root}}", @site)
22
+ assert_equal @site.root, template.render
23
+ end
24
+
19
25
  def test_include
20
- template = Jenner::Template.new("{% include 'test' %}", @site)
26
+ template = Jenner::Template.new("{% include 'test.html' %}", @site)
21
27
  assert_equal "hi, jay\n", template.render('name' => 'jay')
22
28
  end
29
+
30
+ def test_include_haml
31
+ template = Jenner::Template.new("{% include 'haml_template.haml' with item %}", @site)
32
+ assert_equal "<p>foo</p>\n", template.render('item' => {'body' => 'foo'})
33
+ end
34
+
35
+ def test_support_for_haml
36
+ template = Jenner::Template.from_haml("!!! 5", @site)
37
+ assert_equal "<!DOCTYPE html>\n", template.render
38
+ end
39
+
40
+ def test_haml_from_file
41
+ template = Jenner::Template.from_file(template_file('haml_template.haml'), @site)
42
+ assert_equal "<p>foo</p>\n", template.render('item' => { 'body' => 'foo' })
43
+ end
44
+
45
+ def test_haml_can_use_liquid
46
+ template = Jenner::Template.new("%p hi, {{name}}", @site, haml: true)
47
+ assert_equal "<p>hi, jay</p>\n", template.render('name' => 'jay')
48
+ end
49
+
50
+ def test_haml_has_access_to_liquid_context_in_body
51
+ template = Jenner::Template.new(%(%p hi, \#{name}), @site, haml: true)
52
+ assert_equal "<p>hi, jay</p>\n", template.body('name' => 'jay')
53
+ end
54
+
55
+ def test_haml_body_gets_passed_through_with_render
56
+ template = Jenner::Template.new(%(%p hi, \#{name}), @site, haml: true)
57
+ assert_equal "<p>hi, jay</p>\n", template.render('name' => 'jay')
58
+ end
59
+
23
60
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jenner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.2.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-01-25 00:00:00.000000000 Z
12
+ date: 2014-01-30 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: haml
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 4.0.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 4.0.5
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: liquid
16
32
  requirement: !ruby/object:Gem::Requirement
@@ -107,23 +123,35 @@ files:
107
123
  - bin/jenner
108
124
  - jenner.gemspec
109
125
  - lib/jenner.rb
126
+ - lib/jenner/asset.rb
110
127
  - lib/jenner/item.rb
128
+ - lib/jenner/liquid_filters.rb
111
129
  - lib/jenner/site.rb
130
+ - lib/jenner/tag.rb
112
131
  - lib/jenner/template.rb
113
132
  - lib/jenner/template_file_system.rb
114
133
  - lib/jenner/version.rb
115
134
  - test/fixtures/source/_site/_hidden.html
135
+ - test/fixtures/source/_site/blog/20140128_test_post.markdown
136
+ - test/fixtures/source/_site/embed_test.html
137
+ - test/fixtures/source/_site/foo.txt
138
+ - test/fixtures/source/_site/haml_test.haml
116
139
  - test/fixtures/source/_site/markdown_test.markdown
117
140
  - test/fixtures/source/_site/subdirectory/subfile.html
118
141
  - test/fixtures/source/_site/subdirectory/test.png
119
142
  - test/fixtures/source/_site/test.html
120
143
  - test/fixtures/source/_site/test.scss
144
+ - test/fixtures/source/_templates/haml_template.haml
145
+ - test/fixtures/source/_templates/post.html
121
146
  - test/fixtures/source/_templates/simple.html
122
147
  - test/fixtures/source/_templates/test.html
123
148
  - test/fixtures/source/_templates/wrapper.html
124
149
  - test/helper.rb
150
+ - test/jenner/test_asset.rb
125
151
  - test/jenner/test_item.rb
152
+ - test/jenner/test_liquid_filters.rb
126
153
  - test/jenner/test_site.rb
154
+ - test/jenner/test_tag.rb
127
155
  - test/jenner/test_template.rb
128
156
  homepage: http://github.com/unreal/jenner
129
157
  licenses: []
@@ -151,16 +179,25 @@ specification_version: 3
151
179
  summary: Jenner is an opinionated static site generator
152
180
  test_files:
153
181
  - test/fixtures/source/_site/_hidden.html
182
+ - test/fixtures/source/_site/blog/20140128_test_post.markdown
183
+ - test/fixtures/source/_site/embed_test.html
184
+ - test/fixtures/source/_site/foo.txt
185
+ - test/fixtures/source/_site/haml_test.haml
154
186
  - test/fixtures/source/_site/markdown_test.markdown
155
187
  - test/fixtures/source/_site/subdirectory/subfile.html
156
188
  - test/fixtures/source/_site/subdirectory/test.png
157
189
  - test/fixtures/source/_site/test.html
158
190
  - test/fixtures/source/_site/test.scss
191
+ - test/fixtures/source/_templates/haml_template.haml
192
+ - test/fixtures/source/_templates/post.html
159
193
  - test/fixtures/source/_templates/simple.html
160
194
  - test/fixtures/source/_templates/test.html
161
195
  - test/fixtures/source/_templates/wrapper.html
162
196
  - test/helper.rb
197
+ - test/jenner/test_asset.rb
163
198
  - test/jenner/test_item.rb
199
+ - test/jenner/test_liquid_filters.rb
164
200
  - test/jenner/test_site.rb
201
+ - test/jenner/test_tag.rb
165
202
  - test/jenner/test_template.rb
166
203
  has_rdoc: