jenner 0.0.3 → 0.2.6

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