ruhoh 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,13 +1,13 @@
1
1
  source "http://rubygems.org"
2
2
  gemspec
3
3
 
4
- gem 'rake'
5
4
  gem 'rack', "~> 1.4"
6
5
  gem 'directory_watcher', "~> 1.4"
7
6
  gem 'mustache', "~> 0.99"
8
7
  gem 'maruku', "~> 0.6"
9
- gem 'aws-s3'
8
+ gem 'psych', "~> 1.3"
10
9
 
11
10
  group :development do
12
11
  gem 'rspec'
13
- end
12
+ gem 'rake'
13
+ end
data/README.md CHANGED
@@ -7,3 +7,43 @@
7
7
 
8
8
  $ gem install ruhoh
9
9
  $ ruhoh help
10
+
11
+
12
+ - Ruhoh.setup :after_setup
13
+ - Ruhoh::DB.update\_all :after_db_update
14
+ - @page = Ruhoh::Page.new :after_page_initialize
15
+ - templater
16
+ - converter
17
+
18
+
19
+ - setup
20
+ - database
21
+ - page
22
+ - templater
23
+ - converter
24
+ - preview
25
+ - compiler
26
+
27
+ ### Plugins
28
+
29
+
30
+ 1. Mustache method additions
31
+ Extend the templating language
32
+ * Mounted onto the page object
33
+
34
+
35
+ 2. Add additional converter support (textile)
36
+ * Mounted onto the page object
37
+
38
+ 3. Generators ? compose extra pages?
39
+ I'm leaning toward why? any page should be able to be made
40
+ by calling a custom mustache method within a given textfile.
41
+ Jekyll's relevant example is to create mass-pages per each category.
42
+ Generating extra pages can be mounted onto the compiler
43
+
44
+
45
+
46
+
47
+
48
+
49
+
data/dash.html ADDED
@@ -0,0 +1,225 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <style type="text/css">
5
+ /*!
6
+ * Bootstrap v2.0.2
7
+ *
8
+ * Copyright 2012 Twitter, Inc
9
+ * Licensed under the Apache License v2.0
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
13
+ */
14
+ .clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
15
+ .clearfix:after{clear:both;}
16
+ .hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;}
17
+ .input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
18
+ article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
19
+ audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
20
+ audio:not([controls]){display:none;}
21
+ html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
22
+ a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
23
+ a:hover,a:active{outline:0;}
24
+ sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
25
+ sup{top:-0.5em;}
26
+ sub{bottom:-0.25em;}
27
+ img{height:auto;border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;}
28
+ button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
29
+ button,input{*overflow:visible;line-height:normal;}
30
+ button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
31
+ button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
32
+ input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
33
+ input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
34
+ textarea{overflow:auto;vertical-align:top;}
35
+ body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
36
+ a{color:#0088cc;text-decoration:none;}
37
+ a:hover{color:#005580;text-decoration:underline;}
38
+
39
+ /* deep purple: #2E2633 */
40
+ /* deep red #99173C */
41
+ body, html {
42
+ font-size:14px;
43
+ height:100%;
44
+ color: #2E2633;
45
+ }
46
+ a, a:hover {
47
+ color: #2E2633;
48
+ text-decoration:none;
49
+ }
50
+ ul {
51
+ list-style:none;
52
+ margin:0; padding:0;
53
+ }
54
+ #wrapper {
55
+ margin:auto;
56
+ width:500px;
57
+ min-height:100%;
58
+ position:relative;
59
+ border-left:2px solid #99173C;
60
+ }
61
+ .page-pane {
62
+ padding:20px 0;
63
+ width:500px;
64
+ }
65
+
66
+ /* ----------------------------------------- */
67
+ #nav-wrapper {
68
+ position:absolute;
69
+ top:20px;
70
+ left:-202px;
71
+ }
72
+ #nav {
73
+ position:fixed;
74
+ line-height:2em;
75
+ width:200px;
76
+ }
77
+ #nav li {margin-bottom:3px;}
78
+ #nav li a {
79
+ display:block;
80
+ padding:0 25px;
81
+ line-height:50px;
82
+ border-radius:5px 0 0 5px;
83
+ font-size:20px;
84
+ font-weight:bold;
85
+ font-family:verdana;
86
+ text-align:right;
87
+ }
88
+ #nav li a:hover {
89
+ background:#DCE9BE;
90
+ }
91
+ #nav li a.active {
92
+ background:#99173C;
93
+ color:#FFF;
94
+ }
95
+
96
+ /* ----------------------------------------- */
97
+ ul.page-list {
98
+ line-height:1.5em;
99
+ }
100
+ ul.page-list li {
101
+ margin-bottom:3px;
102
+ }
103
+ ul.page-list li a{
104
+ display:block;
105
+ overflow:hidden;
106
+ padding:10px 25px;
107
+ font-family:courier;
108
+ border-radius:0 5px 5px 0;
109
+ line-height:24px;
110
+ }
111
+ ul.page-list li a:hover {
112
+ background:#DCE9BE;
113
+ }
114
+ ul.page-list li a span.title{
115
+ font-size:18px;
116
+ font-weight:bold;
117
+ }
118
+ ul.page-list li a span.date{
119
+ position:absolute;
120
+ right:25px;
121
+ font-size:12px;
122
+ }
123
+ ul.page-list li a span.id {
124
+ font-size:12px;
125
+ }
126
+ </style>
127
+ </head>
128
+ <body>
129
+ <div id="wrapper">
130
+
131
+ <div id="nav-wrapper">
132
+ <ul id="nav">
133
+ <li><a href="#drafts" class="active">Drafts</a></li>
134
+ <li><a href="#public">Posts</a></li>
135
+ <li><a href="#pages">Pages</a></li>
136
+ </ul>
137
+ </div>
138
+
139
+ <div id="drafts" class="page-pane">
140
+ <ul class="page-list">
141
+ {{# db.posts.drafts?to_posts }}
142
+ <li>
143
+ <a href="{{url}}">
144
+ <span class="title">{{title}}</span>
145
+ <span class="date">{{date}}</span>
146
+ <br><span class="id">{{id}}</span></small>
147
+ </a>
148
+ </li>
149
+ {{/ db.posts.drafts?to_posts }}
150
+ </ul>
151
+ </div>
152
+
153
+ <div id="public" class="page-pane" style="display:none">
154
+ <ul class="page-list">
155
+ {{#posts}}
156
+ {{^ type }}
157
+ <li>
158
+ <a href="{{url}}">
159
+ <span class="title">{{title}}</span>
160
+ <span class="date">{{date}}</span>
161
+ <br><span class="id">{{id}}</span></small>
162
+ </a>
163
+ </li>
164
+ {{/ type }}
165
+ {{/posts}}
166
+ </ul>
167
+ </div>
168
+
169
+ <div id="pages" class="page-pane" style="display:none">
170
+ <ul class="page-list">
171
+ {{#pages}}
172
+ <li>
173
+ <a href="{{url}}">
174
+ <span class="title">{{title}}</span>
175
+ <span class="date">{{date}}</span>
176
+ <br><span class="id">{{id}}</span></small>
177
+ </a>
178
+ </li>
179
+ {{/pages}}
180
+ </ul>
181
+ </div>
182
+
183
+ </div>
184
+
185
+ <script>
186
+ // Tabs is a small script for showing/hiding the different page lists.
187
+ // This mostly likely only works in modern browsers but
188
+ // I'd rather not add jQuery as a dependency until it's really warranted.
189
+ var Tabs = {
190
+ panes : document.getElementsByClassName('page-pane'),
191
+ nav : document.getElementById('nav'),
192
+ listEntries : this.nav.children,
193
+ links : [],
194
+
195
+ init : function(){
196
+ for (var i = 0; i < Tabs.listEntries.length; ++i) {
197
+ Tabs.links.push(Tabs.listEntries[i].firstChild);
198
+ }
199
+
200
+ Tabs.nav.addEventListener('click', function(e){
201
+ e.preventDefault();
202
+ if (e.target.tagName.toLowerCase() !== 'a') return;
203
+
204
+ Tabs.clearActive();
205
+ Tabs.hidePanes();
206
+ e.target.className = 'active';
207
+ var id = e.target.href.split('#')[1];
208
+ document.getElementById(id).style.display = 'block';
209
+ }, false)
210
+ },
211
+
212
+ clearActive : function(){
213
+ for (var i = 0; i < Tabs.links.length; ++i)
214
+ Tabs.links[i].className = '';
215
+ },
216
+
217
+ hidePanes : function(){
218
+ for (var i = 0; i < Tabs.panes.length; ++i)
219
+ Tabs.panes[i].style.display = 'none';
220
+ }
221
+ }
222
+ Tabs.init();
223
+ </script>
224
+ </body>
225
+ </html>
data/history.txt CHANGED
@@ -1,3 +1,18 @@
1
+
2
+ 4.5.2012
3
+ 0.2.0 - API changes:
4
+ - [change] Dates in post filenames are now optional but will be required in metadata.
5
+ - [remove] _draft folder. Drafts are now simply a 'type' of post.
6
+ - [remove] publish and un-publish in favor of specifying 'type' meta attribute.
7
+ - [add] titleize method to client which renames draft filenames to their titles if set.
8
+ - [change] /_draft panel is now /dash with updated UI.
9
+ - [change] - rackup configuration is now through Ruhoh::Program.preview.
10
+ - FEATURES:
11
+ - Maintain file extensions for files that don't respond to a converter.
12
+ - Add 'development'/'production' environment configuration flag.
13
+ - @ben-biddington Introduces local temporary directory as SampleSitePath.
14
+ - @ben-biddington adds Travis integration.
15
+ 0.1.4 - BUG: Fix invalid byte sequence in US-ASCII by forcing UTF-8
1
16
  19.4.2012
2
17
  0.1.3 - BUG: Fix drafts not maintaining file extension when publishing
3
18
  BUG: tags helper should use tags database.
@@ -7,6 +7,7 @@ class Ruhoh
7
7
  BlogScaffold = 'git://github.com/ruhoh/blog.git'
8
8
 
9
9
  def initialize(data)
10
+ @iterator = 0
10
11
  self.setup_paths
11
12
  self.setup_options(data)
12
13
 
@@ -58,68 +59,24 @@ class Ruhoh
58
59
  # Public: Create a new draft file.
59
60
  # Requires no settings as it is meant to be fastest way to create content.
60
61
  def draft
61
- filename = File.join(Ruhoh.paths.drafts, "#{Time.now.to_i}.#{@options.ext}")
62
- if File.exist?(filename)
63
- sleep 1 ; self.draft(args) ; exit
64
- end
62
+ begin
63
+ filename = File.join(Ruhoh.paths.posts, "untitled-#{@iterator}.#{@options.ext}")
64
+ @iterator += 1
65
+ end while File.exist?(filename)
65
66
 
66
67
  FileUtils.mkdir_p File.dirname(filename)
67
- File.open(@paths.post_template) do |template|
68
- File.open(filename, 'w') do |post|
69
- post.puts template.read
70
- end
71
- end
72
-
73
- Ruhoh::Friend.say {
74
- green "New draft:"
75
- green Ruhoh.relative_path(filename)
76
- green 'View drafts at the URL: /_drafts'
77
- }
78
- end
79
-
80
- # Public: Publishes the last active draft file.
81
- def publish
82
- id = self.last('draft')
83
- Ruhoh::Friend.say { yellow "No draft to publish." ; exit } if id.nil?
84
- Ruhoh::Friend.say { plain "Publishing draft: #{id}" }
85
- draft = Ruhoh::Parsers::Posts.process_file(id)
86
-
87
- Ruhoh::Friend.say { red "Draft title cannot be blank." ; exit } unless draft['data']['title']
88
- Ruhoh::Friend.say {
89
- red "Invalid date format: #{draft['data']['date']}"
90
- red "Date format must be YYYY-MM-DD."
91
- exit
92
- } unless draft['data']['date']
93
-
94
- draft['data']['ext'] = File.extname(id).gsub('.','')
95
- filename = Ruhoh::Parsers::Posts.to_filename(draft['data'])
96
68
 
97
- if File.exist?(filename)
98
- abort("\e[31m Aborted! \e[0m") if ask("#{filename} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
99
- end
69
+ output = File.open(@paths.post_template) { |f| f.read }
70
+ output = output.gsub('{{DATE}}', Ruhoh::Parsers::Posts.formatted_date(Time.now))
71
+ File.open(filename, 'w') {|f| f.puts output }
100
72
 
101
- FileUtils.mkdir_p File.dirname(filename)
102
- FileUtils.mv id, filename
103
-
104
73
  Ruhoh::Friend.say {
105
- green "Published post:"
74
+ green "New draft:"
106
75
  green Ruhoh.relative_path(filename)
76
+ green 'View drafts at the URL: /dash'
107
77
  }
108
78
  end
109
-
110
- # Public: Unpublishes the last active post file.
111
- def unpublish
112
- post = self.last('post')
113
- Ruhoh::Friend.say { yellow "No post to unpublish." ; exit } if post.nil?
114
- Ruhoh::Friend.say { plain "Unpublishing post: #{post}" }
115
-
116
- FileUtils.mv post, File.join(Ruhoh.paths.drafts, File.basename(post))
117
-
118
- Ruhoh::Friend.say {
119
- yellow "Unpublished post:"
120
- yellow Ruhoh.relative_path(post)
121
- }
122
- end
79
+ alias_method :post, :draft
123
80
 
124
81
  # Public: Create a new page file.
125
82
  def page
@@ -149,6 +106,19 @@ class Ruhoh
149
106
  }
150
107
  end
151
108
 
109
+ # Public: Update draft filenames to their corresponding titles.
110
+ def titleize
111
+ Ruhoh::Parsers::Posts.files.each do |file|
112
+ next unless File.basename(file) =~ /^untitled/
113
+ parsed_page = Ruhoh::Utils.parse_file(file)
114
+ next unless parsed_page['data']['title']
115
+ new_name = Ruhoh::Parsers::Posts.to_slug(parsed_page['data']['title'])
116
+ new_file = File.join(File.dirname(file), "#{new_name}#{File.extname(file)}")
117
+ FileUtils.mv(file, new_file)
118
+ Ruhoh::Friend.say { green "Renamed #{file} to: #{new_file}" }
119
+ end
120
+ end
121
+
152
122
  # Public: Compile to static website.
153
123
  def compile
154
124
  Ruhoh::Compiler.new(@args[1]).compile
@@ -256,7 +226,7 @@ class Ruhoh
256
226
  # Return the payload hash for inspection/study.
257
227
  def payload
258
228
  require 'pp'
259
- Ruhoh::DB.update!
229
+ Ruhoh::DB.update_all
260
230
  Ruhoh::Friend.say {
261
231
  plain Ruhoh::Templaters::Base.build_payload.pretty_inspect
262
232
  }
@@ -264,9 +234,20 @@ class Ruhoh
264
234
 
265
235
  # Internal: Outputs a list of the given data-type to the terminal.
266
236
  def list(type)
267
- Ruhoh::DB.update(type)
268
- data = Ruhoh::DB.__send__(type)
269
- data = data['dictionary'] if type == :posts
237
+ data = case type
238
+ when :posts
239
+ Ruhoh::DB.update(:posts)
240
+ Ruhoh::DB.posts['dictionary']
241
+ when :drafts
242
+ Ruhoh::DB.update(:posts)
243
+ drafts = Ruhoh::DB.posts['drafts']
244
+ h = {}
245
+ drafts.each {|id| h[id] = Ruhoh::DB.posts['dictionary'][id]}
246
+ h
247
+ when :pages
248
+ Ruhoh::DB.update(:pages)
249
+ Ruhoh::DB.pages
250
+ end
270
251
 
271
252
  if @options.verbose
272
253
  Ruhoh::Friend.say {
@@ -298,8 +279,6 @@ class Ruhoh
298
279
  case type
299
280
  when 'post'
300
281
  Ruhoh::Parsers::Posts.files
301
- when 'draft'
302
- Ruhoh::Parsers::Drafts.files
303
282
  when 'page'
304
283
  Ruhoh::Parsers::Pages.files
305
284
  else
@@ -15,22 +15,18 @@ commands:
15
15
  "desc" : |
16
16
  Compile to static website.
17
17
  -
18
- "command" : "draft"
18
+ "command" : "post | draft"
19
19
  "desc" : |
20
20
  Create a new draft.
21
- -
22
- "command" : "publish"
23
- "desc" : |
24
- Publish the last active draft file.
25
- -
26
- "command" : "unpublish"
27
- "desc" : |
28
- UnPublish the last active post file.
29
21
  -
30
22
  "command" : "page <path>"
31
23
  "desc" : |
32
24
  Create a new page at the given path.
33
25
  -
26
+ "command" : "titleize"
27
+ "desc" : |
28
+ Update draft filenames to their corresponding titles. Drafts without titles are ignored.
29
+ -
34
30
  "command" : "drafts"
35
31
  "desc" : |
36
32
  List all drafts.
@@ -3,7 +3,10 @@ class Ruhoh
3
3
  class Compiler
4
4
 
5
5
  def initialize(target_directory)
6
- Ruhoh::DB.update!
6
+ Ruhoh.config.env ||= 'production'
7
+ Ruhoh::Friend.say { plain "Compiling for environment: '#{Ruhoh.config.env}'" }
8
+
9
+ Ruhoh::DB.update_all
7
10
  @target = target_directory || "./#{Ruhoh.folders.compiled}"
8
11
  @page = Ruhoh::Page.new
9
12
  end
@@ -21,11 +24,11 @@ class Ruhoh
21
24
 
22
25
  def pages
23
26
  FileUtils.cd(@target) {
24
- Ruhoh::DB.posts['dictionary'].merge(Ruhoh::DB.pages).each_value do |p|
27
+ Ruhoh::DB.all_pages.each_value do |p|
25
28
  @page.change(p['id'])
26
29
 
27
30
  FileUtils.mkdir_p File.dirname(@page.compiled_path)
28
- File.open(@page.compiled_path, 'w') { |p| p.puts @page.render }
31
+ File.open(@page.compiled_path, 'w:UTF-8') { |p| p.puts @page.render }
29
32
 
30
33
  Ruhoh::Friend.say { green "processed: #{p['id']}" }
31
34
  end
data/lib/ruhoh/db.rb CHANGED
@@ -1,12 +1,8 @@
1
- require "observer"
2
-
3
1
  class Ruhoh
4
-
5
2
  # Public: Database class for interacting with "data" in Ruhoh.
6
3
  class DB
7
4
  class << self
8
- include Observable
9
- WhiteList = [:site, :posts, :drafts, :pages, :routes, :layouts, :partials]
5
+ WhiteList = [:site, :posts, :pages, :routes, :layouts, :partials]
10
6
  self.__send__ :attr_reader, *WhiteList
11
7
 
12
8
  def update(name)
@@ -18,8 +14,6 @@ class Ruhoh
18
14
  Ruhoh::Parsers::Routes.generate
19
15
  when :posts
20
16
  Ruhoh::Parsers::Posts.generate
21
- when :drafts
22
- Ruhoh::Parsers::Drafts.generate
23
17
  when :pages
24
18
  Ruhoh::Parsers::Pages.generate
25
19
  when :layouts
@@ -30,18 +24,18 @@ class Ruhoh
30
24
  raise "Data type: '#{name}' is not a valid data type."
31
25
  end
32
26
  )
33
- changed
34
- notify_observers(name)
35
27
  end
36
-
37
- def update!
28
+
29
+ def all_pages
30
+ self.posts['dictionary'].merge(self.pages)
31
+ end
32
+
33
+ def update_all
38
34
  WhiteList.each do |var|
39
35
  self.__send__ :update, var
40
36
  end
41
37
  end
42
38
 
43
39
  end #self
44
-
45
40
  end #DB
46
-
47
41
  end #Ruhoh
data/lib/ruhoh/page.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  class Ruhoh
2
-
3
2
  class Page
4
3
  attr_reader :id, :data, :content, :sub_layout, :master_layout
5
4
  attr_accessor :templater, :converter
@@ -11,12 +10,10 @@ class Ruhoh
11
10
 
12
11
  # Public: Change this page using an id.
13
12
  def change(id)
14
- @data = nil
13
+ self.reset
15
14
  @path = id
16
15
  @data = if id =~ Regexp.new("^#{Ruhoh.folders.posts}")
17
16
  Ruhoh::DB.posts['dictionary'][id]
18
- elsif id =~ Regexp.new("^#{Ruhoh.folders.drafts}")
19
- Ruhoh::DB.drafts[id]
20
17
  else
21
18
  @path = "#{Ruhoh.folders.pages}/#{id}"
22
19
  Ruhoh::DB.pages[id]
@@ -26,26 +23,15 @@ class Ruhoh
26
23
  @id = id
27
24
  end
28
25
 
29
- # Public: Change this page using a URL.
30
- def change_with_url(url)
31
- id = if url =~ Regexp.new("^/#{Ruhoh.folders.drafts}")
32
- url.gsub(/^\//,'')
33
- else
34
- Ruhoh::DB.routes[url]
35
- end
36
- raise "Page id not found for url: #{url}" unless id
37
- self.change(id)
38
- end
39
-
40
26
  def render
41
- raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
27
+ self.ensure_id
42
28
  self.process_layouts
43
29
  self.process_content
44
30
  @templater.render(self)
45
31
  end
46
32
 
47
33
  def process_layouts
48
- raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
34
+ self.ensure_id
49
35
  if @data['layout']
50
36
  @sub_layout = Ruhoh::DB.layouts[@data['layout']]
51
37
  raise "Layout does not exist: #{@data['layout']}" unless @sub_layout
@@ -61,7 +47,7 @@ class Ruhoh
61
47
  # in order to invoke converters on the result.
62
48
  # Converters (markdown) always choke on the templating language.
63
49
  def process_content
64
- raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
50
+ self.ensure_id
65
51
  data = Ruhoh::Utils.parse_file(Ruhoh.paths.site_source, @path)
66
52
  raise "Invalid Frontmatter in page: #{@path}" if data.empty?
67
53
 
@@ -72,7 +58,7 @@ class Ruhoh
72
58
  # Public: Return page attributes suitable for inclusion in the
73
59
  # 'payload' of the given templater.
74
60
  def attributes
75
- raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
61
+ self.ensure_id
76
62
  @data['content'] = @content
77
63
  @data
78
64
  end
@@ -81,13 +67,24 @@ class Ruhoh
81
67
  #
82
68
  # Returns: [String] The relative path to the compiled file for this page.
83
69
  def compiled_path
84
- raise "ID is null: Id must be set via page.change(id) or page.change_with_url(url)" if @id.nil?
70
+ self.ensure_id
85
71
  path = CGI.unescape(@data['url']).gsub(/^\//, '') #strip leading slash.
86
72
  path = "index.html" if path.empty?
87
- path += '/index.html' unless path =~ /\.html$/
73
+ path += '/index.html' unless path =~ /\.\w+$/
88
74
  path
89
75
  end
90
76
 
77
+ def reset
78
+ @id = nil
79
+ @data = nil
80
+ @content = nil
81
+ @sub_layout = nil
82
+ @master_layout = nil
83
+ end
84
+
85
+ def ensure_id
86
+ raise '@page ID is null: ID must be set via page.change(id) or page.change_with_url(url)' if @id.nil?
87
+ end
88
+
91
89
  end #Page
92
-
93
90
  end #Ruhoh
@@ -5,13 +5,18 @@ class Ruhoh
5
5
  # Generate layouts only from the active theme.
6
6
  def self.generate
7
7
  layouts = {}
8
-
8
+ invalid = []
9
9
  self.files.each do |filename|
10
10
  id = File.basename(filename, File.extname(filename))
11
11
  layouts[id] = Ruhoh::Utils.parse_file(Ruhoh.paths.layouts, filename)
12
- raise "Invalid Frontmatter in layout: #{filename}" if layouts[id].empty?
12
+
13
+ if layouts[id].empty?
14
+ error = "Invalid YAML Front Matter. Ensure this page has valid YAML, even if it's empty."
15
+ invalid << [filename, error]
16
+ end
13
17
  end
14
18
 
19
+ Ruhoh::Utils.report('Layouts', layouts, invalid)
15
20
  layouts
16
21
  end
17
22
 
@@ -25,6 +30,7 @@ class Ruhoh
25
30
  }
26
31
  end
27
32
 
33
+
28
34
  end #Layouts
29
35
  end #Parsers
30
36
  end #Ruhoh