ruhoh 0.0.2 → 0.0.3

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.
Files changed (87) hide show
  1. data/Gemfile +13 -0
  2. data/README.md +5 -53
  3. data/Rakefile +56 -0
  4. data/bin/ruhoh +23 -14
  5. data/lib/ruhoh/client/client.rb +310 -0
  6. data/lib/ruhoh/client/help.yml +56 -0
  7. data/lib/ruhoh/compiler.rb +16 -19
  8. data/lib/ruhoh/converters/converter.rb +30 -3
  9. data/lib/ruhoh/db.rb +7 -17
  10. data/lib/ruhoh/deployers/s3.rb +71 -0
  11. data/lib/ruhoh/friend.rb +71 -0
  12. data/lib/ruhoh/page.rb +41 -11
  13. data/lib/ruhoh/parsers/drafts.rb +54 -0
  14. data/lib/ruhoh/parsers/layouts.rb +14 -9
  15. data/lib/ruhoh/parsers/pages.rb +47 -35
  16. data/lib/ruhoh/parsers/partials.rb +14 -2
  17. data/lib/ruhoh/parsers/posts.rb +139 -88
  18. data/lib/ruhoh/parsers/routes.rb +4 -8
  19. data/lib/ruhoh/parsers/site.rb +2 -8
  20. data/lib/ruhoh/previewer.rb +48 -0
  21. data/lib/ruhoh/templaters/base.rb +53 -0
  22. data/lib/ruhoh/templaters/helpers.rb +159 -0
  23. data/lib/ruhoh/templaters/rmustache.rb +29 -0
  24. data/lib/ruhoh/utils.rb +25 -7
  25. data/lib/ruhoh/version.rb +1 -1
  26. data/lib/ruhoh/watch.rb +22 -9
  27. data/lib/ruhoh.rb +74 -29
  28. data/ruhoh.gemspec +70 -9
  29. data/scaffolds/blog/_config.yml +33 -0
  30. data/scaffolds/blog/_drafts/.gitkeep +0 -0
  31. data/scaffolds/blog/_posts/.gitkeep +0 -0
  32. data/scaffolds/blog/_site.yml +16 -0
  33. data/scaffolds/blog/_templates/partials/categories_list +3 -0
  34. data/scaffolds/blog/_templates/partials/pages_list +7 -0
  35. data/scaffolds/blog/_templates/partials/posts_collate +9 -0
  36. data/scaffolds/blog/_templates/partials/posts_list +1 -0
  37. data/scaffolds/blog/_templates/partials/tags_list +3 -0
  38. data/scaffolds/blog/_templates/syntax/google_prettify/default.css +52 -0
  39. data/scaffolds/blog/_templates/syntax/google_prettify/desert.css +34 -0
  40. data/scaffolds/blog/_templates/syntax/google_prettify/sons-of-obsidian.css +117 -0
  41. data/scaffolds/blog/_templates/syntax/google_prettify/sunburst.css +51 -0
  42. data/scaffolds/blog/_templates/syntax/google_prettify/twitter-bootstrap.css +30 -0
  43. data/scaffolds/blog/_templates/themes/twitter/bootstrap/css/bootstrap.min.css +689 -0
  44. data/scaffolds/blog/_templates/themes/twitter/bootstrap/img/glyphicons-halflings-white.png +0 -0
  45. data/scaffolds/blog/_templates/themes/twitter/bootstrap/img/glyphicons-halflings.png +0 -0
  46. data/scaffolds/blog/_templates/themes/twitter/css/style.css +68 -0
  47. data/scaffolds/blog/_templates/themes/twitter/layouts/default.html +64 -0
  48. data/scaffolds/blog/_templates/themes/twitter/layouts/page.html +13 -0
  49. data/scaffolds/blog/_templates/themes/twitter/layouts/post.html +55 -0
  50. data/scaffolds/blog/_templates/themes/twitter/manifest.yml +11 -0
  51. data/scaffolds/blog/about.md +5 -0
  52. data/scaffolds/blog/archive.html +11 -0
  53. data/scaffolds/blog/categories.html +21 -0
  54. data/scaffolds/blog/config.ru +9 -0
  55. data/scaffolds/blog/index.html +13 -0
  56. data/scaffolds/blog/pages.html +14 -0
  57. data/scaffolds/blog/tags.html +21 -0
  58. data/scaffolds/layout.html +3 -0
  59. data/scaffolds/page.html +6 -0
  60. data/scaffolds/post.html +8 -0
  61. data/scaffolds/theme/css/style.css +0 -0
  62. data/scaffolds/theme/images/.gitkeep +0 -0
  63. data/scaffolds/theme/layouts/default.html +17 -0
  64. data/scaffolds/theme/layouts/page.html +7 -0
  65. data/scaffolds/theme/layouts/post.html +8 -0
  66. data/scaffolds/theme/partials/.gitkeep +0 -0
  67. data/spec/db_spec.rb +88 -0
  68. data/spec/page_spec.rb +200 -0
  69. data/spec/parsers/layouts_spec.rb +32 -0
  70. data/spec/parsers/pages_spec.rb +97 -0
  71. data/spec/parsers/posts_spec.rb +301 -0
  72. data/spec/parsers/routes_spec.rb +45 -0
  73. data/spec/parsers/site_spec.rb +32 -0
  74. data/spec/setup_spec.rb +72 -0
  75. data/spec/spec_helper.rb +23 -0
  76. data/system_partials/analytics/getclicky +12 -0
  77. data/system_partials/analytics/google +11 -0
  78. data/system_partials/comments/disqus +13 -0
  79. data/system_partials/comments/facebook +9 -0
  80. data/system_partials/comments/intensedebate +6 -0
  81. data/system_partials/comments/livefyre +6 -0
  82. data/system_partials/syntax/google_prettify +11 -0
  83. metadata +84 -23
  84. data/lib/ruhoh/client.rb +0 -28
  85. data/lib/ruhoh/preview.rb +0 -36
  86. data/lib/ruhoh/templaters/helper_mustache.rb +0 -109
  87. data/lib/ruhoh/templaters/templater.rb +0 -39
@@ -1,7 +1,5 @@
1
1
  class Ruhoh
2
-
3
2
  module Parsers
4
-
5
3
  module Posts
6
4
 
7
5
  MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
@@ -10,95 +8,142 @@ class Ruhoh
10
8
  #
11
9
  def self.generate
12
10
  raise "Ruhoh.config cannot be nil.\n To set config call: Ruhoh.setup" unless Ruhoh.config
13
- puts "=> Generating Posts..."
11
+
12
+ dictionary = self.process
13
+ ordered_posts = self.ordered_posts(dictionary)
14
14
 
15
- dictionary, invalid_posts = process_posts
16
- ordered_posts = []
17
- dictionary.each_value { |val| ordered_posts << val }
18
-
19
- ordered_posts.sort! {
20
- |a,b| Date.parse(b['date']) <=> Date.parse(a['date'])
15
+ {
16
+ 'dictionary' => dictionary,
17
+ 'chronological' => self.build_chronology(ordered_posts),
18
+ 'collated' => self.collate(ordered_posts),
19
+ 'tags' => self.parse_tags(ordered_posts),
20
+ 'categories' => self.parse_categories(ordered_posts)
21
21
  }
22
+ end
22
23
 
23
- data = {
24
- 'dictionary' => dictionary,
25
- 'chronological' => build_chronology(ordered_posts),
26
- 'collated' => collate(ordered_posts),
27
- 'tags' => parse_tags(ordered_posts),
28
- 'categories' => parse_categories(ordered_posts)
29
- }
24
+ def self.process
25
+ dictionary = {}
26
+ invalid = []
27
+
28
+ self.files.each do |filename|
29
+ parsed_page = Ruhoh::Utils.parse_file(filename)
30
+ if parsed_page.empty?
31
+ error = "Invalid YAML Front Matter. Ensure this page has valid YAML, even if it's empty."
32
+ invalid << [filename, error] ; next
33
+ end
34
+ data = parsed_page['data']
35
+
36
+ filename_data = self.parse_filename(filename)
37
+ if filename_data.empty?
38
+ error = "Invalid Filename Format. Format should be: YYYY-MM-DD-my-post-title.ext"
39
+ invalid << [filename, error] ; next
40
+ end
41
+
42
+ data['date'] ||= filename_data['date']
30
43
 
31
- if invalid_posts.empty?
32
- puts "=> #{dictionary.count}/#{dictionary.count + invalid_posts.count} posts processed."
33
- else
34
- puts "=> Invalid posts not processed:"
35
- puts invalid_posts.to_yaml
44
+ unless self.formatted_date(data['date'])
45
+ error = "Invalid Date Format. Date should be: YYYY-MM-DD"
46
+ invalid << [filename, error] ; next
47
+ end
48
+
49
+ data['date'] = data['date'].to_s
50
+ data['id'] = filename
51
+ data['title'] = data['title'] || filename_data['title']
52
+ data['url'] = self.permalink(data)
53
+ dictionary[filename] = data
36
54
  end
37
-
38
- data
55
+
56
+ self.report(dictionary, invalid)
57
+ dictionary
39
58
  end
59
+
60
+
61
+ def self.process_file(filename)
62
+ p = Ruhoh::Utils.parse_file(filename)
63
+ filename_data = self.parse_filename(filename)
64
+
65
+ if p['data']['title'].nil? || p['data']['title'].gsub(/\s/, '').empty?
66
+ p['data']['title'] = filename_data['title'] || nil
67
+ end
40
68
 
41
- def self.process_posts
42
- dictionary = {}
43
- invalid_posts = []
44
-
69
+ p['data']['date'] ||= filename_data['date']
70
+ p['data']['date'] = self.formatted_date(p['data']['date'] || Time.now)
71
+
72
+ p
73
+ end
74
+
75
+ def self.formatted_date(date)
76
+ Time.parse(date.to_s).strftime('%Y-%m-%d')
77
+ rescue
78
+ false
79
+ end
80
+
81
+ def self.report(dictionary, invalid)
82
+ output = "#{dictionary.count}/#{dictionary.count + invalid.count} posts processed."
83
+ if dictionary.empty? && invalid.empty?
84
+ Ruhoh::Friend.say { plain "0 posts to process." }
85
+ elsif invalid.empty?
86
+ Ruhoh::Friend.say { green output }
87
+ else
88
+ Ruhoh::Friend.say {
89
+ yellow output
90
+ list "Posts not processed:", invalid
91
+ }
92
+ end
93
+ end
94
+
95
+ def self.files
45
96
  FileUtils.cd(Ruhoh.paths.site_source) {
46
- Dir.glob("#{Ruhoh.folders.posts}/**/*.*") { |filename|
97
+ return Dir["#{Ruhoh.folders.posts}/**/*.*"].select { |filename|
47
98
  next if FileTest.directory?(filename)
48
99
  next if ['.'].include? filename[0]
49
-
50
- File.open(filename) do |page|
51
- front_matter = page.read.match(Ruhoh::Utils::FMregex)
52
- if !front_matter
53
- invalid_posts << filename ; next
54
- end
55
-
56
- post = YAML.load(front_matter[0].gsub(/---\n/, "")) || {}
57
-
58
- m, path, file_date, file_slug, ext = *filename.match(MATCHER)
59
- post['date'] = post['date'] || file_date
60
-
61
- ## Test for valid date
62
- begin
63
- Time.parse(post['date'])
64
- rescue
65
- puts "Invalid date format on: #{filename}"
66
- puts "Date should be: YYYY/MM/DD"
67
- invalid_posts << filename
68
- next
69
- end
70
-
71
- post['id'] = filename
72
- post['title'] = post['title'] || self.titleize(file_slug)
73
- post['url'] = self.permalink(post)
74
- dictionary[filename] = post
75
- end
100
+ true
76
101
  }
77
102
  }
103
+ end
104
+
105
+ def self.ordered_posts(dictionary)
106
+ ordered_posts = []
107
+ dictionary.each_value { |val| ordered_posts << val }
108
+ ordered_posts.sort! {
109
+ |a,b| Date.parse(b['date']) <=> Date.parse(a['date'])
110
+ }
78
111
 
79
- [dictionary, invalid_posts]
112
+ ordered_posts
80
113
  end
81
-
114
+
115
+ def self.parse_filename(filename)
116
+ data = *filename.match(MATCHER)
117
+ return {} if data.empty?
118
+ {
119
+ "path" => data[1],
120
+ "date" => data[2],
121
+ "slug" => data[3],
122
+ "title" => self.titleize(data[3]),
123
+ "extension" => data[4]
124
+ }
125
+ end
126
+
82
127
  # my-post-title ===> My Post Title
83
128
  def self.titleize(file_slug)
84
129
  file_slug.gsub(/[\W\_]/, ' ').gsub(/\b\w/){$&.upcase}
85
130
  end
86
131
 
132
+ # My Post Title ===> my-post-title
133
+ def self.to_slug(title)
134
+ title.downcase.strip.gsub(/\s/, '-').gsub(/[^\w-]/, '')
135
+ end
136
+
137
+ def self.to_filename(data)
138
+ File.join(Ruhoh.paths.posts, "#{self.formatted_date(data['date'])}-#{self.to_slug(data['title'])}.#{data['ext']}")
139
+ end
140
+
87
141
  # Another blatently stolen method from Jekyll
88
142
  def self.permalink(post)
89
143
  date = Date.parse(post['date'])
90
144
  title = post['title'].downcase.gsub(' ', '-').gsub('.','')
91
- format = case (post['permalink'] || Ruhoh.config.permalink)
92
- when :pretty
93
- "/:categories/:year/:month/:day/:title/"
94
- when :none
95
- "/:categories/:title.html"
96
- when :date
97
- "/:categories/:year/:month/:day/:title.html"
98
- else
99
- post['permalink'] || Ruhoh.config.permalink
100
- end
101
-
145
+ format = post['permalink'] || Ruhoh.config.permalink || "/:categories/:year/:month/:day/:title.html"
146
+
102
147
  url = {
103
148
  "year" => date.strftime("%Y"),
104
149
  "month" => date.strftime("%m"),
@@ -106,7 +151,7 @@ class Ruhoh
106
151
  "title" => CGI::escape(title),
107
152
  "i_day" => date.strftime("%d").to_i.to_s,
108
153
  "i_month" => date.strftime("%m").to_i.to_s,
109
- "categories" => Array(post['categories'] || post['category']).join('/'),
154
+ "categories" => Array(post['categories'])[0] || '',
110
155
  "output_ext" => 'html' # what's this for?
111
156
  }.inject(format) { |result, token|
112
157
  result.gsub(/:#{Regexp.escape token.first}/, token.last)
@@ -118,8 +163,8 @@ class Ruhoh
118
163
  url
119
164
  end
120
165
 
121
- def self.build_chronology(posts)
122
- posts.map { |post| post['id'] }
166
+ def self.build_chronology(ordered_posts)
167
+ ordered_posts.map { |post| post['id'] }
123
168
  end
124
169
 
125
170
  # Internal: Create a collated posts data structure.
@@ -131,16 +176,16 @@ class Ruhoh
131
176
  # 'months' : [{ 'month' : month,
132
177
  # 'posts': [{}, {}, ..] }, ..] }, ..]
133
178
  #
134
- def self.collate(posts)
179
+ def self.collate(ordered_posts)
135
180
  collated = []
136
- posts.each_with_index do |post, i|
181
+ ordered_posts.each_with_index do |post, i|
137
182
  thisYear = Time.parse(post['date']).strftime('%Y')
138
183
  thisMonth = Time.parse(post['date']).strftime('%B')
139
- if posts[i-1]
140
- prevYear = Time.parse(posts[i-1]['date']).strftime('%Y')
141
- prevMonth = Time.parse(posts[i-1]['date']).strftime('%B')
184
+ if (i-1 >= 0)
185
+ prevYear = Time.parse(ordered_posts[i-1]['date']).strftime('%Y')
186
+ prevMonth = Time.parse(ordered_posts[i-1]['date']).strftime('%B')
142
187
  end
143
-
188
+
144
189
  if(prevYear == thisYear)
145
190
  if(prevMonth == thisMonth)
146
191
  collated.last['months'].last['posts'] << post['id'] # append to last year & month
@@ -165,15 +210,20 @@ class Ruhoh
165
210
  collated
166
211
  end
167
212
 
168
- def self.parse_tags(posts)
213
+ def self.parse_tags(ordered_posts)
169
214
  tags = {}
170
215
 
171
- posts.each do |post|
216
+ ordered_posts.each do |post|
172
217
  Array(post['tags']).each do |tag|
173
218
  if tags[tag]
174
219
  tags[tag]['count'] += 1
175
220
  else
176
- tags[tag] = { 'count' => 1, 'name' => tag, 'posts' => [] }
221
+ tags[tag] = {
222
+ 'count' => 1,
223
+ 'name' => tag,
224
+ 'url' => "/tags.html##{tag}-ref",
225
+ 'posts' => []
226
+ }
177
227
  end
178
228
 
179
229
  tags[tag]['posts'] << post['id']
@@ -182,18 +232,21 @@ class Ruhoh
182
232
  tags
183
233
  end
184
234
 
185
- def self.parse_categories(posts)
235
+ def self.parse_categories(ordered_posts)
186
236
  categories = {}
187
237
 
188
- posts.each do |post|
189
- cats = post['categories'] ? post['categories'] : Array(post['category']).join('/')
190
-
191
- Array(cats).each do |cat|
238
+ ordered_posts.each do |post|
239
+ Array(post['categories']).each do |cat|
192
240
  cat = Array(cat).join('/')
193
241
  if categories[cat]
194
242
  categories[cat]['count'] += 1
195
243
  else
196
- categories[cat] = { 'count' => 1, 'name' => cat, 'posts' => [] }
244
+ categories[cat] = {
245
+ 'count' => 1,
246
+ 'name' => cat,
247
+ 'url' => "/categories.html##{cat}-ref",
248
+ 'posts' => []
249
+ }
197
250
  end
198
251
 
199
252
  categories[cat]['posts'] << post['id']
@@ -203,7 +256,5 @@ class Ruhoh
203
256
  end
204
257
 
205
258
  end # Post
206
-
207
259
  end #Parsers
208
-
209
260
  end #Ruhoh
@@ -1,24 +1,20 @@
1
1
  class Ruhoh
2
-
3
2
  module Parsers
4
-
5
3
  module Routes
6
4
 
7
- #[{"url" => "id"}, ... ]
8
5
  def self.generate
9
6
  routes = {}
10
- Ruhoh::Parsers::Pages.generate.each_value { |page|
7
+ Ruhoh::DB.pages.each_value { |page|
11
8
  routes[page['url']] = page['id']
12
9
  }
13
- Ruhoh::Parsers::Posts.generate['dictionary'].each_value { |page|
10
+
11
+ Ruhoh::DB.posts['dictionary'].each_value { |page|
14
12
  routes[page['url']] = page['id']
15
13
  }
16
-
14
+
17
15
  routes
18
16
  end
19
17
 
20
18
  end #Routes
21
-
22
19
  end #Parsers
23
-
24
20
  end #Ruhoh
@@ -6,14 +6,8 @@ class Ruhoh
6
6
  module Site
7
7
 
8
8
  def self.generate
9
- site = File.join(Ruhoh.paths.site_source, Ruhoh.files.site)
10
- site = File.exist?(site) ? File.open(site).read : ''
11
- site = YAML.load(site) || {}
12
-
13
- config = File.join(Ruhoh.paths.site_source, Ruhoh.files.config)
14
- config = File.exist?(config) ? File.open(config).read : ''
15
- config = YAML.load(config) || {}
16
-
9
+ site = Ruhoh::Utils.parse_file_as_yaml(Ruhoh.paths.site_source, Ruhoh.files.site) || {}
10
+ config = Ruhoh::Utils.parse_file_as_yaml(Ruhoh.paths.site_source, Ruhoh.files.config)
17
11
  site['config'] = config
18
12
  site
19
13
  end
@@ -0,0 +1,48 @@
1
+ class Ruhoh
2
+
3
+ # Public: Rack application used to render singular pages via their URL.
4
+ #
5
+ # Examples
6
+ #
7
+ # In config.ru:
8
+ #
9
+ # require 'ruhoh'
10
+ #
11
+ # Ruhoh.setup
12
+ # use Rack::Static, {:urls => ["/#{Ruhoh.folders.media}", "/#{Ruhoh.folders.templates}"]}
13
+ # run Ruhoh::Previewer.new
14
+ #
15
+ class Previewer
16
+
17
+ def initialize
18
+ Ruhoh::DB.update!
19
+ @page = Ruhoh::Page.new
20
+ Ruhoh::Watch.start
21
+ end
22
+
23
+ def call(env)
24
+ return favicon if env['PATH_INFO'] == '/favicon.ico'
25
+ return drafts if ["/#{Ruhoh.folders.drafts}", "/#{Ruhoh.folders.drafts}/"].include?(env['PATH_INFO'])
26
+
27
+ @page.change_with_url(env['PATH_INFO'])
28
+ [200, {'Content-Type' => 'text/html'}, [@page.render]]
29
+ end
30
+
31
+ def favicon
32
+ [200, {'Content-Type' => 'image/x-icon'}, ['']]
33
+ end
34
+
35
+ def drafts
36
+ html = '<h3>Drafts</h3>'
37
+ html += '<ul>'
38
+ Ruhoh::DB.drafts.each_value do |draft|
39
+ html += "<li><a href='#{draft['url']}'>#{draft['id']}: #{draft['title']}</a></li>"
40
+ end
41
+ html += '</ul>'
42
+
43
+ [200, {'Content-Type' => 'text/html'}, [html]]
44
+ end
45
+
46
+ end #Previewer
47
+
48
+ end #Ruhoh
@@ -0,0 +1,53 @@
1
+ class Ruhoh
2
+
3
+ module Templaters
4
+
5
+ module Base
6
+
7
+ def self.build_payload(page)
8
+ {
9
+ "page" => page.attributes,
10
+ "site" => Ruhoh::DB.site,
11
+ "pages" => Ruhoh::DB.pages,
12
+ "_posts" => Ruhoh::DB.posts,
13
+ "THEME_PATH" => Ruhoh.config.theme_path,
14
+ "SYNTAX_PATH" => Ruhoh.config.syntax_path,
15
+ "MEDIA_PATH" => Ruhoh.config.media_path
16
+ }
17
+ end
18
+
19
+ # Render a given page object.
20
+ # This is different from parse only in that rendering a page
21
+ # assumes we use page.content and its layouts as the incoming view.
22
+ def self.render(page)
23
+ self.parse(self.expand(page), page)
24
+ end
25
+
26
+ # Parse arbitrary content relative to a given page.
27
+ def self.parse(output, page)
28
+ Ruhoh::Templaters::RMustache.render(output, self.build_payload(page))
29
+ end
30
+
31
+ # Expand the page.
32
+ # Places page content into sub-template then into master template if available.
33
+ def self.expand(page)
34
+ if page.sub_layout
35
+ output = page.sub_layout['content'].gsub(Ruhoh::Utils::ContentRegex, page.content)
36
+ else
37
+ output = page.content
38
+ end
39
+
40
+ # An undefined master means the page/post layouts is only one deep.
41
+ # This means it expects to load directly into a master template.
42
+ if page.master_layout && page.master_layout['content']
43
+ output = page.master_layout['content'].gsub(Ruhoh::Utils::ContentRegex, output);
44
+ end
45
+
46
+ output
47
+ end
48
+
49
+ end #Base
50
+
51
+ end #Templaters
52
+
53
+ end #Ruhoh
@@ -0,0 +1,159 @@
1
+ require 'pp'
2
+
3
+ class Ruhoh
4
+
5
+ module Templaters
6
+
7
+ module Helpers
8
+
9
+ def partial(name)
10
+ Ruhoh::DB.partials[name.to_s]
11
+ end
12
+
13
+ def raw_code(sub_context)
14
+ code = sub_context.gsub('{', '&#123;').gsub('}', '&#125;').gsub('<', '&lt;').gsub('>', '&gt;')
15
+ "<pre><code>#{code}</code></pre>"
16
+ end
17
+
18
+ def debug(sub_context)
19
+ Ruhoh::Friend.say {
20
+ yellow "?debug:"
21
+ magenta sub_context.class
22
+ cyan sub_context.inspect
23
+ }
24
+
25
+ "<pre>#{sub_context.class}\n#{sub_context.pretty_inspect}</pre>"
26
+ end
27
+
28
+ def to_tags(sub_context)
29
+ if sub_context.is_a?(Array)
30
+ sub_context.map { |id|
31
+ self.context['_posts']['tags'][id] if self.context['_posts']['tags'][id]
32
+ }
33
+ else
34
+ tags = []
35
+ self.context['_posts']['tags'].each_value { |tag|
36
+ tags << tag
37
+ }
38
+ tags
39
+ end
40
+ end
41
+
42
+ def to_posts(sub_context)
43
+ sub_context = sub_context.is_a?(Array) ? sub_context : self.context['_posts']['chronological']
44
+
45
+ sub_context.map { |id|
46
+ self.context['_posts']['dictionary'][id] if self.context['_posts']['dictionary'][id]
47
+ }
48
+ end
49
+
50
+ def to_pages(sub_context)
51
+ pages = []
52
+ if sub_context.is_a?(Array)
53
+ sub_context.each do |id|
54
+ if self.context[:pages][id]
55
+ pages << self.context[:pages][id]
56
+ end
57
+ end
58
+ else
59
+ self.context[:pages].each_value {|page| pages << page }
60
+ end
61
+
62
+ pages.each_with_index do |page, i|
63
+ next unless page['id'] == self.context[:page]['id']
64
+ active_page = page.dup
65
+ active_page['is_active_page'] = true
66
+ pages[i] = active_page
67
+ end
68
+
69
+ pages
70
+ end
71
+
72
+ def to_categories(sub_context)
73
+ if sub_context.is_a?(Array)
74
+ sub_context.map { |id|
75
+ self.context['_posts']['categories'][id] if self.context['_posts']['categories'][id]
76
+ }
77
+ else
78
+ cats = []
79
+ self.context['_posts']['categories'].each_value { |cat|
80
+ cats << cat
81
+ }
82
+ cats
83
+ end
84
+ end
85
+
86
+ def next(sub_context)
87
+ return unless sub_context.is_a?(String) || sub_context.is_a?(Hash)
88
+ id = sub_context.is_a?(Hash) ? sub_context['id'] : sub_context
89
+ return unless id
90
+ index = self.context['_posts']['chronological'].index(id)
91
+ return unless index && (index-1 >= 0)
92
+ next_id = self.context['_posts']['chronological'][index-1]
93
+ return unless next_id
94
+ self.to_posts([next_id])
95
+ end
96
+
97
+ def previous(sub_context)
98
+ return unless sub_context.is_a?(String) || sub_context.is_a?(Hash)
99
+ id = sub_context.is_a?(Hash) ? sub_context['id'] : sub_context
100
+ return unless id
101
+ index = self.context['_posts']['chronological'].index(id)
102
+ return unless index && (index+1 >= 0)
103
+ prev_id = self.context['_posts']['chronological'][index+1]
104
+ return unless prev_id
105
+ self.to_posts([prev_id])
106
+ end
107
+
108
+ def analytics
109
+ return '' if self.context['page']['analytics'].to_s == 'false'
110
+ analytics_config = self.context['site']['config']['analytics']
111
+ return '' unless analytics_config && analytics_config['provider']
112
+
113
+ if analytics_config['provider'] == "custom"
114
+ code = self.partial("custom_analytics")
115
+ else
116
+ code = self.partial("analytics/#{analytics_config['provider']}")
117
+ end
118
+
119
+ return "<h2 style='color:red'>!Analytics Provider partial for '#{analytics_config['provider']}' not found </h2>" if code.nil?
120
+
121
+ self.render(code)
122
+ end
123
+
124
+ def comments
125
+ return '' if self.context['page']['comments'].to_s == 'false'
126
+ comments_config = self.context['site']['config']['comments']
127
+ return '' unless comments_config && comments_config['provider']
128
+
129
+ if comments_config['provider'] == "custom"
130
+ code = self.partial("custom_comments")
131
+ else
132
+ code = self.partial("comments/#{comments_config['provider']}")
133
+ end
134
+
135
+ return "<h2 style='color:red'>!Comments Provider partial for '#{comments_config['provider']}' not found </h2>" if code.nil?
136
+
137
+ self.render(code)
138
+ end
139
+
140
+ def syntax
141
+ syntax_config = self.context['site']['config']['syntax']
142
+ return '' unless syntax_config && syntax_config['provider']
143
+
144
+ if syntax_config['provider'] == "custom"
145
+ code = self.partial("custom_syntax")
146
+ else
147
+ code = self.partial("syntax/#{syntax_config['provider']}")
148
+ end
149
+
150
+ return "<h2 style='color:red'>!Syntax Provider partial for '#{syntax_config['provider']}' not found </h2>" if code.nil?
151
+
152
+ self.render(code)
153
+ end
154
+
155
+ end #Helpers
156
+
157
+ end #Templaters
158
+
159
+ end #Ruhoh
@@ -0,0 +1,29 @@
1
+ class Ruhoh
2
+
3
+ module Templaters
4
+
5
+ class RMustache < Mustache
6
+ include Ruhoh::Templaters::Helpers
7
+
8
+ class RContext < Context
9
+
10
+ # Overload find method to catch helper expressions
11
+ def find(obj, key, default = nil)
12
+ return super unless key.to_s.index('?')
13
+ context, helper = key.to_s.split('?')
14
+ context = context.empty? ? obj : super(obj, context)
15
+
16
+ self.mustache_in_stack.__send__ helper, context
17
+ end
18
+
19
+ end #RContext
20
+
21
+ def context
22
+ @context ||= RContext.new(self)
23
+ end
24
+
25
+ end #RMustache
26
+
27
+ end #Templaters
28
+
29
+ end #Ruhoh