ruhoh 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +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: []
|