ruhoh 2.3 → 2.4
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/Gemfile +0 -1
- data/features/base_path.feature +26 -0
- data/features/data.feature +31 -0
- data/features/json.feature +33 -0
- data/features/layouts.feature +15 -0
- data/features/support/helpers.rb +7 -1
- data/history.json +22 -0
- data/lib/ruhoh.rb +18 -8
- data/lib/ruhoh/base/collection.rb +6 -2
- data/lib/ruhoh/base/compiler.rb +22 -11
- data/lib/ruhoh/base/model.rb +3 -31
- data/lib/ruhoh/base/model_view.rb +0 -1
- data/lib/ruhoh/collections.rb +14 -7
- data/lib/ruhoh/parse.rb +85 -0
- data/lib/ruhoh/programs/watch.rb +1 -4
- data/lib/ruhoh/resources/data/collection.rb +17 -1
- data/lib/ruhoh/resources/media/compiler.rb +2 -10
- data/lib/ruhoh/resources/pages/compiler.rb +27 -32
- data/lib/ruhoh/resources/static/compiler.rb +2 -11
- data/lib/ruhoh/resources/theme/compiler.rb +4 -17
- data/lib/ruhoh/resources/widgets/collection_view.rb +1 -1
- data/lib/ruhoh/resources/widgets/compiler.rb +3 -11
- data/lib/ruhoh/utils.rb +0 -21
- data/lib/ruhoh/version.rb +1 -1
- data/lib/ruhoh/views/master_view.rb +26 -29
- data/system/plugins/sprockets/compiler.rb +2 -3
- metadata +5 -8
data/Gemfile
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: base_path
|
2
|
+
As a content publisher
|
3
|
+
I want to configure base_path
|
4
|
+
so I can publish content relative to arbitrary webhost file structure requirements.
|
5
|
+
|
6
|
+
Scenario: Setting base_path
|
7
|
+
Given some files with values:
|
8
|
+
| file | body |
|
9
|
+
| config.yml | base_path: '/hello/world' |
|
10
|
+
| _root/index.html | |
|
11
|
+
| essays/water.md | <span>{{ page.url }}</span> |
|
12
|
+
When I compile my site
|
13
|
+
Then my compiled site should have the file "hello/world/index.html"
|
14
|
+
And my compiled site should have the file "hello/world/essays/water/index.html"
|
15
|
+
And this file should contain the content node "span|/hello/world/essays/water"
|
16
|
+
|
17
|
+
Scenario: Setting base_path with compile_as_root
|
18
|
+
Given some files with values:
|
19
|
+
| file | body |
|
20
|
+
| config.yml | base_path: '/hello/world' \ncompile_as_root: true |
|
21
|
+
| _root/index.html | |
|
22
|
+
| essays/water.md | <span>{{ page.url }}</span> |
|
23
|
+
When I compile my site
|
24
|
+
Then my compiled site should have the file "index.html"
|
25
|
+
And my compiled site should have the file "essays/water/index.html"
|
26
|
+
And this file should contain the content node "span|/hello/world/essays/water"
|
data/features/data.feature
CHANGED
@@ -30,3 +30,34 @@ Feature: Data
|
|
30
30
|
And this file should contain the content node "city|alhambra"
|
31
31
|
And this file should contain the content node "li|mango"
|
32
32
|
And this file should contain the content node "li|kiwi"
|
33
|
+
|
34
|
+
Scenario: Defining a basic data structure in data.json
|
35
|
+
Given the file "data.json" with body:
|
36
|
+
"""
|
37
|
+
{
|
38
|
+
"address": {
|
39
|
+
"city": "alhambra"
|
40
|
+
},
|
41
|
+
"name": "jade",
|
42
|
+
"fruits": [
|
43
|
+
"mango",
|
44
|
+
"kiwi"
|
45
|
+
]
|
46
|
+
}
|
47
|
+
"""
|
48
|
+
And the file "_root/index.html" with body:
|
49
|
+
"""
|
50
|
+
<name>{{ data.name }}</name>
|
51
|
+
<city>{{ data.address.city }}</city>
|
52
|
+
<ul>
|
53
|
+
{{# data.fruits }}
|
54
|
+
<li>{{ . }}</li>
|
55
|
+
{{/ data.fruits }}
|
56
|
+
</ul>
|
57
|
+
"""
|
58
|
+
When I compile my site
|
59
|
+
Then my compiled site should have the file "index.html"
|
60
|
+
And this file should contain the content node "name|jade"
|
61
|
+
And this file should contain the content node "city|alhambra"
|
62
|
+
And this file should contain the content node "li|mango"
|
63
|
+
And this file should contain the content node "li|kiwi"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Feature: Config
|
2
|
+
As a content publisher
|
3
|
+
I want the option to configure my site in JSON
|
4
|
+
because JSON is the preferred data format for modern web-technologies
|
5
|
+
and it benefits me to learn and get used to it, not to mention YAML is more
|
6
|
+
problematic than it's worth.
|
7
|
+
|
8
|
+
Scenario: Setting configuration in JSON
|
9
|
+
Given some files with values:
|
10
|
+
| file | body |
|
11
|
+
| config.json | { "production_url" : "http://hello-world.com" } |
|
12
|
+
| _root/index.html | <span>{{ urls.production_url }}</span> |
|
13
|
+
When I compile my site
|
14
|
+
Then my compiled site should have the file "index.html"
|
15
|
+
And this file should contain the content node "span|http://hello-world.com"
|
16
|
+
|
17
|
+
Scenario: Setting Top Metadata in JSON
|
18
|
+
Given the file "_root/index.html" with body:
|
19
|
+
"""
|
20
|
+
{
|
21
|
+
"title" : "Hello World",
|
22
|
+
"author" : "Isosceles",
|
23
|
+
"tags" : ["apple", "orange"]
|
24
|
+
}
|
25
|
+
|
26
|
+
<title>{{ page.title }}</title>
|
27
|
+
|
28
|
+
<author>{{ page.author }}</author>
|
29
|
+
"""
|
30
|
+
When I compile my site
|
31
|
+
Then my compiled site should have the file "index.html"
|
32
|
+
And this file should contain the content node "title|Hello World"
|
33
|
+
And this file should contain the content node "author|Isosceles"
|
data/features/layouts.feature
CHANGED
@@ -39,3 +39,18 @@ Feature: Layouts
|
|
39
39
|
Then my compiled site should have the file "essays/hello/index.html"
|
40
40
|
And this file should contain the content node "div#minimal-layout|cookie dough"
|
41
41
|
And this file should contain the content node "div#minimal-sub-layout|cookie dough"
|
42
|
+
|
43
|
+
Scenario: Defining more than two layouts.
|
44
|
+
Given some files with values:
|
45
|
+
| file | body | layout |
|
46
|
+
| layouts/outer.md | <div id="minimal-outer-layout">outer {{{ content }}}</div> | |
|
47
|
+
| layouts/inner.md | <div id="minimal-inner-layout">inner {{{ content }}}</div> | outer |
|
48
|
+
| layouts/essays.md | <div id="minimal-layout">{{{ content }}}</div> | inner |
|
49
|
+
| essays/hello.md | cookie dough | essays |
|
50
|
+
When I compile my site
|
51
|
+
Then my compiled site should have the file "essays/hello/index.html"
|
52
|
+
And this file should contain the content node "div#minimal-outer-layout|inner"
|
53
|
+
And this file should contain the content node "div#minimal-inner-layout|cookie dough"
|
54
|
+
And this file should NOT contain the content node "div#minimal-inner-layout|outer"
|
55
|
+
And this file should NOT contain the content node "div#minimal-layout|outer"
|
56
|
+
And this file should NOT contain the content node "div#minimal-layout|inner"
|
data/features/support/helpers.rb
CHANGED
@@ -40,11 +40,17 @@ def make_file(opts)
|
|
40
40
|
metadata = data.empty? ? '' : data.to_yaml.to_s + "\n---\n"
|
41
41
|
|
42
42
|
File.open(path, "w+") { |file|
|
43
|
-
|
43
|
+
if metadata.empty?
|
44
|
+
file.puts <<-TEXT
|
45
|
+
#{ opts[:body] }
|
46
|
+
TEXT
|
47
|
+
else
|
48
|
+
file.puts <<-TEXT
|
44
49
|
#{ metadata }
|
45
50
|
|
46
51
|
#{ opts[:body] }
|
47
52
|
TEXT
|
53
|
+
end
|
48
54
|
}
|
49
55
|
end
|
50
56
|
|
data/history.json
CHANGED
@@ -1,4 +1,26 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"version" : "2.4",
|
4
|
+
"date" : "10.09.2013",
|
5
|
+
"changes" : [
|
6
|
+
"(Internal) Add Ruhoh::Parse service to parse various file formats.",
|
7
|
+
"Remove psych gem dependency for yaml parsing. Native yaml lib should still work for ruby 1.9.2+"
|
8
|
+
],
|
9
|
+
"features" : [
|
10
|
+
"@stebalien exposes total_pages in pagination view.",
|
11
|
+
"@stebalien adds spport for unlimited nested sub-layouts.",
|
12
|
+
"Support JSON data-type in config, data, and page metadata.",
|
13
|
+
"Add 'compile_as_root' configuration setting to automated hosting on sub-directory style web-hosts."
|
14
|
+
],
|
15
|
+
"bugs" : [
|
16
|
+
"Don't choke on 'theme' check if them config not formatted correctly.",
|
17
|
+
"Ensure base_path is well-formed with leading and trailing slash.",
|
18
|
+
"Expose urls.production and urls.production_url in the view.",
|
19
|
+
"data.yml/data.json should be discoverable on every cascade level.",
|
20
|
+
"Allow collections to be discoverable from all cascade levels."
|
21
|
+
]
|
22
|
+
}
|
23
|
+
,
|
2
24
|
{
|
3
25
|
"version" : "2.3",
|
4
26
|
"date" : "25.08.2013",
|
data/lib/ruhoh.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
Encoding.default_internal = 'UTF-8'
|
3
|
-
require 'yaml'
|
4
|
-
require 'psych'
|
5
|
-
YAML::ENGINE.yamler = 'psych'
|
6
3
|
require 'json'
|
7
4
|
require 'time'
|
8
5
|
require 'cgi'
|
@@ -11,12 +8,14 @@ require 'ostruct'
|
|
11
8
|
require 'delegate'
|
12
9
|
require 'digest'
|
13
10
|
require 'observer'
|
11
|
+
require 'set'
|
14
12
|
|
15
13
|
require 'mustache'
|
16
14
|
|
17
15
|
require 'ruhoh/logger'
|
18
16
|
require 'ruhoh/utils'
|
19
17
|
require 'ruhoh/friend'
|
18
|
+
require 'ruhoh/parse'
|
20
19
|
|
21
20
|
require 'ruhoh/converter'
|
22
21
|
require 'ruhoh/views/master_view'
|
@@ -65,7 +64,7 @@ class Ruhoh
|
|
65
64
|
def config(reload=false)
|
66
65
|
return @config unless (reload or @config.nil?)
|
67
66
|
|
68
|
-
config = Ruhoh::
|
67
|
+
config = Ruhoh::Parse.data_file(@base, "config") || {}
|
69
68
|
config['compiled'] = config['compiled'] ? File.expand_path(config['compiled']) : "compiled"
|
70
69
|
|
71
70
|
config['_root'] ||= {}
|
@@ -92,7 +91,7 @@ class Ruhoh
|
|
92
91
|
@paths.system = File.join(Ruhoh::Root, "system")
|
93
92
|
@paths.compiled = @config["compiled"]
|
94
93
|
|
95
|
-
theme = @config.find{ |resource, data| data['use'] == "theme" }
|
94
|
+
theme = @config.find { |resource, data| data.is_a?(Hash) && data['use'] == "theme" }
|
96
95
|
if theme
|
97
96
|
Ruhoh::Friend.say { plain "Using theme: \"#{theme[0]}\""}
|
98
97
|
@paths.theme = File.join(@base, theme[0])
|
@@ -140,9 +139,20 @@ class Ruhoh
|
|
140
139
|
end
|
141
140
|
|
142
141
|
def base_path
|
143
|
-
(env == 'production')
|
144
|
-
|
145
|
-
|
142
|
+
return '/' unless (env == 'production')
|
143
|
+
|
144
|
+
string = config['base_path'].chomp('/').reverse.chomp('/').reverse
|
145
|
+
return '/' if string.empty? || string == '/'
|
146
|
+
"/#{ string }/"
|
147
|
+
end
|
148
|
+
|
149
|
+
def compiled_path(url)
|
150
|
+
if config['compile_as_root']
|
151
|
+
url = url.gsub(/^#{ base_path.chomp('/') }\/?/, '')
|
152
|
+
end
|
153
|
+
|
154
|
+
path = File.expand_path(File.join(paths.compiled, url)).gsub(/\/{2,}/, '/')
|
155
|
+
CGI.unescape(path)
|
146
156
|
end
|
147
157
|
|
148
158
|
# @config['base_path'] is assumed to be well-formed.
|
@@ -81,7 +81,7 @@ module Ruhoh::Base
|
|
81
81
|
def config
|
82
82
|
config = @ruhoh.config[resource_name] || {}
|
83
83
|
unless config.is_a?(Hash)
|
84
|
-
Ruhoh.log.error("'#{resource_name}' config key in config
|
84
|
+
Ruhoh.log.error("'#{resource_name}' config key in config" +
|
85
85
|
" is a #{config.class}; it needs to be a Hash (object).")
|
86
86
|
end
|
87
87
|
config
|
@@ -237,6 +237,10 @@ module Ruhoh::Base
|
|
237
237
|
File.open(pointer['realpath'], 'r:UTF-8') { |f| f.read }
|
238
238
|
end
|
239
239
|
|
240
|
+
def compiled_path
|
241
|
+
@compiled_path ||= @ruhoh.compiled_path(@ruhoh.to_url(url_endpoint))
|
242
|
+
end
|
243
|
+
|
240
244
|
protected
|
241
245
|
|
242
246
|
# Load the registered resource else default to Pages if not configured.
|
@@ -251,7 +255,7 @@ module Ruhoh::Base
|
|
251
255
|
else
|
252
256
|
klass = camelize(type)
|
253
257
|
Friend.say {
|
254
|
-
red "#{resource_name} resource set to use:'#{type}' in config
|
258
|
+
red "#{resource_name} resource set to use:'#{type}' in config" +
|
255
259
|
" but Ruhoh::Resources::#{klass} does not exist."
|
256
260
|
}
|
257
261
|
abort
|
data/lib/ruhoh/base/compiler.rb
CHANGED
@@ -9,6 +9,26 @@ module Ruhoh::Base
|
|
9
9
|
@ruhoh = collection.ruhoh
|
10
10
|
@collection = collection
|
11
11
|
end
|
12
|
+
|
13
|
+
def setup_compilable
|
14
|
+
return false unless collection_exists?
|
15
|
+
|
16
|
+
compile_collection_path
|
17
|
+
end
|
18
|
+
|
19
|
+
def compile_collection_path
|
20
|
+
FileUtils.mkdir_p(@collection.compiled_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def collection_exists?
|
24
|
+
collection = @collection
|
25
|
+
unless @collection.paths?
|
26
|
+
Ruhoh::Friend.say { yellow "#{ collection.resource_name.capitalize }: directory not found - skipping." }
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
Ruhoh::Friend.say { cyan "#{ collection.resource_name.capitalize }: (copying valid files)" }
|
30
|
+
true
|
31
|
+
end
|
12
32
|
end
|
13
33
|
|
14
34
|
module CompilableAsset
|
@@ -22,24 +42,15 @@ module Ruhoh::Base
|
|
22
42
|
#
|
23
43
|
# @returns Nothing.
|
24
44
|
def run
|
25
|
-
|
26
|
-
|
27
|
-
unless @collection.paths?
|
28
|
-
Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
|
29
|
-
return
|
30
|
-
end
|
31
|
-
Ruhoh::Friend.say { cyan "#{collection.resource_name.capitalize}: (copying valid files)" }
|
45
|
+
return unless setup_compilable
|
32
46
|
|
33
|
-
compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
|
34
|
-
FileUtils.mkdir_p compiled_path
|
35
|
-
|
36
47
|
manifest = {}
|
37
48
|
@collection.files.values.each do |pointer|
|
38
49
|
digest = Digest::MD5.file(pointer['realpath']).hexdigest
|
39
50
|
digest_file = pointer['id'].sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
|
40
51
|
manifest[pointer['id']] = digest_file
|
41
52
|
|
42
|
-
compiled_file = File.join(compiled_path, digest_file)
|
53
|
+
compiled_file = File.join(@collection.compiled_path, digest_file)
|
43
54
|
FileUtils.mkdir_p File.dirname(compiled_file)
|
44
55
|
FileUtils.cp_r pointer['realpath'], compiled_file
|
45
56
|
Ruhoh::Friend.say { green " > #{pointer['id']}" }
|
data/lib/ruhoh/base/model.rb
CHANGED
@@ -47,7 +47,6 @@ module Ruhoh::Base
|
|
47
47
|
module PageLike
|
48
48
|
include Modelable
|
49
49
|
|
50
|
-
FMregex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
51
50
|
DateMatcher = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
|
52
51
|
Matcher = /^(.+\/)*(.*)(\.[^.]+)$/
|
53
52
|
|
@@ -88,44 +87,17 @@ module Ruhoh::Base
|
|
88
87
|
!!@pointer['realpath']
|
89
88
|
end
|
90
89
|
|
91
|
-
#
|
92
|
-
# File API is currently defines:
|
93
|
-
# 1. Top YAML meta-data
|
94
|
-
# 2. Page Body
|
95
|
-
#
|
90
|
+
# See Ruhoh::Parse.page_file
|
96
91
|
# @returns[Hash Object] processed top meta-data, raw (unconverted) content body
|
97
92
|
def parse_page_file
|
98
93
|
raise "File not found: #{@pointer['realpath']}" unless File.exist?(@pointer['realpath'])
|
99
|
-
|
100
|
-
page = File.open(@pointer['realpath'], 'r:UTF-8') {|f| f.read }
|
101
|
-
|
102
|
-
begin
|
103
|
-
front_matter = page.match(FMregex)
|
104
|
-
rescue => e
|
105
|
-
raise "Error trying to read meta-data from #{@pointer['realpath']}." +
|
106
|
-
" Check your folder configuration. Error details: #{e}"
|
107
|
-
end
|
108
|
-
|
109
|
-
data = front_matter ?
|
110
|
-
(YAML.load(front_matter[0].gsub(/---\n/, "")) || {}) :
|
111
|
-
{}
|
112
|
-
|
113
|
-
result = {
|
114
|
-
"data" => data,
|
115
|
-
"content" => page.gsub(FMregex, '')
|
116
|
-
}
|
94
|
+
result = Ruhoh::Parse.page_file(@pointer['realpath'])
|
117
95
|
|
118
96
|
# variable cache
|
119
|
-
@data = data
|
97
|
+
@data = result["data"]
|
120
98
|
@content = result['content']
|
121
99
|
|
122
100
|
result
|
123
|
-
rescue Psych::SyntaxError => e
|
124
|
-
Ruhoh.log.error("Psych::SyntaxError while parsing top YAML Metadata in #{ @pointer['realpath'] }\n" +
|
125
|
-
"#{ e.message }\n" +
|
126
|
-
"Try validating the YAML metadata using http://yamllint.com"
|
127
|
-
)
|
128
|
-
nil
|
129
101
|
end
|
130
102
|
|
131
103
|
def parse_page_filename(filename)
|
data/lib/ruhoh/collections.rb
CHANGED
@@ -47,7 +47,7 @@ class Ruhoh
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def all
|
50
|
-
(discover + registered).
|
50
|
+
(discover + registered).to_a
|
51
51
|
end
|
52
52
|
|
53
53
|
def base
|
@@ -67,12 +67,19 @@ class Ruhoh
|
|
67
67
|
end
|
68
68
|
|
69
69
|
# discover all the resource mappings
|
70
|
+
# @return[Set]
|
70
71
|
def discover
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
results = Set.new
|
73
|
+
|
74
|
+
@ruhoh.cascade.each do |h|
|
75
|
+
FileUtils.cd(h["path"]) do
|
76
|
+
results += Dir['*'].select { |x|
|
77
|
+
File.directory?(x) && !["plugins", 'compiled'].include?(x)
|
78
|
+
}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
results
|
76
83
|
end
|
77
84
|
|
78
85
|
def acting_as_pages
|
@@ -146,7 +153,7 @@ class Ruhoh
|
|
146
153
|
else
|
147
154
|
klass = camelize(type)
|
148
155
|
Friend.say {
|
149
|
-
red "#{resource} resource set to use:'#{type}' in config
|
156
|
+
red "#{resource} resource set to use:'#{type}' in config but Ruhoh::Resources::#{klass} does not exist."
|
150
157
|
}
|
151
158
|
abort
|
152
159
|
end
|
data/lib/ruhoh/parse.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
module Parse
|
3
|
+
TopYAMLregex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
4
|
+
TopJSONregex = /^({\s*\n.*?\n?)^(}\s*$\n?)/m
|
5
|
+
|
6
|
+
# Primary method to parse the file as a page-like object.
|
7
|
+
# File API is currently defines:
|
8
|
+
# 1. Top meta-data
|
9
|
+
# 2. Page Body
|
10
|
+
#
|
11
|
+
# @returns[Hash Object] processed top meta-data, raw (unconverted) content body
|
12
|
+
def self.page_file(filepath)
|
13
|
+
result = {}
|
14
|
+
front_matter = nil
|
15
|
+
format = nil
|
16
|
+
page = File.open(filepath, 'r:UTF-8') { |f| f.read }
|
17
|
+
first_line = page.lines.first
|
18
|
+
|
19
|
+
begin
|
20
|
+
if (first_line.strip == '---')
|
21
|
+
front_matter = page.match(TopYAMLregex)
|
22
|
+
format = 'yaml'
|
23
|
+
elsif (first_line.strip == '{')
|
24
|
+
front_matter = page.match(TopJSONregex)
|
25
|
+
format = 'json'
|
26
|
+
end
|
27
|
+
rescue => e
|
28
|
+
raise "Error trying to read meta-data from #{ filepath }." +
|
29
|
+
" Check your folder configuration. Error details: #{e}"
|
30
|
+
end
|
31
|
+
|
32
|
+
if format == 'yaml'
|
33
|
+
data = yaml_for_pages(front_matter, filepath)
|
34
|
+
result["content"] = page.gsub(TopYAMLregex, '')
|
35
|
+
else
|
36
|
+
data = json_for_pages(front_matter, filepath)
|
37
|
+
result["content"] = page.gsub(TopJSONregex, '')
|
38
|
+
end
|
39
|
+
|
40
|
+
result["data"] = data
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.data_file(*args)
|
45
|
+
base = File.__send__(:join, args)
|
46
|
+
|
47
|
+
filepath = nil
|
48
|
+
["#{ base }.json", "#{ base }.yml", "#{ base }.yaml"].each do |result|
|
49
|
+
filepath = result and break if File.exist?(result)
|
50
|
+
end
|
51
|
+
return nil unless filepath
|
52
|
+
|
53
|
+
file = File.open(filepath, 'r:UTF-8') { |f| f.read }
|
54
|
+
|
55
|
+
File.extname(filepath) == ".json" ? json(file) : yaml(file)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.yaml(file)
|
59
|
+
YAML.load(file) || {}
|
60
|
+
rescue Psych::SyntaxError => e
|
61
|
+
Ruhoh.log.error("ERROR in #{filepath}: #{e.message}")
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.json(file)
|
66
|
+
JSON.load(file) || {}
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.yaml_for_pages(front_matter, filepath)
|
70
|
+
return {} unless front_matter
|
71
|
+
YAML.load(front_matter[0].gsub(/---\n/, "")) || {}
|
72
|
+
rescue Psych::SyntaxError => e
|
73
|
+
Ruhoh.log.error("Psych::SyntaxError while parsing top YAML Metadata in #{ filepath }\n" +
|
74
|
+
"#{ e.message }\n" +
|
75
|
+
"Try validating the YAML metadata using http://yamllint.com"
|
76
|
+
)
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.json_for_pages(front_matter, filepath)
|
81
|
+
return {} unless front_matter
|
82
|
+
JSON.load(front_matter[0]) || {}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/ruhoh/programs/watch.rb
CHANGED
@@ -27,7 +27,7 @@ class Ruhoh
|
|
27
27
|
yellow "Watch [#{Time.now.strftime("%H:%M:%S")}] [Update #{path}] : #{args.size} files changed"
|
28
28
|
}
|
29
29
|
|
30
|
-
if
|
30
|
+
if %w{ config.json config.yml config.yaml }.include?(path)
|
31
31
|
ruhoh.config true
|
32
32
|
else
|
33
33
|
separator = File::ALT_SEPARATOR ?
|
@@ -38,10 +38,7 @@ class Ruhoh
|
|
38
38
|
ruhoh.cache.delete(ruhoh.collection(resource).files_cache_key)
|
39
39
|
ruhoh.cache.delete("#{ resource }-all")
|
40
40
|
|
41
|
-
puts("HERE", resource)
|
42
|
-
|
43
41
|
ruhoh.collection(resource).load_watcher.update(path)
|
44
|
-
puts(ruhoh.collection(resource))
|
45
42
|
end
|
46
43
|
end
|
47
44
|
end
|
@@ -2,8 +2,24 @@ module Ruhoh::Resources::Data
|
|
2
2
|
class Collection
|
3
3
|
include Ruhoh::Base::Collectable
|
4
4
|
|
5
|
+
# TODO: This is ugly but it works.
|
6
|
+
# Should handle data extensions in the cascade more elegantly
|
5
7
|
def dictionary
|
6
|
-
|
8
|
+
found_path_prefix = nil
|
9
|
+
|
10
|
+
@ruhoh.cascade.reverse.map do |h|
|
11
|
+
path_prefix = File.join(h["path"], resource_name)
|
12
|
+
|
13
|
+
["#{ path_prefix }.json", "#{ path_prefix }.yml", "#{ path_prefix }.yaml"].each do |file|
|
14
|
+
found_path_prefix = path_prefix and break if File.exist?(file)
|
15
|
+
end
|
16
|
+
|
17
|
+
break if found_path_prefix
|
18
|
+
end
|
19
|
+
|
20
|
+
return {} unless found_path_prefix
|
21
|
+
|
22
|
+
Ruhoh::Parse.data_file(found_path_prefix) || {}
|
7
23
|
end
|
8
24
|
end
|
9
25
|
end
|
@@ -6,18 +6,10 @@ module Ruhoh::Resources::Media
|
|
6
6
|
# We can't use it now because there is automatic digest support
|
7
7
|
# but currently no way to dynamically update all media links in views with digest path.
|
8
8
|
def run
|
9
|
-
|
10
|
-
unless @collection.paths?
|
11
|
-
Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
|
12
|
-
return
|
13
|
-
end
|
14
|
-
Ruhoh::Friend.say { cyan "#{collection.resource_name.capitalize}: (copying valid files)" }
|
9
|
+
return unless setup_compilable
|
15
10
|
|
16
|
-
compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
|
17
|
-
FileUtils.mkdir_p compiled_path
|
18
|
-
|
19
11
|
@collection.files.values.each do |pointer|
|
20
|
-
compiled_file = File.join(compiled_path, pointer['id'])
|
12
|
+
compiled_file = File.join(@collection.compiled_path, pointer['id'])
|
21
13
|
FileUtils.mkdir_p File.dirname(compiled_file)
|
22
14
|
FileUtils.cp_r pointer['realpath'], compiled_file
|
23
15
|
Ruhoh::Friend.say { green " > #{pointer['id']}" }
|
@@ -7,17 +7,16 @@ module Ruhoh::Resources::Pages
|
|
7
7
|
pages = @collection.all
|
8
8
|
resource_name = @collection.resource_name
|
9
9
|
Ruhoh::Friend.say { cyan "#{resource_name.capitalize}: (#{pages.count} #{resource_name})" }
|
10
|
-
|
11
|
-
FileUtils.cd(@ruhoh.paths.compiled) {
|
12
|
-
pages.each do |data|
|
13
|
-
view = @ruhoh.master_view(data['pointer'])
|
14
10
|
|
15
|
-
|
16
|
-
|
11
|
+
pages.each do |data|
|
12
|
+
view = @ruhoh.master_view(data['pointer'])
|
13
|
+
|
14
|
+
FileUtils.mkdir_p File.dirname(view.compiled_path)
|
15
|
+
File.open(view.compiled_path, 'w:UTF-8') { |p| p.puts view.render_full }
|
16
|
+
|
17
|
+
Ruhoh::Friend.say { green " > #{data['id']}" }
|
18
|
+
end
|
17
19
|
|
18
|
-
Ruhoh::Friend.say { green " > #{data['id']}" }
|
19
|
-
end
|
20
|
-
}
|
21
20
|
|
22
21
|
pagination
|
23
22
|
rss
|
@@ -35,23 +34,22 @@ module Ruhoh::Resources::Pages
|
|
35
34
|
total_pages = (pages_count.to_f/config["per_page"]).ceil
|
36
35
|
|
37
36
|
Ruhoh::Friend.say { cyan "#{resource_name} paginator: (#{total_pages} pages)" }
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
}
|
51
|
-
FileUtils.mkdir_p File.dirname(view.compiled_path)
|
52
|
-
File.open(view.compiled_path, 'w:UTF-8') { |p| p.puts view.render_full }
|
53
|
-
Ruhoh::Friend.say { green " > #{view.page_data['url']}" }
|
37
|
+
|
38
|
+
total_pages.times.map { |i|
|
39
|
+
# if a root page is defined we assume it's getting compiled elsewhere.
|
40
|
+
next if (i.zero? && config["root_page"])
|
41
|
+
|
42
|
+
url = "#{config["url"]}/#{i+1}"
|
43
|
+
view = @ruhoh.master_view({"resource" => resource_name})
|
44
|
+
view.page_data = {
|
45
|
+
"layout" => config["layout"],
|
46
|
+
"current_page" => (i+1),
|
47
|
+
"total_pages" => total_pages,
|
48
|
+
"url" => @ruhoh.to_url(url)
|
54
49
|
}
|
50
|
+
FileUtils.mkdir_p File.dirname(view.compiled_path)
|
51
|
+
File.open(view.compiled_path, 'w:UTF-8') { |p| p.puts view.render_full }
|
52
|
+
Ruhoh::Friend.say { green " > #{view.page_data['url']}" }
|
55
53
|
}
|
56
54
|
end
|
57
55
|
|
@@ -88,15 +86,12 @@ module Ruhoh::Resources::Pages
|
|
88
86
|
}
|
89
87
|
end
|
90
88
|
|
91
|
-
|
92
|
-
compiled_path = CGI.unescape(@ruhoh.to_url(config['url'], "rss.xml"))
|
93
|
-
compiled_path = compiled_path.gsub(/^\//, '')
|
89
|
+
compiled_path = @ruhoh.compiled_path(@ruhoh.to_url(config['url'], "rss.xml"))
|
94
90
|
|
95
|
-
|
96
|
-
|
91
|
+
FileUtils.mkdir_p File.dirname(compiled_path)
|
92
|
+
File.open(compiled_path, 'w'){ |p| p.puts feed.to_xml }
|
97
93
|
|
98
|
-
|
99
|
-
}
|
94
|
+
Ruhoh::Friend.say { green " > #{compiled_path}" }
|
100
95
|
end
|
101
96
|
end
|
102
97
|
end
|
@@ -9,19 +9,10 @@ module Ruhoh::Resources::Static
|
|
9
9
|
#
|
10
10
|
# @returns Nothing.
|
11
11
|
def run
|
12
|
-
|
13
|
-
|
14
|
-
unless @collection.paths?
|
15
|
-
Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
|
16
|
-
return
|
17
|
-
end
|
18
|
-
Ruhoh::Friend.say { cyan "#{collection.resource_name.capitalize}: (copying valid files)" }
|
19
|
-
|
20
|
-
compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
|
21
|
-
FileUtils.mkdir_p compiled_path
|
12
|
+
return unless setup_compilable
|
22
13
|
|
23
14
|
@collection.files.values.each do |pointer|
|
24
|
-
compiled_file = File.join(compiled_path, pointer['id'])
|
15
|
+
compiled_file = File.join(@collection.compiled_path, pointer['id'])
|
25
16
|
|
26
17
|
FileUtils.mkdir_p File.dirname(compiled_file)
|
27
18
|
FileUtils.cp_r pointer['realpath'], compiled_file
|
@@ -2,27 +2,14 @@ module Ruhoh::Resources::Theme
|
|
2
2
|
class Compiler
|
3
3
|
include Ruhoh::Base::Compilable
|
4
4
|
|
5
|
-
def run
|
6
|
-
copy
|
7
|
-
end
|
8
|
-
|
9
5
|
# Copies all assets over to the compiled site.
|
10
6
|
# Note the compiled assets are namespaced at /assets/
|
11
|
-
def
|
12
|
-
|
13
|
-
unless @collection.paths?
|
14
|
-
Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
|
15
|
-
return
|
16
|
-
end
|
17
|
-
|
18
|
-
Ruhoh::Friend.say { cyan "Theme: ('#{collection.resource_name}' copying non-resource files)" }
|
19
|
-
|
20
|
-
theme = Ruhoh::Utils.url_to_path(@collection.url_endpoint, @ruhoh.paths.compiled)
|
21
|
-
FileUtils.mkdir_p theme
|
7
|
+
def run
|
8
|
+
return unless setup_compilable
|
22
9
|
|
23
10
|
self.files.each do |file|
|
24
11
|
original_file = File.join(@ruhoh.paths.theme, file)
|
25
|
-
compiled_file = File.join(
|
12
|
+
compiled_file = File.join(@collection.compiled_path, file)
|
26
13
|
FileUtils.mkdir_p File.dirname(compiled_file)
|
27
14
|
FileUtils.cp_r original_file, compiled_file
|
28
15
|
Ruhoh::Friend.say { green " > #{file}" }
|
@@ -39,7 +26,7 @@ module Ruhoh::Resources::Theme
|
|
39
26
|
}
|
40
27
|
end
|
41
28
|
|
42
|
-
# Checks a given asset filepath against any user-defined exclusion rules in
|
29
|
+
# Checks a given asset filepath against any user-defined exclusion rules in config
|
43
30
|
# Omit layouts, stylesheets, javascripts, media as they are handled by their respective resources.
|
44
31
|
# @returns[Boolean]
|
45
32
|
def is_valid_asset?(filepath)
|
@@ -9,7 +9,7 @@ module Ruhoh::Resources::Widgets
|
|
9
9
|
model = find("#{ name }/#{ (widget_config['use'] || "default") }")
|
10
10
|
return '' unless model
|
11
11
|
|
12
|
-
# merge the config
|
12
|
+
# merge the config data into the inline layout data.
|
13
13
|
# Note this is reversing the normal hierarchy
|
14
14
|
# in that inline should always override config level.
|
15
15
|
# However the inline in this case is set as implementation defaults
|
@@ -3,21 +3,13 @@ module Ruhoh::Resources::Widgets
|
|
3
3
|
include Ruhoh::Base::Compilable
|
4
4
|
|
5
5
|
def run
|
6
|
-
|
7
|
-
unless @collection.paths?
|
8
|
-
Ruhoh::Friend.say { yellow "#{collection.resource_name.capitalize}: directory not found - skipping." }
|
9
|
-
return
|
10
|
-
end
|
11
|
-
Ruhoh::Friend.say { cyan "#{collection.resource_name.capitalize}: (copying valid files)" }
|
12
|
-
|
13
|
-
compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
|
14
|
-
FileUtils.mkdir_p compiled_path
|
6
|
+
return unless setup_compilable
|
15
7
|
|
16
|
-
files = collection.files.values
|
8
|
+
files = @collection.files.values
|
17
9
|
files.delete_if { |p| !is_valid_file? (p['id']) }
|
18
10
|
|
19
11
|
files.each do |pointer|
|
20
|
-
compiled_file = File.join(compiled_path, pointer['id'])
|
12
|
+
compiled_file = File.join(@collection.compiled_path, pointer['id'])
|
21
13
|
FileUtils.mkdir_p File.dirname(compiled_file)
|
22
14
|
FileUtils.cp_r pointer['realpath'], compiled_file
|
23
15
|
Ruhoh::Friend.say { green " > #{pointer['id']}" }
|
data/lib/ruhoh/utils.rb
CHANGED
@@ -1,26 +1,5 @@
|
|
1
1
|
class Ruhoh
|
2
2
|
module Utils
|
3
|
-
|
4
|
-
FMregex = /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
5
|
-
|
6
|
-
def self.parse_yaml_file(*args)
|
7
|
-
filepath = File.__send__ :join, args
|
8
|
-
return nil unless File.exist? filepath
|
9
|
-
|
10
|
-
file = File.open(filepath, 'r:UTF-8') {|f| f.read }
|
11
|
-
yaml = YAML.load(file) || {}
|
12
|
-
yaml
|
13
|
-
rescue Psych::SyntaxError => e
|
14
|
-
Ruhoh.log.error("ERROR in #{filepath}: #{e.message}")
|
15
|
-
nil
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.url_to_path(url, base=nil)
|
19
|
-
url = url.gsub(/^\//, '')
|
20
|
-
parts = url.split('/')
|
21
|
-
parts = parts.unshift(base) if base
|
22
|
-
File.__send__(:join, parts)
|
23
|
-
end
|
24
3
|
|
25
4
|
def self.report(name, collection, invalid)
|
26
5
|
output = "#{collection.count}/#{collection.count + invalid.count} #{name} processed."
|
data/lib/ruhoh/version.rb
CHANGED
@@ -3,7 +3,6 @@ require 'ruhoh/views/rmustache'
|
|
3
3
|
module Ruhoh::Views
|
4
4
|
module Helpers ; end
|
5
5
|
class MasterView < RMustache
|
6
|
-
attr_reader :sub_layout, :master_layout
|
7
6
|
attr_accessor :page_data
|
8
7
|
|
9
8
|
def initialize(ruhoh, pointer_or_data)
|
@@ -27,8 +26,13 @@ module Ruhoh::Views
|
|
27
26
|
end
|
28
27
|
|
29
28
|
def render_full
|
30
|
-
|
31
|
-
|
29
|
+
if page_layouts.empty?
|
30
|
+
render_content
|
31
|
+
else
|
32
|
+
page_layouts.drop(1).reduce(render(page_layouts.first.content)) do |c, l|
|
33
|
+
render(l.content, :content => c)
|
34
|
+
end
|
35
|
+
end
|
32
36
|
end
|
33
37
|
|
34
38
|
def render_content
|
@@ -104,7 +108,7 @@ module Ruhoh::Views
|
|
104
108
|
#
|
105
109
|
# Returns: [String] The relative path to the compiled file for this page.
|
106
110
|
def compiled_path
|
107
|
-
path =
|
111
|
+
path = @ruhoh.compiled_path(@page_data['url'])
|
108
112
|
path = "index.html" if path.empty?
|
109
113
|
path += '/index.html' unless path =~ /\.\w+$/
|
110
114
|
path
|
@@ -112,38 +116,31 @@ module Ruhoh::Views
|
|
112
116
|
|
113
117
|
protected
|
114
118
|
|
115
|
-
def
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
+
def page_layouts
|
120
|
+
return @page_layouts unless @page_layouts.nil?
|
121
|
+
|
122
|
+
layout = if @page_data['layout']
|
123
|
+
layouts.find(@page_data['layout'], :all => true) or raise "Layout does not exist: #{@page_data['layout']}"
|
119
124
|
elsif @page_data['layout'] != false
|
120
125
|
# try default
|
121
|
-
|
126
|
+
layouts.find(@pointer['resource'], :all => true)
|
122
127
|
end
|
123
128
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
+
@page_layouts = if layout.nil?
|
130
|
+
[]
|
131
|
+
else
|
132
|
+
page_layouts = [layout]
|
133
|
+
until layout.layout.nil?
|
134
|
+
layout = layouts.find(layout.layout) or raise "Layout does not exist: #{layout.layout}"
|
129
135
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
if @sub_layout
|
134
|
-
layout = @sub_layout.content
|
136
|
+
raise "Layout cycle detected when rendering #{@pointer}: \n #{
|
137
|
+
(page_layouts<<layout).map{|l| l.pointer["realpath"]}.join("\n")
|
138
|
+
}" if page_layouts.include?(layout)
|
135
139
|
|
136
|
-
|
137
|
-
# into the master_layout using mustache.
|
138
|
-
if @master_layout && @master_layout.content
|
139
|
-
layout = render(@master_layout.content, {"content" => layout})
|
140
|
+
page_layouts << layout
|
140
141
|
end
|
141
|
-
|
142
|
-
# Minimum layout if no layout defined.
|
143
|
-
layout = page ? '{{{ page.content }}}' : '{{{ content }}}'
|
142
|
+
page_layouts
|
144
143
|
end
|
145
|
-
|
146
|
-
layout
|
147
144
|
end
|
148
145
|
|
149
146
|
private
|
@@ -184,4 +181,4 @@ module Ruhoh::Views
|
|
184
181
|
}.compact
|
185
182
|
end
|
186
183
|
end
|
187
|
-
end
|
184
|
+
end
|
@@ -21,10 +21,9 @@ module Ruhoh::SprocketsPlugin
|
|
21
21
|
env.append_path(path)
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
FileUtils.mkdir_p compiled_path
|
24
|
+
compile_collection_path
|
26
25
|
|
27
|
-
manifest = Sprockets::Manifest.new(env, compiled_path)
|
26
|
+
manifest = Sprockets::Manifest.new(env, @collection.compiled_path)
|
28
27
|
assets = collection.files.values.map{ |p|
|
29
28
|
Ruhoh::Friend.say { green " > #{p['id']}" }
|
30
29
|
p["id"]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruhoh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '2.
|
4
|
+
version: '2.4'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-09-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -151,6 +151,7 @@ files:
|
|
151
151
|
- Rakefile
|
152
152
|
- bin/ruhoh
|
153
153
|
- cucumber.yml
|
154
|
+
- features/base_path.feature
|
154
155
|
- features/categories.feature
|
155
156
|
- features/config.feature
|
156
157
|
- features/conversion.feature
|
@@ -158,6 +159,7 @@ files:
|
|
158
159
|
- features/drafts.feature
|
159
160
|
- features/ignore.feature
|
160
161
|
- features/javascripts.feature
|
162
|
+
- features/json.feature
|
161
163
|
- features/layouts.feature
|
162
164
|
- features/pagination.feature
|
163
165
|
- features/partials.feature
|
@@ -188,6 +190,7 @@ files:
|
|
188
190
|
- lib/ruhoh/converters/markdown.rb
|
189
191
|
- lib/ruhoh/friend.rb
|
190
192
|
- lib/ruhoh/logger.rb
|
193
|
+
- lib/ruhoh/parse.rb
|
191
194
|
- lib/ruhoh/programs/compile.rb
|
192
195
|
- lib/ruhoh/programs/preview.rb
|
193
196
|
- lib/ruhoh/programs/watch.rb
|
@@ -274,18 +277,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
274
277
|
- - ! '>='
|
275
278
|
- !ruby/object:Gem::Version
|
276
279
|
version: '0'
|
277
|
-
segments:
|
278
|
-
- 0
|
279
|
-
hash: 897888631782695949
|
280
280
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
281
281
|
none: false
|
282
282
|
requirements:
|
283
283
|
- - ! '>='
|
284
284
|
- !ruby/object:Gem::Version
|
285
285
|
version: '0'
|
286
|
-
segments:
|
287
|
-
- 0
|
288
|
-
hash: 897888631782695949
|
289
286
|
requirements: []
|
290
287
|
rubyforge_project:
|
291
288
|
rubygems_version: 1.8.24
|