ruhoh 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +57 -0
- data/bin/ruhoh +22 -0
- data/lib/ruhoh/client.rb +28 -0
- data/lib/ruhoh/compiler.rb +58 -0
- data/lib/ruhoh/converters/converter.rb +17 -0
- data/lib/ruhoh/db.rb +57 -0
- data/lib/ruhoh/page.rb +63 -0
- data/lib/ruhoh/parsers/layouts.rb +25 -0
- data/lib/ruhoh/parsers/pages.rb +71 -0
- data/lib/ruhoh/parsers/partials.rb +29 -0
- data/lib/ruhoh/parsers/posts.rb +209 -0
- data/lib/ruhoh/parsers/routes.rb +24 -0
- data/lib/ruhoh/parsers/site.rb +25 -0
- data/lib/ruhoh/preview.rb +36 -0
- data/lib/ruhoh/templaters/helper_mustache.rb +109 -0
- data/lib/ruhoh/templaters/templater.rb +39 -0
- data/lib/ruhoh/utils.rb +28 -0
- data/lib/ruhoh/version.rb +3 -0
- data/lib/ruhoh/watch.rb +57 -0
- data/lib/ruhoh.rb +84 -0
- data/ruhoh.gemspec +47 -0
- metadata +111 -0
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
=)
|
2
|
+
|
3
|
+
|
4
|
+
## Setup/Config
|
5
|
+
|
6
|
+
Ruhoh.setup is responsible for building the configuration data.
|
7
|
+
The config takes a given source directory, parses its \_config.yml
|
8
|
+
and builds absolute paths to all the Filesystem API endpoints relative to the source directory.
|
9
|
+
|
10
|
+
## Parsers
|
11
|
+
|
12
|
+
Parsers are small, atomic, functional methods used to process data.
|
13
|
+
Parsers follow the Ruhoh interface specification, to locate, extract, and process data relative to the source directory.
|
14
|
+
|
15
|
+
|
16
|
+
## DB
|
17
|
+
|
18
|
+
The database is responsible for holding website's main data.
|
19
|
+
The database depends on Ruhoh.config.
|
20
|
+
It depends on the parsers to parse the source directory in order to return the data.
|
21
|
+
|
22
|
+
|
23
|
+
## Page
|
24
|
+
|
25
|
+
The page class models a page in the ruhoh system.
|
26
|
+
A page is technically a post object or a page object.
|
27
|
+
|
28
|
+
- content
|
29
|
+
- data (includes yaml front matter + url, id etc.)
|
30
|
+
- sub\_layout
|
31
|
+
- master\_layout
|
32
|
+
|
33
|
+
## Converter
|
34
|
+
|
35
|
+
The converter is responsible for converting a pages 'content' into HTML.
|
36
|
+
A page can be written in various markup formats specified by the page's extension.
|
37
|
+
|
38
|
+
## Templater
|
39
|
+
|
40
|
+
The templater is responsible for building the finished page by
|
41
|
+
utilizing the layouts, content, and any embedded templating language expressions.
|
42
|
+
|
43
|
+
|
44
|
+
# Compiler
|
45
|
+
|
46
|
+
Compiles the full website
|
47
|
+
|
48
|
+
# Preview
|
49
|
+
|
50
|
+
Serves a singular page view to a web-server identified by its url.
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
data/bin/ruhoh
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require 'ruhoh'
|
6
|
+
require 'ruhoh/client'
|
7
|
+
|
8
|
+
help = <<HELP
|
9
|
+
Ruhoh is the best static blog generator known to mankind.
|
10
|
+
|
11
|
+
Basic Command Line Usage:
|
12
|
+
Test
|
13
|
+
HELP
|
14
|
+
|
15
|
+
case ARGV[0]
|
16
|
+
when 'new'
|
17
|
+
Ruhoh::Client.new_blog(ARGV[1])
|
18
|
+
when 'help'
|
19
|
+
puts help
|
20
|
+
else
|
21
|
+
puts help
|
22
|
+
end
|
data/lib/ruhoh/client.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Client
|
4
|
+
Root = File.expand_path(File.join(File.dirname(__FILE__), '../..'))
|
5
|
+
|
6
|
+
def self.new_blog(name)
|
7
|
+
if name.nil?
|
8
|
+
puts "Name must be specified"
|
9
|
+
exit 0
|
10
|
+
end
|
11
|
+
|
12
|
+
source_directory = File.join(Root, 'scaffolds/blog')
|
13
|
+
target_directory = File.join(Dir.pwd, name)
|
14
|
+
|
15
|
+
if File.exist?(target_directory)
|
16
|
+
puts "#{target_directory} already exists. Specify another directory."
|
17
|
+
exit 0
|
18
|
+
end
|
19
|
+
|
20
|
+
FileUtils.mkdir target_directory
|
21
|
+
FileUtils.cp_r "#{source_directory}/.", target_directory
|
22
|
+
|
23
|
+
puts "=> Blog successfully cloned to:"
|
24
|
+
puts "=> #{target_directory}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end #Ruhoh
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
class Compiler
|
4
|
+
|
5
|
+
def initialize(target_directory = './_compiled')
|
6
|
+
Ruhoh::DB.initialize
|
7
|
+
@target = target_directory
|
8
|
+
@page = Ruhoh::Page.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def compile
|
12
|
+
FileUtils.mkdir @target unless File.exist?(@target)
|
13
|
+
self.theme
|
14
|
+
self.pages
|
15
|
+
self.media
|
16
|
+
end
|
17
|
+
|
18
|
+
def pages
|
19
|
+
processed = []
|
20
|
+
|
21
|
+
FileUtils.cd(@target) {
|
22
|
+
Ruhoh::DB.posts['dictionary'].merge(Ruhoh::DB.pages).each_value do |p|
|
23
|
+
@page.change(p['id'])
|
24
|
+
|
25
|
+
FileUtils.mkdir_p File.dirname(@page.compiled_path)
|
26
|
+
File.open(@page.compiled_path, 'w') { |p| p.puts @page.render }
|
27
|
+
|
28
|
+
processed << path
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
puts "=> Posts Processed:"
|
33
|
+
puts processed
|
34
|
+
end
|
35
|
+
|
36
|
+
def theme
|
37
|
+
FileUtils.mkdir_p File.join(@target, Ruhoh.config.asset_path)
|
38
|
+
FileUtils.cp_r Ruhoh.paths.theme, File.join(@target, Ruhoh.folders.templates, Ruhoh.folders.themes)
|
39
|
+
end
|
40
|
+
|
41
|
+
def media
|
42
|
+
FileUtils.mkdir_p File.join(@target, Ruhoh.folders.media)
|
43
|
+
FileUtils.cp_r Ruhoh.paths.media, File.join(@target)
|
44
|
+
end
|
45
|
+
|
46
|
+
def write_data
|
47
|
+
File.open(Ruhoh.paths.database + '/posts_dictionary.yml', 'w') { |page|
|
48
|
+
page.puts Ruhoh::DB.posts.to_yaml
|
49
|
+
}
|
50
|
+
|
51
|
+
File.open(Ruhoh.paths.database + '/pages_dictionary.yml', 'w') { |page|
|
52
|
+
page.puts Ruhoh::DB.pages.to_yaml
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
end #Compiler
|
57
|
+
|
58
|
+
end #Ruhoh
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'maruku'
|
2
|
+
|
3
|
+
class Ruhoh
|
4
|
+
|
5
|
+
module Converter
|
6
|
+
|
7
|
+
def self.convert(page)
|
8
|
+
if ['.md', '.markdown'].include? File.extname(page.data['id']).downcase
|
9
|
+
Maruku.new(page.content).to_html
|
10
|
+
else
|
11
|
+
page.content
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end #Converter
|
16
|
+
|
17
|
+
end #Ruhoh
|
data/lib/ruhoh/db.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "observer"
|
2
|
+
|
3
|
+
class Ruhoh
|
4
|
+
|
5
|
+
# Public: Database class for interacting with "data" in Ruhoh.
|
6
|
+
#
|
7
|
+
class DB
|
8
|
+
|
9
|
+
class << self
|
10
|
+
include Observable
|
11
|
+
attr_reader :site, :routes, :posts, :pages, :layouts, :partials
|
12
|
+
|
13
|
+
# Note this is class-level so you have to call it manually.
|
14
|
+
def initialize
|
15
|
+
@site = ''
|
16
|
+
@routes = ''
|
17
|
+
@posts = ''
|
18
|
+
@pages = ''
|
19
|
+
@layouts = ''
|
20
|
+
@partials = ''
|
21
|
+
self.update!
|
22
|
+
end
|
23
|
+
|
24
|
+
def update(name)
|
25
|
+
self.instance_variable_set("@#{name}",
|
26
|
+
case name
|
27
|
+
when :site
|
28
|
+
Ruhoh::Parsers::Site.generate
|
29
|
+
when :routes
|
30
|
+
Ruhoh::Parsers::Routes.generate
|
31
|
+
when :posts
|
32
|
+
Ruhoh::Parsers::Posts.generate
|
33
|
+
when :pages
|
34
|
+
Ruhoh::Parsers::Pages.generate
|
35
|
+
when :layouts
|
36
|
+
Ruhoh::Parsers::Layouts.generate
|
37
|
+
when :partials
|
38
|
+
Ruhoh::Parsers::Partials.generate
|
39
|
+
else
|
40
|
+
raise "Data type: '#{name}' is not a valid data type."
|
41
|
+
end
|
42
|
+
)
|
43
|
+
changed
|
44
|
+
notify_observers(name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def update!
|
48
|
+
self.instance_variables.each { |var|
|
49
|
+
self.__send__ :update, var.to_s.gsub('@', '').to_sym
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
end #self
|
54
|
+
|
55
|
+
end #DB
|
56
|
+
|
57
|
+
end #Ruhoh
|
data/lib/ruhoh/page.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
class Page
|
4
|
+
attr_reader :data, :content, :sub_layout, :master_layout
|
5
|
+
|
6
|
+
# Public: Change this page using an id.
|
7
|
+
def change(id)
|
8
|
+
@data = nil
|
9
|
+
@data = id =~ Regexp.new("^#{Ruhoh.folders.posts}") ? Ruhoh::DB.posts['dictionary'][id] : Ruhoh::DB.pages[id]
|
10
|
+
raise "Page #{id} not found in database" unless @data
|
11
|
+
@id = id
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: Change this page using a URL.
|
15
|
+
def change_with_url(url)
|
16
|
+
url = '/index.html' if url == '/'
|
17
|
+
id = Ruhoh::DB.routes[url]
|
18
|
+
raise "Page id not found for url: #{url}" unless id
|
19
|
+
self.change(id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def render
|
23
|
+
self.process_layouts
|
24
|
+
self.process_content
|
25
|
+
Ruhoh::Templater.expand_and_render(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def process_layouts
|
29
|
+
@sub_layout = Ruhoh::DB.layouts[@data['layout']]
|
30
|
+
|
31
|
+
if @sub_layout['data']['layout']
|
32
|
+
@master_layout = Ruhoh::DB.layouts[@sub_layout['data']['layout']]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# We need to pre-process the content data
|
37
|
+
# in order to invoke converters on the result.
|
38
|
+
# Converters (markdown) always choke on the templating language.
|
39
|
+
def process_content
|
40
|
+
@content = Ruhoh::Utils.parse_file(Ruhoh.paths.site_source, @id)['content']
|
41
|
+
@content = Ruhoh::Templater.render(@content, self)
|
42
|
+
@content = Ruhoh::Converter.convert(self)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: Return page attributes suitable for inclusion in the
|
46
|
+
# 'payload' of the given templater.
|
47
|
+
def attributes
|
48
|
+
@data['content'] = @content
|
49
|
+
@data
|
50
|
+
end
|
51
|
+
|
52
|
+
# Public: Formats the path to the compiled file based on the URL.
|
53
|
+
#
|
54
|
+
# Returns: [String] The relative path to the compiled file for this page.
|
55
|
+
def compiled_path
|
56
|
+
path = CGI.unescape(@data['url']).gsub(/^\//, '') #strip leading slash.
|
57
|
+
path += '/index.html' unless path =~ /\.html$/
|
58
|
+
path
|
59
|
+
end
|
60
|
+
|
61
|
+
end #Page
|
62
|
+
|
63
|
+
end #Ruhoh
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
module Layouts
|
6
|
+
|
7
|
+
# Generate layouts only from the active theme.
|
8
|
+
def self.generate
|
9
|
+
layouts = {}
|
10
|
+
FileUtils.cd(Ruhoh.paths.layouts) {
|
11
|
+
Dir.glob("**/*.*") { |filename|
|
12
|
+
next if FileTest.directory?(filename)
|
13
|
+
next if ['_','.'].include? filename[0]
|
14
|
+
id = File.basename(filename, File.extname(filename))
|
15
|
+
layouts[id] = Ruhoh::Utils.parse_file(Ruhoh.paths.layouts, filename)
|
16
|
+
}
|
17
|
+
}
|
18
|
+
layouts
|
19
|
+
end
|
20
|
+
|
21
|
+
end #Layouts
|
22
|
+
|
23
|
+
end #Parsers
|
24
|
+
|
25
|
+
end #Ruhoh
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
module Pages
|
6
|
+
|
7
|
+
# Public: Generate the Pages dictionary.
|
8
|
+
#
|
9
|
+
def self.generate
|
10
|
+
raise "Ruhoh.config cannot be nil.\n To set config call: Ruhoh.setup" unless Ruhoh.config
|
11
|
+
puts "=> Generating Pages..."
|
12
|
+
|
13
|
+
invalid_pages = []
|
14
|
+
dictionary = {}
|
15
|
+
total_pages = 0
|
16
|
+
FileUtils.cd(Ruhoh.paths.site_source) {
|
17
|
+
Dir.glob("**/*.*") { |filename|
|
18
|
+
next unless self.is_valid_page?(filename)
|
19
|
+
total_pages += 1
|
20
|
+
|
21
|
+
File.open(filename) do |page|
|
22
|
+
front_matter = page.read.match(Ruhoh::Utils::FMregex)
|
23
|
+
if !front_matter
|
24
|
+
invalid_pages << filename ; next
|
25
|
+
end
|
26
|
+
|
27
|
+
data = YAML.load(front_matter[0].gsub(/---\n/, "")) || {}
|
28
|
+
data['id'] = filename
|
29
|
+
data['url'] = self.permalink(data)
|
30
|
+
data['title'] = data['title'] || self.titleize(filename)
|
31
|
+
|
32
|
+
dictionary[filename] = data
|
33
|
+
end
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
if invalid_pages.empty?
|
38
|
+
puts "=> #{total_pages - invalid_pages.count }/#{total_pages} pages processed."
|
39
|
+
else
|
40
|
+
puts "=> Invalid pages not processed:"
|
41
|
+
puts invalid_pages.to_yaml
|
42
|
+
end
|
43
|
+
|
44
|
+
dictionary
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.is_valid_page?(filename)
|
48
|
+
return false if FileTest.directory?(filename)
|
49
|
+
return false if ['_', '.'].include? filename[0]
|
50
|
+
return false if Ruhoh.filters.pages['names'].include? filename
|
51
|
+
Ruhoh.filters.pages['regexes'].each {|regex| return false if filename =~ regex }
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.titleize(filename)
|
56
|
+
File.basename( filename, File.extname(filename) ).gsub(/[\W\_]/, ' ').gsub(/\b\w/){$&.upcase}
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.permalink(page)
|
60
|
+
url = '/' + page['id'].gsub(File.extname(page['id']), '.html')
|
61
|
+
|
62
|
+
# sanitize url
|
63
|
+
url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
|
64
|
+
url
|
65
|
+
end
|
66
|
+
|
67
|
+
end # Pages
|
68
|
+
|
69
|
+
end #Parsers
|
70
|
+
|
71
|
+
end #Ruhoh
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
module Partials
|
6
|
+
|
7
|
+
def self.generate
|
8
|
+
self.process(Ruhoh.paths.global_partials).merge( self.process(Ruhoh.paths.partials) )
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.process(path)
|
12
|
+
return {} unless File.exist?(path)
|
13
|
+
|
14
|
+
partials = {}
|
15
|
+
FileUtils.cd(path) {
|
16
|
+
Dir.glob("**/*").each { |filename|
|
17
|
+
next if FileTest.directory?(filename)
|
18
|
+
next if ['.'].include? filename[0]
|
19
|
+
partials[filename] = File.open(filename).read
|
20
|
+
}
|
21
|
+
}
|
22
|
+
partials
|
23
|
+
end
|
24
|
+
|
25
|
+
end #Partials
|
26
|
+
|
27
|
+
end #Parsers
|
28
|
+
|
29
|
+
end #Ruhoh
|
@@ -0,0 +1,209 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
module Posts
|
6
|
+
|
7
|
+
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
|
8
|
+
|
9
|
+
# Public: Generate the Posts dictionary.
|
10
|
+
#
|
11
|
+
def self.generate
|
12
|
+
raise "Ruhoh.config cannot be nil.\n To set config call: Ruhoh.setup" unless Ruhoh.config
|
13
|
+
puts "=> Generating Posts..."
|
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'])
|
21
|
+
}
|
22
|
+
|
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
|
+
}
|
30
|
+
|
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
|
36
|
+
end
|
37
|
+
|
38
|
+
data
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.process_posts
|
42
|
+
dictionary = {}
|
43
|
+
invalid_posts = []
|
44
|
+
|
45
|
+
FileUtils.cd(Ruhoh.paths.site_source) {
|
46
|
+
Dir.glob("#{Ruhoh.folders.posts}/**/*.*") { |filename|
|
47
|
+
next if FileTest.directory?(filename)
|
48
|
+
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
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
[dictionary, invalid_posts]
|
80
|
+
end
|
81
|
+
|
82
|
+
# my-post-title ===> My Post Title
|
83
|
+
def self.titleize(file_slug)
|
84
|
+
file_slug.gsub(/[\W\_]/, ' ').gsub(/\b\w/){$&.upcase}
|
85
|
+
end
|
86
|
+
|
87
|
+
# Another blatently stolen method from Jekyll
|
88
|
+
def self.permalink(post)
|
89
|
+
date = Date.parse(post['date'])
|
90
|
+
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
|
+
|
102
|
+
url = {
|
103
|
+
"year" => date.strftime("%Y"),
|
104
|
+
"month" => date.strftime("%m"),
|
105
|
+
"day" => date.strftime("%d"),
|
106
|
+
"title" => CGI::escape(title),
|
107
|
+
"i_day" => date.strftime("%d").to_i.to_s,
|
108
|
+
"i_month" => date.strftime("%m").to_i.to_s,
|
109
|
+
"categories" => Array(post['categories'] || post['category']).join('/'),
|
110
|
+
"output_ext" => 'html' # what's this for?
|
111
|
+
}.inject(format) { |result, token|
|
112
|
+
result.gsub(/:#{Regexp.escape token.first}/, token.last)
|
113
|
+
}.gsub(/\/\//, "/")
|
114
|
+
|
115
|
+
# sanitize url
|
116
|
+
url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
|
117
|
+
url += "/" if url =~ /\/$/
|
118
|
+
url
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.build_chronology(posts)
|
122
|
+
posts.map { |post| post['id'] }
|
123
|
+
end
|
124
|
+
|
125
|
+
# Internal: Create a collated posts data structure.
|
126
|
+
#
|
127
|
+
# posts - Required [Array]
|
128
|
+
# Must be sorted chronologically beforehand.
|
129
|
+
#
|
130
|
+
# [{ 'year': year,
|
131
|
+
# 'months' : [{ 'month' : month,
|
132
|
+
# 'posts': [{}, {}, ..] }, ..] }, ..]
|
133
|
+
#
|
134
|
+
def self.collate(posts)
|
135
|
+
collated = []
|
136
|
+
posts.each_with_index do |post, i|
|
137
|
+
thisYear = Time.parse(post['date']).strftime('%Y')
|
138
|
+
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')
|
142
|
+
end
|
143
|
+
|
144
|
+
if(prevYear == thisYear)
|
145
|
+
if(prevMonth == thisMonth)
|
146
|
+
collated.last['months'].last['posts'] << post['id'] # append to last year & month
|
147
|
+
else
|
148
|
+
collated.last['months'] << {
|
149
|
+
'month' => thisMonth,
|
150
|
+
'posts' => [post['id']]
|
151
|
+
} # create new month
|
152
|
+
end
|
153
|
+
else
|
154
|
+
collated << {
|
155
|
+
'year' => thisYear,
|
156
|
+
'months' => [{
|
157
|
+
'month' => thisMonth,
|
158
|
+
'posts' => [post['id']]
|
159
|
+
}]
|
160
|
+
} # create new year & month
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
collated
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.parse_tags(posts)
|
169
|
+
tags = {}
|
170
|
+
|
171
|
+
posts.each do |post|
|
172
|
+
Array(post['tags']).each do |tag|
|
173
|
+
if tags[tag]
|
174
|
+
tags[tag]['count'] += 1
|
175
|
+
else
|
176
|
+
tags[tag] = { 'count' => 1, 'name' => tag, 'posts' => [] }
|
177
|
+
end
|
178
|
+
|
179
|
+
tags[tag]['posts'] << post['id']
|
180
|
+
end
|
181
|
+
end
|
182
|
+
tags
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.parse_categories(posts)
|
186
|
+
categories = {}
|
187
|
+
|
188
|
+
posts.each do |post|
|
189
|
+
cats = post['categories'] ? post['categories'] : Array(post['category']).join('/')
|
190
|
+
|
191
|
+
Array(cats).each do |cat|
|
192
|
+
cat = Array(cat).join('/')
|
193
|
+
if categories[cat]
|
194
|
+
categories[cat]['count'] += 1
|
195
|
+
else
|
196
|
+
categories[cat] = { 'count' => 1, 'name' => cat, 'posts' => [] }
|
197
|
+
end
|
198
|
+
|
199
|
+
categories[cat]['posts'] << post['id']
|
200
|
+
end
|
201
|
+
end
|
202
|
+
categories
|
203
|
+
end
|
204
|
+
|
205
|
+
end # Post
|
206
|
+
|
207
|
+
end #Parsers
|
208
|
+
|
209
|
+
end #Ruhoh
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
module Routes
|
6
|
+
|
7
|
+
#[{"url" => "id"}, ... ]
|
8
|
+
def self.generate
|
9
|
+
routes = {}
|
10
|
+
Ruhoh::Parsers::Pages.generate.each_value { |page|
|
11
|
+
routes[page['url']] = page['id']
|
12
|
+
}
|
13
|
+
Ruhoh::Parsers::Posts.generate['dictionary'].each_value { |page|
|
14
|
+
routes[page['url']] = page['id']
|
15
|
+
}
|
16
|
+
|
17
|
+
routes
|
18
|
+
end
|
19
|
+
|
20
|
+
end #Routes
|
21
|
+
|
22
|
+
end #Parsers
|
23
|
+
|
24
|
+
end #Ruhoh
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Parsers
|
4
|
+
|
5
|
+
# Sitewide data hash + configuration file.
|
6
|
+
module Site
|
7
|
+
|
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
|
+
|
17
|
+
site['config'] = config
|
18
|
+
site
|
19
|
+
end
|
20
|
+
|
21
|
+
end #Site
|
22
|
+
|
23
|
+
end #Parsers
|
24
|
+
|
25
|
+
end #Ruhoh
|
@@ -0,0 +1,36 @@
|
|
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, {:root => '.', :urls => ['/_media', "/#{Ruhoh.folders.templates}"]}
|
13
|
+
# run Ruhoh::Preview.new
|
14
|
+
#
|
15
|
+
class Preview
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
Ruhoh::DB.initialize
|
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
|
+
|
26
|
+
@page.change_with_url(env['PATH_INFO'])
|
27
|
+
[200, {'Content-Type' => 'text/html'}, [@page.render]]
|
28
|
+
end
|
29
|
+
|
30
|
+
def favicon
|
31
|
+
[200, {'Content-Type' => 'image/x-icon'}, ['']]
|
32
|
+
end
|
33
|
+
|
34
|
+
end #Preview
|
35
|
+
|
36
|
+
end #Ruhoh
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
class Ruhoh
|
4
|
+
|
5
|
+
class HelperMustache < Mustache
|
6
|
+
|
7
|
+
class HelperContext < Context
|
8
|
+
|
9
|
+
# Overload find method to catch helper expressions
|
10
|
+
def find(obj, key, default = nil)
|
11
|
+
return super unless key.to_s.index('?')
|
12
|
+
|
13
|
+
puts "=> Executing helper: #{key}"
|
14
|
+
context, helper = key.to_s.split('?')
|
15
|
+
context = context.empty? ? obj : super(obj, context)
|
16
|
+
|
17
|
+
self.mustache_in_stack.__send__ helper, context
|
18
|
+
end
|
19
|
+
|
20
|
+
end #HelperContext
|
21
|
+
|
22
|
+
def context
|
23
|
+
@context ||= HelperContext.new(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def partial(name)
|
27
|
+
Ruhoh::DB.partials[name.to_s]
|
28
|
+
end
|
29
|
+
|
30
|
+
def debug(sub_context)
|
31
|
+
puts "=>DEBUG:"
|
32
|
+
puts sub_context.class
|
33
|
+
puts sub_context.inspect
|
34
|
+
"<pre>#{sub_context.class}\n#{sub_context.pretty_inspect}</pre>"
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_tags(sub_context)
|
38
|
+
if sub_context.is_a?(Array)
|
39
|
+
sub_context.map { |id|
|
40
|
+
self.context['_posts']['tags'][id] if self.context['_posts']['tags'][id]
|
41
|
+
}
|
42
|
+
else
|
43
|
+
tags = []
|
44
|
+
self.context['_posts']['tags'].each_value { |tag|
|
45
|
+
tags << tag
|
46
|
+
}
|
47
|
+
tags
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_posts(sub_context)
|
52
|
+
sub_context = sub_context.is_a?(Array) ? sub_context : self.context['_posts']['chronological']
|
53
|
+
|
54
|
+
sub_context.map { |id|
|
55
|
+
self.context['_posts']['dictionary'][id] if self.context['_posts']['dictionary'][id]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_pages(sub_context)
|
60
|
+
puts "=> call: pages_list with context: #{sub_context}"
|
61
|
+
pages = []
|
62
|
+
if sub_context.is_a?(Array)
|
63
|
+
sub_context.each do |id|
|
64
|
+
if self.context[:pages][id]
|
65
|
+
pages << self.context[:pages][id]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
else
|
69
|
+
self.context[:pages].each_value {|page| pages << page }
|
70
|
+
end
|
71
|
+
pages
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_categories(sub_context)
|
75
|
+
if sub_context.is_a?(Array)
|
76
|
+
sub_context.map { |id|
|
77
|
+
self.context['_posts']['categories'][id] if self.context['_posts']['categories'][id]
|
78
|
+
}
|
79
|
+
else
|
80
|
+
cats = []
|
81
|
+
self.context['_posts']['categories'].each_value { |cat|
|
82
|
+
cats << cat
|
83
|
+
}
|
84
|
+
cats
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def analytics
|
89
|
+
analytics_config = self.context['site']['config']['analytics']
|
90
|
+
return '' unless analytics_config && analytics_config['provider']
|
91
|
+
code = self.partial("analytics-providers/#{analytics_config['provider']}")
|
92
|
+
return "<h2 style='color:red'>!Analytics Provider partial for '#{analytics_config['provider']}' not found </h2>" if code.nil?
|
93
|
+
|
94
|
+
self.render(code)
|
95
|
+
end
|
96
|
+
|
97
|
+
def comments
|
98
|
+
comments_config = self.context['site']['config']['comments']
|
99
|
+
return '' unless comments_config && comments_config['provider']
|
100
|
+
code = self.partial("comments-providers/#{comments_config['provider']}")
|
101
|
+
puts "comments-provders/#{comments_config['provider']}"
|
102
|
+
return "<h2 style='color:red'>!Comments Provider partial for '#{comments_config['provider']}' not found </h2>" if code.nil?
|
103
|
+
|
104
|
+
self.render(code)
|
105
|
+
end
|
106
|
+
|
107
|
+
end #HelperMustache
|
108
|
+
|
109
|
+
end #Ruhoh
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Templater
|
4
|
+
|
5
|
+
def self.build_payload(page)
|
6
|
+
{
|
7
|
+
"page" => page.attributes,
|
8
|
+
"site" => Ruhoh::DB.site,
|
9
|
+
"pages" => Ruhoh::DB.pages,
|
10
|
+
"_posts" => Ruhoh::DB.posts,
|
11
|
+
"ASSET_PATH" => Ruhoh.config.asset_path
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.expand_and_render(page)
|
16
|
+
self.render(self.expand(page), page)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.render(output, page)
|
20
|
+
Ruhoh::HelperMustache.render(output, self.build_payload(page))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Expand the page.
|
24
|
+
# Places page content into sub-template then into master template if available.
|
25
|
+
def self.expand(page)
|
26
|
+
output = page.sub_layout['content'].gsub(Ruhoh::Utils::ContentRegex, page.content)
|
27
|
+
|
28
|
+
# An undefined master means the page/post layouts is only one deep.
|
29
|
+
# This means it expects to load directly into a master template.
|
30
|
+
if page.master_layout && page.master_layout['content']
|
31
|
+
output = page.master_layout['content'].gsub(Ruhoh::Utils::ContentRegex, output);
|
32
|
+
end
|
33
|
+
|
34
|
+
output
|
35
|
+
end
|
36
|
+
|
37
|
+
end #Templater
|
38
|
+
|
39
|
+
end #Ruhoh
|
data/lib/ruhoh/utils.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
|
3
|
+
module Utils
|
4
|
+
|
5
|
+
FMregex = /^---\n(.|\n)*---\n/
|
6
|
+
ContentRegex = /\{\{\s*content\s*\}\}/i
|
7
|
+
|
8
|
+
# Relative file_path from site_source
|
9
|
+
def self.parse_file(*args)
|
10
|
+
path = File.__send__ :join, args
|
11
|
+
|
12
|
+
raise "File not found: #{path}" unless File.exist?(path)
|
13
|
+
|
14
|
+
page = File.open(path).read
|
15
|
+
front_matter = page.match(FMregex)
|
16
|
+
raise "Invalid Frontmatter" unless front_matter
|
17
|
+
|
18
|
+
data = YAML.load(front_matter[0].gsub(/---\n/, "")) || {}
|
19
|
+
|
20
|
+
{
|
21
|
+
"data" => data,
|
22
|
+
"content" => page.gsub(FMregex, '')
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end #Ruhoh
|
data/lib/ruhoh/watch.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'directory_watcher'
|
2
|
+
|
3
|
+
class Ruhoh
|
4
|
+
module Watch
|
5
|
+
|
6
|
+
# Internal: Watch website source directory for file changes.
|
7
|
+
# The observer triggers data regeneration as files change
|
8
|
+
# in order to keep the data up to date in real time.
|
9
|
+
#
|
10
|
+
# Returns: Nothing
|
11
|
+
def self.start
|
12
|
+
raise "Ruhoh.config cannot be nil.\n To set config call: Ruhoh.setup" unless Ruhoh.config
|
13
|
+
puts "=> Start watching: #{Ruhoh.paths.site_source}"
|
14
|
+
glob = ''
|
15
|
+
|
16
|
+
# Watch all files + all sub directories except for special folders e.g '_database'
|
17
|
+
Dir.chdir(Ruhoh.paths.site_source) {
|
18
|
+
dirs = Dir['*'].select { |x| File.directory?(x) }
|
19
|
+
dirs -= [Ruhoh.folders.database]
|
20
|
+
dirs = dirs.map { |x| "#{x}/**/*" }
|
21
|
+
dirs += ['*']
|
22
|
+
glob = dirs
|
23
|
+
}
|
24
|
+
|
25
|
+
dw = DirectoryWatcher.new(Ruhoh.paths.site_source, {
|
26
|
+
:glob => glob,
|
27
|
+
:pre_load => true
|
28
|
+
})
|
29
|
+
dw.interval = 1
|
30
|
+
dw.add_observer {|*args|
|
31
|
+
args.each {|event|
|
32
|
+
path = event['path'].gsub(Ruhoh.paths.site_source, '')
|
33
|
+
puts path
|
34
|
+
if path =~ Regexp.new("^\/?#{Ruhoh.folders.posts}")
|
35
|
+
puts "Watch: update posts"
|
36
|
+
Ruhoh::DB.update(:posts)
|
37
|
+
Ruhoh::DB.update(:routes)
|
38
|
+
elsif path =~ Regexp.new("^\/?#{Ruhoh.folders.templates}")
|
39
|
+
puts "Watch: update themes"
|
40
|
+
Ruhoh::DB.update(:layouts)
|
41
|
+
Ruhoh::DB.update(:partials)
|
42
|
+
else
|
43
|
+
puts "Watch: update pages"
|
44
|
+
Ruhoh::DB.update(:pages)
|
45
|
+
Ruhoh::DB.update(:routes)
|
46
|
+
end
|
47
|
+
|
48
|
+
t = Time.now.strftime("%H:%M:%S")
|
49
|
+
puts "[#{t}] regeneration: #{args.size} files changed"
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
dw.start
|
54
|
+
end
|
55
|
+
|
56
|
+
end #Watch
|
57
|
+
end #Ruhoh
|
data/lib/ruhoh.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'time'
|
4
|
+
require 'cgi'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
require 'mustache'
|
8
|
+
|
9
|
+
require 'ruhoh/utils'
|
10
|
+
require 'ruhoh/parsers/posts'
|
11
|
+
require 'ruhoh/parsers/pages'
|
12
|
+
require 'ruhoh/parsers/routes'
|
13
|
+
require 'ruhoh/parsers/layouts'
|
14
|
+
require 'ruhoh/parsers/partials'
|
15
|
+
require 'ruhoh/parsers/site'
|
16
|
+
require 'ruhoh/db'
|
17
|
+
require 'ruhoh/templaters/helper_mustache'
|
18
|
+
require 'ruhoh/templaters/templater'
|
19
|
+
require 'ruhoh/converters/converter'
|
20
|
+
require 'ruhoh/page'
|
21
|
+
require 'ruhoh/preview'
|
22
|
+
require 'ruhoh/watch'
|
23
|
+
|
24
|
+
class Ruhoh
|
25
|
+
|
26
|
+
class << self; attr_reader :folders, :files, :config, :paths, :filters end
|
27
|
+
|
28
|
+
Folders = Struct.new(:database, :posts, :templates, :themes, :layouts, :partials, :media)
|
29
|
+
Files = Struct.new(:site, :config)
|
30
|
+
Filters = Struct.new(:posts, :pages, :static)
|
31
|
+
Config = Struct.new(:permalink, :theme, :asset_path)
|
32
|
+
Paths = Struct.new(
|
33
|
+
:site_source,
|
34
|
+
:database,
|
35
|
+
:posts,
|
36
|
+
:theme,
|
37
|
+
:layouts,
|
38
|
+
:partials,
|
39
|
+
:global_partials,
|
40
|
+
:media
|
41
|
+
)
|
42
|
+
|
43
|
+
# Public: Setup Ruhoh utilities relative to the current directory
|
44
|
+
# of the application and its corresponding ruhoh.json file.
|
45
|
+
#
|
46
|
+
def self.setup
|
47
|
+
@folders = Folders.new('_database', '_posts', '_templates', 'themes', 'layouts', 'partials', "_media")
|
48
|
+
@files = Files.new('_site.yml', '_config.yml')
|
49
|
+
@filters = Filters.new
|
50
|
+
@config = Config.new
|
51
|
+
@paths = Paths.new
|
52
|
+
|
53
|
+
config = { 'site_source' => Dir.getwd }
|
54
|
+
site_config = YAML.load_file( File.join(config['site_source'], '_config.yml') )
|
55
|
+
|
56
|
+
@config.permalink = site_config['permalink'] || :date
|
57
|
+
@config.theme = site_config['theme']
|
58
|
+
@config.asset_path = File.join('/', @folders.templates, @folders.themes, @config.theme)
|
59
|
+
|
60
|
+
@paths.site_source = config['site_source']
|
61
|
+
@paths.database = self.absolute_path(@folders.database)
|
62
|
+
@paths.posts = self.absolute_path(@folders.posts)
|
63
|
+
|
64
|
+
@paths.theme = self.absolute_path(@folders.templates, @folders.themes, @config.theme)
|
65
|
+
@paths.layouts = self.absolute_path(@folders.templates, @folders.themes, @config.theme, @folders.layouts)
|
66
|
+
@paths.partials = self.absolute_path(@folders.templates, @folders.themes, @config.theme, @folders.partials)
|
67
|
+
@paths.global_partials = self.absolute_path(@folders.templates, @folders.partials)
|
68
|
+
@paths.media = self.absolute_path(@folders.media)
|
69
|
+
|
70
|
+
|
71
|
+
# filename filters:
|
72
|
+
@filters.pages = { 'names' => [], 'regexes' => [] }
|
73
|
+
exclude = ['Gemfile', 'Gemfile.lock', 'config.ru', 'README.md']
|
74
|
+
exclude.each {|node|
|
75
|
+
@filters.pages['names'] << node if node.is_a?(String)
|
76
|
+
@filters.pages['regexes'] << node if node.is_a?(Regexp)
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.absolute_path(*args)
|
81
|
+
File.__send__ :join, args.unshift(self.paths.site_source)
|
82
|
+
end
|
83
|
+
|
84
|
+
end # Ruhoh
|
data/ruhoh.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
$LOAD_PATH.unshift 'lib'
|
2
|
+
require 'ruhoh/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "ruhoh"
|
6
|
+
s.version = Ruhoh::Version
|
7
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
8
|
+
s.license = "http://unlicense.org/"
|
9
|
+
s.summary = 'Ruby based library to process your Ruhoh static blog.'
|
10
|
+
s.homepage = "http://github.com/plusjade/ruhoh.rb"
|
11
|
+
s.email = "plusjade@gmail.com"
|
12
|
+
s.authors = ['Jade Dominguez']
|
13
|
+
s.description = 'Ruhoh is a Universal API for your static blog.'
|
14
|
+
s.executables = ["ruhoh"]
|
15
|
+
|
16
|
+
# dependencies defined in Gemfile
|
17
|
+
s.add_dependency 'rack'
|
18
|
+
s.add_dependency 'mustache'
|
19
|
+
s.add_dependency 'directory_watcher'
|
20
|
+
s.add_dependency 'maruku'
|
21
|
+
|
22
|
+
# = MANIFEST =
|
23
|
+
s.files = %w[
|
24
|
+
README.md
|
25
|
+
bin/ruhoh
|
26
|
+
lib/ruhoh.rb
|
27
|
+
lib/ruhoh/client.rb
|
28
|
+
lib/ruhoh/compiler.rb
|
29
|
+
lib/ruhoh/converters/converter.rb
|
30
|
+
lib/ruhoh/db.rb
|
31
|
+
lib/ruhoh/page.rb
|
32
|
+
lib/ruhoh/parsers/layouts.rb
|
33
|
+
lib/ruhoh/parsers/pages.rb
|
34
|
+
lib/ruhoh/parsers/partials.rb
|
35
|
+
lib/ruhoh/parsers/posts.rb
|
36
|
+
lib/ruhoh/parsers/routes.rb
|
37
|
+
lib/ruhoh/parsers/site.rb
|
38
|
+
lib/ruhoh/preview.rb
|
39
|
+
lib/ruhoh/templaters/helper_mustache.rb
|
40
|
+
lib/ruhoh/templaters/templater.rb
|
41
|
+
lib/ruhoh/utils.rb
|
42
|
+
lib/ruhoh/version.rb
|
43
|
+
lib/ruhoh/watch.rb
|
44
|
+
ruhoh.gemspec
|
45
|
+
]
|
46
|
+
# = MANIFEST =
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruhoh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jade Dominguez
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-07 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rack
|
16
|
+
requirement: &70125560161900 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70125560161900
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mustache
|
27
|
+
requirement: &70125560161480 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70125560161480
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: directory_watcher
|
38
|
+
requirement: &70125560190740 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70125560190740
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: maruku
|
49
|
+
requirement: &70125560190320 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70125560190320
|
58
|
+
description: Ruhoh is a Universal API for your static blog.
|
59
|
+
email: plusjade@gmail.com
|
60
|
+
executables:
|
61
|
+
- ruhoh
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- README.md
|
66
|
+
- bin/ruhoh
|
67
|
+
- lib/ruhoh.rb
|
68
|
+
- lib/ruhoh/client.rb
|
69
|
+
- lib/ruhoh/compiler.rb
|
70
|
+
- lib/ruhoh/converters/converter.rb
|
71
|
+
- lib/ruhoh/db.rb
|
72
|
+
- lib/ruhoh/page.rb
|
73
|
+
- lib/ruhoh/parsers/layouts.rb
|
74
|
+
- lib/ruhoh/parsers/pages.rb
|
75
|
+
- lib/ruhoh/parsers/partials.rb
|
76
|
+
- lib/ruhoh/parsers/posts.rb
|
77
|
+
- lib/ruhoh/parsers/routes.rb
|
78
|
+
- lib/ruhoh/parsers/site.rb
|
79
|
+
- lib/ruhoh/preview.rb
|
80
|
+
- lib/ruhoh/templaters/helper_mustache.rb
|
81
|
+
- lib/ruhoh/templaters/templater.rb
|
82
|
+
- lib/ruhoh/utils.rb
|
83
|
+
- lib/ruhoh/version.rb
|
84
|
+
- lib/ruhoh/watch.rb
|
85
|
+
- ruhoh.gemspec
|
86
|
+
homepage: http://github.com/plusjade/ruhoh.rb
|
87
|
+
licenses:
|
88
|
+
- http://unlicense.org/
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.8.17
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: Ruby based library to process your Ruhoh static blog.
|
111
|
+
test_files: []
|