ruhoh 2.2 → 2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/README.md +1 -0
- data/features/config.feature +22 -0
- data/features/ignore.feature +14 -0
- data/features/permalinks.feature +23 -0
- data/features/static.feature +19 -0
- data/features/step_defs.rb +9 -0
- data/features/summary.feature +24 -0
- data/history.json +21 -2
- data/lib/ruhoh/base/compiler.rb +1 -0
- data/lib/ruhoh/base/model.rb +10 -71
- data/lib/ruhoh/base/model_view.rb +11 -62
- data/lib/ruhoh/client.rb +1 -0
- data/lib/ruhoh/collections.rb +1 -3
- data/lib/ruhoh/programs/preview.rb +2 -2
- data/lib/ruhoh/programs/watch.rb +17 -10
- data/lib/ruhoh/resources/ignore/collection.rb +5 -0
- data/lib/ruhoh/resources/pages/client.rb +27 -17
- data/lib/ruhoh/resources/pages/compiler.rb +2 -1
- data/lib/ruhoh/resources/static/collection.rb +9 -0
- data/lib/ruhoh/resources/static/compiler.rb +33 -0
- data/lib/ruhoh/string_format.rb +51 -0
- data/lib/ruhoh/summarizer.rb +65 -0
- data/lib/ruhoh/url_slug.rb +126 -0
- data/lib/ruhoh/utils.rb +2 -25
- data/lib/ruhoh/version.rb +1 -1
- data/lib/ruhoh/views/master_view.rb +6 -2
- data/lib/ruhoh.rb +6 -2
- data/system/_scaffold.html +2 -2
- data/system/plugins/sprockets/stylesheets/compiler.rb +1 -0
- data/system/plugins/sprockets/stylesheets/previewer.rb +1 -0
- metadata +13 -4
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
Feature: Config
|
2
|
+
As a content publisher
|
3
|
+
I want to configure the way my site works
|
4
|
+
so I can publish content in a way that makes me happy.
|
5
|
+
|
6
|
+
Scenario: Setting a production_url
|
7
|
+
Given some files with values:
|
8
|
+
| file | body |
|
9
|
+
| config.yml | production_url: 'http://hello-world.com' |
|
10
|
+
| _root/index.html | <span>{{ urls.production }}</span> |
|
11
|
+
When I compile my site
|
12
|
+
Then my compiled site should have the file "index.html"
|
13
|
+
And this file should contain the content node "span|http://hello-world.com"
|
14
|
+
|
15
|
+
Scenario: Setting a production_url and using legacy urls.production_url
|
16
|
+
Given some files with values:
|
17
|
+
| file | body |
|
18
|
+
| config.yml | production_url: 'http://hello-world.com' |
|
19
|
+
| _root/index.html | <span>{{ urls.production_url }}</span> |
|
20
|
+
When I compile my site
|
21
|
+
Then my compiled site should have the file "index.html"
|
22
|
+
And this file should contain the content node "span|http://hello-world.com"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: Ignored resources
|
2
|
+
As a content publisher
|
3
|
+
I want to ignore certain folders
|
4
|
+
so that I can manage non-website related resources freely without screwing up my website.
|
5
|
+
|
6
|
+
Scenario: Defining an ignored resource (directory)
|
7
|
+
Given a config file with values:
|
8
|
+
| deploy | { "use" : "ignore" } |
|
9
|
+
Given some files with values:
|
10
|
+
| file |
|
11
|
+
| deploy/hello.md |
|
12
|
+
When I compile my site
|
13
|
+
Then my compiled site should NOT have the file "deploy/hello/index.html"
|
14
|
+
Then my compiled site should NOT have the folder "deploy"
|
data/features/permalinks.feature
CHANGED
@@ -54,6 +54,29 @@ Feature: Page Permalinks
|
|
54
54
|
When I compile my site
|
55
55
|
Then my compiled site should have the file "essays/2012/1/2/hello/index.html"
|
56
56
|
|
57
|
+
Scenario: Custom permalink format in page metadata using explicit html extension
|
58
|
+
Given some files with values:
|
59
|
+
| file | permalink |
|
60
|
+
| essays/hello.md | :filename.html |
|
61
|
+
When I compile my site
|
62
|
+
Then my compiled site should have the file "hello.html"
|
63
|
+
|
64
|
+
Scenario: Custom permalink format in page metadata using explicit convertable extension
|
65
|
+
Given some files with values:
|
66
|
+
| file | permalink |
|
67
|
+
| essays/hello.md | :filename.md |
|
68
|
+
When I compile my site
|
69
|
+
Then my compiled site should have the file "hello.md"
|
70
|
+
|
71
|
+
Scenario: Custom permalink format in page metadata using explicit arbitrary extension
|
72
|
+
Given some files with values:
|
73
|
+
| file | permalink |
|
74
|
+
| essays/hello.md | :filename.derp |
|
75
|
+
When I compile my site
|
76
|
+
Then my compiled site should have the file "hello.derp"
|
77
|
+
|
78
|
+
## Literal permalink
|
79
|
+
|
57
80
|
Scenario: Literal permalink format in page metadata.
|
58
81
|
Given some files with values:
|
59
82
|
| file | permalink | body |
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Feature: Static resources
|
2
|
+
As a content publisher
|
3
|
+
I want to include static folders
|
4
|
+
so that I can statically transfer files over to my website output.
|
5
|
+
|
6
|
+
Scenario: Defining an ignored resource (directory)
|
7
|
+
Given a config file with values:
|
8
|
+
| recipes | { "use" : "static" } |
|
9
|
+
Given some files with values:
|
10
|
+
| file |
|
11
|
+
| recipes/hello.md |
|
12
|
+
| recipes/hi.txt |
|
13
|
+
| recipes/yo.html |
|
14
|
+
| recipes/cool/data.json |
|
15
|
+
When I compile my site
|
16
|
+
Then my compiled site should have the file "recipes/hello.md"
|
17
|
+
Then my compiled site should have the file "recipes/hi.txt"
|
18
|
+
Then my compiled site should have the file "recipes/yo.html"
|
19
|
+
Then my compiled site should have the file "recipes/cool/data.json"
|
data/features/step_defs.rb
CHANGED
@@ -34,6 +34,15 @@ Then(/^my compiled site (should|should NOT) have the file "(.*?)"$/) do |matcher
|
|
34
34
|
}
|
35
35
|
end
|
36
36
|
|
37
|
+
Then(/^my compiled site (should|should NOT) have the (?:directory|folder) "(.*?)"$/) do |matcher, path|
|
38
|
+
@filepath = path
|
39
|
+
FileUtils.cd(@ruhoh.paths.compiled) {
|
40
|
+
# Done this way so the error output is more informative.
|
41
|
+
files = Dir.glob("**/*").delete_if{ |a| File.file?(a) }
|
42
|
+
files.__send__(matcher, include(path))
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
37
46
|
Then(/^this file (should|should NOT) (?:have|contain) the content "(.*?)"$/) do |matcher, content|
|
38
47
|
this_compiled_file.__send__(matcher, have_content(content))
|
39
48
|
end
|
data/features/summary.feature
CHANGED
@@ -28,6 +28,30 @@ Feature: Summary
|
|
28
28
|
And this file should contain the content node "div.summary|The Giraffe and the Zebra and the Eland and the Koodoo and the Hartebeest lived there: and they were 'sclusively sandy-yellow-brownish all over; but the Leopard, he was the 'sclusivest sandiest-yellowest-brownest of them all -- a greyish-yellowish catty-shaped kind of beast, and he matched the 'sclusively yellowish-greyish-brownish colour of the High Veldt to one hair."
|
29
29
|
And this file should NOT contain the content "This was very bad for the Giraffe and the Zebra and the rest of them: for he would lie down by a 'sclusively yellowish-greyish-brownish stone or clump of grass, and when the Giraffe or the Zebra or the Eland or the Koodoo or the Bush-Buck or the Bonte-Buck came by he would surprise them out of their jumpsome lives."
|
30
30
|
|
31
|
+
Scenario: Summary with configured summary_lines in page's meta-data
|
32
|
+
Given some files with values:
|
33
|
+
| file | body |
|
34
|
+
| layouts/essays.md | {{{ page.summary }}} |
|
35
|
+
And the file "essays/hello.md" with body:
|
36
|
+
"""
|
37
|
+
---
|
38
|
+
summary_lines: 2
|
39
|
+
---
|
40
|
+
In the days when everybody started fair, Best Beloved, the Leopard lived in a place called the High Veldt. 'Member it wasn't the Low Veldt, or the Bush Veldt, or the Sour Veldt, but the 'sclusively bare, hot shiny High Veldt, where there was sand and sandy-coloured rock and 'sclusively tufts of sandy-yellowish grass.
|
41
|
+
|
42
|
+
The Giraffe and the Zebra and the Eland and the Koodoo and the Hartebeest lived there: and they were 'sclusively sandy-yellow-brownish all over; but the Leopard, he was the 'sclusivest sandiest-yellowest-brownest of them all -- a greyish-yellowish catty-shaped kind of beast, and he matched the 'sclusively yellowish-greyish-brownish colour of the High Veldt to one hair.
|
43
|
+
|
44
|
+
This was very bad for the Giraffe and the Zebra and the rest of them: for he would lie down by a 'sclusively yellowish-greyish-brownish stone or clump of grass, and when the Giraffe or the Zebra or the Eland or the Koodoo or the Bush-Buck or the Bonte-Buck came by he would surprise them out of their jumpsome lives.
|
45
|
+
|
46
|
+
He would indeed! And, also, there was an Ethiopian with bows and arrows (a 'sclusively greyish-brownish-yellowish man he was then), who lived on the High Veldt with the Leopard: and the two used to hunt together -- the Ethiopian with his bows and arrows, and the Leopard 'sclusively with his teeth and claws -- till the Giraffe and the Eland and the Koodoo and the Quagga and all the rest of them didn't know which way to jump, Best Beloved.
|
47
|
+
They didn't indeed!
|
48
|
+
"""
|
49
|
+
When I compile my site
|
50
|
+
Then my compiled site should have the file "essays/hello/index.html"
|
51
|
+
And this file should contain the content node "div.summary|In the days when everybody started fair, Best Beloved, the Leopard lived in a place called the High Veldt. 'Member it wasn't the Low Veldt, or the Bush Veldt, or the Sour Veldt, but the 'sclusively bare, hot shiny High Veldt, where there was sand and sandy-coloured rock and 'sclusively tufts of sandy-yellowish grass."
|
52
|
+
And this file should contain the content node "div.summary|The Giraffe and the Zebra and the Eland and the Koodoo and the Hartebeest lived there: and they were 'sclusively sandy-yellow-brownish all over; but the Leopard, he was the 'sclusivest sandiest-yellowest-brownest of them all -- a greyish-yellowish catty-shaped kind of beast, and he matched the 'sclusively yellowish-greyish-brownish colour of the High Veldt to one hair."
|
53
|
+
And this file should NOT contain the content "This was very bad for the Giraffe and the Zebra and the rest of them: for he would lie down by a 'sclusively yellowish-greyish-brownish stone or clump of grass, and when the Giraffe or the Zebra or the Eland or the Koodoo or the Bush-Buck or the Bonte-Buck came by he would surprise them out of their jumpsome lives."
|
54
|
+
|
31
55
|
Scenario: Specifying an explicit summary DOM node
|
32
56
|
Given some files with values:
|
33
57
|
| file | body |
|
data/history.json
CHANGED
@@ -1,10 +1,29 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"version" : "2.3",
|
4
|
+
"date" : "25.08.2013",
|
5
|
+
"changes" : [
|
6
|
+
"(Internal) Ruhoh::CleanUrl and Ruhoh::StringFormat better encapsulate url-slug generation.",
|
7
|
+
"(Internal) page.summary is now single-purpose Ruhoh::Summarizer service."
|
8
|
+
],
|
9
|
+
"features" : [
|
10
|
+
"Add ability to use: 'ignore' to ignore a collection folder.",
|
11
|
+
"Add ability to use: 'static' to serve a collection folder staticly.",
|
12
|
+
"Add title into base scaffold if defined from CLI."
|
13
|
+
],
|
14
|
+
"bugs" : [
|
15
|
+
"Fix plugins not running on the CLI.",
|
16
|
+
"Fix permalinks not persisting explicit extentions, e.g. .html",
|
17
|
+
"Expose urls.production and urls.production_url in the view."
|
18
|
+
]
|
19
|
+
}
|
20
|
+
,
|
2
21
|
{
|
3
22
|
"version" : "2.2",
|
4
23
|
"date" : "09.06.2013",
|
5
24
|
"changes" : [
|
6
25
|
"@binaryphile updates google analytics tracking script",
|
7
|
-
"@stebalien overhauls page.summary internal implementation"
|
26
|
+
"@stebalien overhauls page.summary internal implementation"
|
8
27
|
],
|
9
28
|
"features" : [
|
10
29
|
"Cache the collection#all call to improve performance.",
|
@@ -19,7 +38,7 @@
|
|
19
38
|
"@caspervonb makes widgets compiler respect model excludes",
|
20
39
|
"Avoid data parse errors by always converting to string",
|
21
40
|
"Improve error messages for a few parse error cases",
|
22
|
-
"Fix widget config not deep merging with default widget config"
|
41
|
+
"Fix widget config not deep merging with default widget config"
|
23
42
|
]
|
24
43
|
},
|
25
44
|
{
|
data/lib/ruhoh/base/compiler.rb
CHANGED
@@ -15,6 +15,7 @@ module Ruhoh::Base
|
|
15
15
|
include Compilable
|
16
16
|
|
17
17
|
# A basic compiler task which copies each valid collection resource file to the compiled folder.
|
18
|
+
# This is different from the static compiler in that it supports fingerprinting.
|
18
19
|
# Valid files are identified by their pointers.
|
19
20
|
# Invalid files are files that are excluded from the resource's configuration settings.
|
20
21
|
# The collection's url_endpoint is used to determine the final compiled path.
|
data/lib/ruhoh/base/model.rb
CHANGED
@@ -39,7 +39,7 @@ module Ruhoh::Base
|
|
39
39
|
|
40
40
|
def try(method)
|
41
41
|
return __send__(method) if respond_to?(method)
|
42
|
-
return data[method] if data.key?(method.to_s)
|
42
|
+
return data[method.to_s] if data.key?(method.to_s)
|
43
43
|
false
|
44
44
|
end
|
45
45
|
end
|
@@ -68,7 +68,7 @@ module Ruhoh::Base
|
|
68
68
|
|
69
69
|
data['title'] = data['title'] || filename_data['title']
|
70
70
|
data['date'] ||= filename_data['date'].to_s
|
71
|
-
data['url'] =
|
71
|
+
data['url'] = url(data)
|
72
72
|
data['layout'] = collection.config['layout'] if data['layout'].nil?
|
73
73
|
|
74
74
|
parsed_page['data'] = data
|
@@ -157,80 +157,19 @@ module Ruhoh::Base
|
|
157
157
|
file_slug = @pointer['id'].split('/')[-2]
|
158
158
|
end
|
159
159
|
|
160
|
-
|
160
|
+
Ruhoh::StringFormat.titleize(file_slug)
|
161
161
|
end
|
162
162
|
|
163
|
-
|
164
|
-
|
165
|
-
def permalink(page_data)
|
166
|
-
format = page_data['permalink'] || collection.config['permalink']
|
167
|
-
format ||= "/:path/:filename"
|
168
|
-
|
169
|
-
url = if format.include?(':')
|
170
|
-
title = Ruhoh::Utils.to_url_slug(page_data['title'])
|
171
|
-
filename = File.basename(page_data['id'])
|
172
|
-
category = Array(page_data['categories'])[0]
|
173
|
-
category = category.split('/').map {|c| Ruhoh::Utils.to_url_slug(c) }.join('/') if category
|
174
|
-
relative_path = File.dirname(page_data['id'])
|
175
|
-
relative_path = "" if relative_path == "."
|
176
|
-
data = {
|
177
|
-
"title" => title,
|
178
|
-
"filename" => filename,
|
179
|
-
"path" => File.join(@pointer["resource"], relative_path),
|
180
|
-
"relative_path" => relative_path,
|
181
|
-
"categories" => category || '',
|
182
|
-
}
|
183
|
-
|
184
|
-
uses_date = false
|
185
|
-
%w{ :year :month :day :i_day :i_month }.each do |token|
|
186
|
-
if format.include?(token)
|
187
|
-
uses_date = true
|
188
|
-
break
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
if uses_date
|
193
|
-
begin
|
194
|
-
date = Time.parse(page_data['date'].to_s)
|
195
|
-
rescue ArgumentError, TypeError
|
196
|
-
Ruhoh.log.error(
|
197
|
-
"ArgumentError:" +
|
198
|
-
" The file '#{ @pointer["realpath"] }' has a permalink '#{ format }'" +
|
199
|
-
" which is date dependant but the date '#{page_data['date']}' could not be parsed." +
|
200
|
-
" Ensure the date's format is: 'YYYY-MM-DD'"
|
201
|
-
)
|
202
|
-
end
|
203
|
-
|
204
|
-
data.merge!({
|
205
|
-
"year" => date.strftime("%Y"),
|
206
|
-
"month" => date.strftime("%m"),
|
207
|
-
"day" => date.strftime("%d"),
|
208
|
-
"i_day" => date.strftime("%d").to_i.to_s,
|
209
|
-
"i_month" => date.strftime("%m").to_i.to_s,
|
210
|
-
})
|
211
|
-
end
|
212
|
-
|
213
|
-
data.inject(format) { |result, token|
|
214
|
-
result.gsub(/:#{Regexp.escape token.first}/, token.last)
|
215
|
-
}.gsub(/\/+/, "/")
|
216
|
-
else
|
217
|
-
# Use the literal permalink if it is a non-tokenized string.
|
218
|
-
format.gsub(/^\//, '').split('/').map {|p| CGI::escape(p) }.join('/')
|
219
|
-
end
|
163
|
+
def url(page_data)
|
164
|
+
page_data['permalink_ext'] ||= collection.config['permalink_ext']
|
220
165
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
url = url.gsub(%r{#{File.extname(url)}$}, '.html')
|
225
|
-
end
|
226
|
-
|
227
|
-
unless (page_data['permalink_ext'] || collection.config['permalink_ext'])
|
228
|
-
url = url.gsub(/index.html$/, '').gsub(/\.html$/, '')
|
229
|
-
end
|
166
|
+
format = page_data['permalink'] ||
|
167
|
+
collection.config['permalink'] ||
|
168
|
+
"/:path/:filename"
|
230
169
|
|
231
|
-
|
170
|
+
slug = Ruhoh::UrlSlug.new(page_data: page_data, format: format)
|
232
171
|
|
233
|
-
@ruhoh.to_url(
|
172
|
+
@ruhoh.to_url(slug.generate)
|
234
173
|
end
|
235
174
|
end
|
236
175
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'nokogiri'
|
2
1
|
require 'set'
|
2
|
+
require 'ruhoh/summarizer'
|
3
3
|
|
4
4
|
module Ruhoh::Base
|
5
5
|
module ModelViewable
|
@@ -89,72 +89,21 @@ module Ruhoh::Base
|
|
89
89
|
def is_active_page
|
90
90
|
id == @model.collection.master.page_data['id']
|
91
91
|
end
|
92
|
-
|
93
|
-
# Generate a truncated summary.
|
94
|
-
# - If a summary element (`<tag class="summary">...</tag>`) is specified
|
95
|
-
# in the content, return it.
|
96
|
-
# - If summary_lines > 0, truncate after the first complete element where
|
97
|
-
# the number of summary lines is greater than summary_lines.
|
98
|
-
# - If summary_stop_at_header is a number n, stop before the nth header.
|
99
|
-
# - If summary_stop_at_header is true, stop before the first header after
|
100
|
-
# content has been included. In other words, don't count headers at the
|
101
|
-
# top of the page.
|
102
|
-
def summary
|
103
|
-
# Parse the document
|
104
|
-
full_content = @ruhoh.master_view(@model.pointer).render_content
|
105
|
-
content_doc = Nokogiri::HTML.fragment(full_content)
|
106
|
-
|
107
|
-
# Return a summary element if specified
|
108
|
-
summary_el = content_doc.at_css('.summary')
|
109
|
-
return summary_el.to_html unless summary_el.nil?
|
110
92
|
|
111
|
-
|
112
|
-
# Default to the parameters provided in the page itself
|
93
|
+
def summary
|
113
94
|
model_data = @model.data
|
114
95
|
collection_config = @model.collection.config
|
115
|
-
line_limit = model_data['summary_lines'] || collection_config['summary_lines']
|
116
|
-
stop_at_header = model_data['summary_stop_at_header']
|
117
|
-
stop_at_header = collection_config['summary_stop_at_header'] if stop_at_header.nil?
|
118
|
-
|
119
|
-
# Create the summary element.
|
120
|
-
summary_doc = Nokogiri::XML::Node.new("div", Nokogiri::HTML::Document.new)
|
121
|
-
summary_doc["class"] = "summary"
|
122
96
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
content_doc.children.each do |node|
|
128
|
-
|
129
|
-
if stop_at_header == true
|
130
|
-
# Detect first header after content
|
131
|
-
if not (headings.include?(node.name) && node.content.empty?)
|
132
|
-
stop_at_header = 1
|
133
|
-
end
|
134
|
-
elsif stop_at_header.is_a?(Integer) && headings.include?(node.name)
|
135
|
-
if stop_at_header > 1
|
136
|
-
stop_at_header -= 1;
|
137
|
-
else
|
138
|
-
summary_doc["class"] += " ellipsis"
|
139
|
-
break
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
if line_limit > 0 && summary_doc.content.lines.to_a.length > line_limit
|
144
|
-
# Skip through leftover whitespace. Without this check, the summary
|
145
|
-
# can be marked as ellipsis even if it isn't.
|
146
|
-
unless node.text? && node.text.strip.empty?
|
147
|
-
summary_doc["class"] += " ellipsis"
|
148
|
-
break
|
149
|
-
else
|
150
|
-
next
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
summary_doc << node
|
155
|
-
end
|
97
|
+
line_limit = model_data['summary_lines'] ||
|
98
|
+
collection_config['summary_lines']
|
99
|
+
stop_at_header = model_data['summary_stop_at_header'] ||
|
100
|
+
collection_config['summary_stop_at_header']
|
156
101
|
|
157
|
-
|
102
|
+
Ruhoh::Summarizer.new({
|
103
|
+
content: @ruhoh.master_view(@model.pointer).render_content,
|
104
|
+
line_limit: line_limit,
|
105
|
+
stop_at_header: stop_at_header
|
106
|
+
}).generate
|
158
107
|
end
|
159
108
|
|
160
109
|
def next
|
data/lib/ruhoh/client.rb
CHANGED
data/lib/ruhoh/collections.rb
CHANGED
@@ -113,8 +113,6 @@ class Ruhoh
|
|
113
113
|
|
114
114
|
def url_endpoints
|
115
115
|
urls = {}
|
116
|
-
urls["base_path"] = @ruhoh.base_path
|
117
|
-
|
118
116
|
all.each do |name|
|
119
117
|
collection = load(name)
|
120
118
|
next unless collection.respond_to?(:url_endpoint)
|
@@ -169,4 +167,4 @@ class Ruhoh
|
|
169
167
|
name.to_s.split('_').map {|a| a.capitalize}.join
|
170
168
|
end
|
171
169
|
end
|
172
|
-
end
|
170
|
+
end
|
@@ -36,8 +36,8 @@ class Ruhoh
|
|
36
36
|
# since presumably they define customized permalinks per singular resource.
|
37
37
|
# Page-like resources are handled the root mapping below.
|
38
38
|
ruhoh.collections.url_endpoints_sorted.each do |h|
|
39
|
-
# Omit
|
40
|
-
next if %w{
|
39
|
+
# Omit theme because they are special use-cases.
|
40
|
+
next if %w{ theme }.include?(h["name"])
|
41
41
|
map h["url"] do
|
42
42
|
collection = ruhoh.collection(h["name"])
|
43
43
|
if collection.previewer?
|
data/lib/ruhoh/programs/watch.rb
CHANGED
@@ -27,15 +27,22 @@ class Ruhoh
|
|
27
27
|
yellow "Watch [#{Time.now.strftime("%H:%M:%S")}] [Update #{path}] : #{args.size} files changed"
|
28
28
|
}
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
30
|
+
if path == "config.yml"
|
31
|
+
ruhoh.config true
|
32
|
+
else
|
33
|
+
separator = File::ALT_SEPARATOR ?
|
34
|
+
%r{#{ File::SEPARATOR }|#{ File::ALT_SEPARATOR }} :
|
35
|
+
File::SEPARATOR
|
36
|
+
resource = path.split(separator)[0]
|
37
|
+
|
38
|
+
ruhoh.cache.delete(ruhoh.collection(resource).files_cache_key)
|
39
|
+
ruhoh.cache.delete("#{ resource }-all")
|
40
|
+
|
41
|
+
puts("HERE", resource)
|
42
|
+
|
43
|
+
ruhoh.collection(resource).load_watcher.update(path)
|
44
|
+
puts(ruhoh.collection(resource))
|
45
|
+
end
|
39
46
|
end
|
40
47
|
end
|
41
48
|
|
@@ -43,4 +50,4 @@ class Ruhoh
|
|
43
50
|
end
|
44
51
|
|
45
52
|
end
|
46
|
-
end
|
53
|
+
end
|
@@ -43,7 +43,7 @@ module Ruhoh::Resources::Pages
|
|
43
43
|
def titleize
|
44
44
|
@collection.dictionary.each do |id, data|
|
45
45
|
next unless File.basename(data['id']) =~ /^untitled/
|
46
|
-
new_name = Ruhoh::
|
46
|
+
new_name = Ruhoh::StringFormat.clean_slug(data['title'])
|
47
47
|
new_file = "#{new_name}#{File.extname(data['id'])}"
|
48
48
|
old_file = File.basename(data['id'])
|
49
49
|
next if old_file == new_file
|
@@ -63,13 +63,35 @@ module Ruhoh::Resources::Pages
|
|
63
63
|
_list(@collection.all)
|
64
64
|
end
|
65
65
|
|
66
|
+
# Public - renders the scaffold, if available, for this resource.
|
67
|
+
def render_scaffold
|
68
|
+
(@collection.scaffold || '')
|
69
|
+
.gsub('{{DATE}}', Time.now.strftime('%Y-%m-%d'))
|
70
|
+
.gsub('{{TITLE}}', @args[2])
|
71
|
+
end
|
72
|
+
|
66
73
|
protected
|
67
74
|
|
68
75
|
def create(opts={})
|
76
|
+
filename = ensure_unique_filename(@args[2], opts)
|
77
|
+
FileUtils.mkdir_p File.dirname(filename)
|
78
|
+
|
79
|
+
File.open(filename, 'w:UTF-8') { |f| f.puts render_scaffold }
|
80
|
+
|
69
81
|
ruhoh = @ruhoh
|
82
|
+
resource_name = @collection.resource_name
|
83
|
+
Ruhoh::Friend.say {
|
84
|
+
green "New #{resource_name}:"
|
85
|
+
green " > #{ruhoh.relative_path(filename)}"
|
86
|
+
if opts[:draft]
|
87
|
+
plain "View drafts at the URL: /dash"
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
70
91
|
|
92
|
+
def ensure_unique_filename(original_filename, opts)
|
71
93
|
begin
|
72
|
-
file =
|
94
|
+
file = original_filename || "untitled"
|
73
95
|
ext = File.extname(file).to_s
|
74
96
|
ext = ext.empty? ? @collection.config["ext"] : ext
|
75
97
|
|
@@ -78,7 +100,7 @@ module Ruhoh::Resources::Pages
|
|
78
100
|
name = File.basename(file, ext).gsub(/\s+/, '-')
|
79
101
|
File.join(File.dirname(file), name)
|
80
102
|
else
|
81
|
-
Ruhoh::
|
103
|
+
Ruhoh::StringFormat.clean_slug(File.basename(file, ext))
|
82
104
|
end
|
83
105
|
|
84
106
|
name = "#{name}-#{@iterator}" unless @iterator.zero?
|
@@ -88,19 +110,7 @@ module Ruhoh::Resources::Pages
|
|
88
110
|
@iterator += 1
|
89
111
|
end while File.exist?(filename)
|
90
112
|
|
91
|
-
|
92
|
-
output = (@collection.scaffold || '').gsub('{{DATE}}', Time.now.strftime('%Y-%m-%d'))
|
93
|
-
|
94
|
-
File.open(filename, 'w:UTF-8') {|f| f.puts output }
|
95
|
-
|
96
|
-
resource_name = @collection.resource_name
|
97
|
-
Ruhoh::Friend.say {
|
98
|
-
green "New #{resource_name}:"
|
99
|
-
green " > #{ruhoh.relative_path(filename)}"
|
100
|
-
if opts[:draft]
|
101
|
-
plain "View drafts at the URL: /dash"
|
102
|
-
end
|
103
|
-
}
|
113
|
+
filename
|
104
114
|
end
|
105
115
|
|
106
116
|
def _list(data)
|
@@ -121,4 +131,4 @@ module Ruhoh::Resources::Pages
|
|
121
131
|
end
|
122
132
|
end
|
123
133
|
end
|
124
|
-
end
|
134
|
+
end
|
@@ -72,6 +72,7 @@ module Ruhoh::Resources::Pages
|
|
72
72
|
xml.rss(:version => '2.0') {
|
73
73
|
xml.channel {
|
74
74
|
xml.title_ data['title']
|
75
|
+
xml.description_ (data['description'] ? data['description'] : data['title'])
|
75
76
|
xml.link_ @ruhoh.config['production_url']
|
76
77
|
xml.pubDate_ Time.now
|
77
78
|
pages.each do |page|
|
@@ -98,4 +99,4 @@ module Ruhoh::Resources::Pages
|
|
98
99
|
}
|
99
100
|
end
|
100
101
|
end
|
101
|
-
end
|
102
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Ruhoh::Resources::Static
|
2
|
+
class Compiler
|
3
|
+
include Ruhoh::Base::Compilable
|
4
|
+
|
5
|
+
# A basic compiler task which copies each valid collection resource file to the compiled folder.
|
6
|
+
# Valid files are identified by their pointers.
|
7
|
+
# Invalid files are files that are excluded from the resource's configuration settings.
|
8
|
+
# The collection's url_endpoint is used to determine the final compiled path.
|
9
|
+
#
|
10
|
+
# @returns Nothing.
|
11
|
+
def run
|
12
|
+
collection = @collection
|
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
|
22
|
+
|
23
|
+
@collection.files.values.each do |pointer|
|
24
|
+
compiled_file = File.join(compiled_path, pointer['id'])
|
25
|
+
|
26
|
+
FileUtils.mkdir_p File.dirname(compiled_file)
|
27
|
+
FileUtils.cp_r pointer['realpath'], compiled_file
|
28
|
+
|
29
|
+
Ruhoh::Friend.say { green " > #{pointer['id']}" }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
# StringFormat is meant to expose the common (public) interface
|
3
|
+
# for where strings (namely URLS) are formatted.
|
4
|
+
# Users are encouraged to reimplement these methods via plugins to enable
|
5
|
+
# custom-defined slug generation logic based on their tastes.
|
6
|
+
#
|
7
|
+
# TODO:
|
8
|
+
# - Natively support the most popular slug formats.
|
9
|
+
# - Better support for Internationalization.
|
10
|
+
module StringFormat
|
11
|
+
|
12
|
+
# Public interface for building 'clean slugs'
|
13
|
+
# Redefine this method to implement custom slug generation.
|
14
|
+
def self.clean_slug(string)
|
15
|
+
hyphenate(string)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.clean_slug_and_escape(string)
|
19
|
+
CGI::escape(clean_slug(string))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Simple url slug normalization.
|
23
|
+
# Converts all non word characters into hyphens.
|
24
|
+
# This may not be what you want so feel free to overwite the public
|
25
|
+
# method in place of another formatter.
|
26
|
+
#
|
27
|
+
# Ex: My Post Title ===> my-post-title
|
28
|
+
def self.hyphenate(string)
|
29
|
+
string = string.to_s.downcase.strip.gsub(/[^\p{Word}+]/u, '-')
|
30
|
+
string.gsub(/^\-+/, '').gsub(/\-+$/, '').gsub(/\-+/, '-')
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO: Probably use ActiveSupport for this stuff
|
34
|
+
# Ex: my-post-title ===> My Post Title
|
35
|
+
def self.titleize(string)
|
36
|
+
string.gsub(/[^\p{Word}+]/u, ' ').gsub(/\b\w/){ $&.upcase }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Convert CamelCase to snake_case
|
40
|
+
# Thanks ActiveSupport: http://stackoverflow.com/a/1509939/101940
|
41
|
+
def self.snake_case(string)
|
42
|
+
string.
|
43
|
+
to_s.
|
44
|
+
gsub(/::/, '/').
|
45
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
46
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
47
|
+
tr("-", "_").
|
48
|
+
downcase
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
class Ruhoh
|
3
|
+
class Summarizer
|
4
|
+
SummaryNodeClassName = 'summary'
|
5
|
+
Headings = Nokogiri::HTML::ElementDescription::HEADING + %w{ header hgroup }
|
6
|
+
|
7
|
+
def initialize(opts)
|
8
|
+
@content = opts[:content] ; opts.delete(:content)
|
9
|
+
@opts = opts
|
10
|
+
end
|
11
|
+
|
12
|
+
# Generate a truncated summary.
|
13
|
+
# - If a summary element (`<tag class="summary">...</tag>`) is specified
|
14
|
+
# in the content, return it.
|
15
|
+
# - If summary_lines > 0, truncate after the first complete element where
|
16
|
+
# the number of summary lines is greater than summary_lines.
|
17
|
+
# - If @opts[:stop_at_header] is a number n, stop before the nth header.
|
18
|
+
# - If @opts[:stop_at_header] is true, stop before the first header after
|
19
|
+
# content has been included. In other words, don't count headers at the
|
20
|
+
# top of the page.
|
21
|
+
def generate
|
22
|
+
content_doc = Nokogiri::HTML.fragment(@content)
|
23
|
+
|
24
|
+
# Return a summary element if specified
|
25
|
+
summary_el = content_doc.at_css('.' + SummaryNodeClassName)
|
26
|
+
return summary_el.to_html if summary_el
|
27
|
+
|
28
|
+
# Create the summary element.
|
29
|
+
summary_doc = Nokogiri::XML::Node.new("div", Nokogiri::HTML::Document.new)
|
30
|
+
summary_doc["class"] = SummaryNodeClassName
|
31
|
+
|
32
|
+
content_doc.children.each do |node|
|
33
|
+
|
34
|
+
if @opts[:stop_at_header] == true
|
35
|
+
# Detect first header after content
|
36
|
+
if not (Headings.include?(node.name) && node.content.empty?)
|
37
|
+
@opts[:stop_at_header] = 1
|
38
|
+
end
|
39
|
+
elsif @opts[:stop_at_header].is_a?(Integer) && Headings.include?(node.name)
|
40
|
+
if @opts[:stop_at_header] > 1
|
41
|
+
@opts[:stop_at_header] -= 1;
|
42
|
+
else
|
43
|
+
summary_doc["class"] += " ellipsis"
|
44
|
+
break
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if @opts[:line_limit] > 0 && summary_doc.content.lines.to_a.length > @opts[:line_limit]
|
49
|
+
# Skip through leftover whitespace. Without this check, the summary
|
50
|
+
# can be marked as ellipsis even if it isn't.
|
51
|
+
unless node.text? && node.text.strip.empty?
|
52
|
+
summary_doc["class"] += " ellipsis"
|
53
|
+
break
|
54
|
+
else
|
55
|
+
next
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
summary_doc << node
|
60
|
+
end
|
61
|
+
|
62
|
+
summary_doc.to_html
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
class UrlSlug
|
3
|
+
def initialize(opts)
|
4
|
+
@page_data = opts[:page_data]
|
5
|
+
@format = opts[:format]
|
6
|
+
@pointer = @page_data["pointer"]
|
7
|
+
end
|
8
|
+
|
9
|
+
# @return[String] URL Slug based on the given data and format.
|
10
|
+
def generate
|
11
|
+
url = @format.include?(':') ? dynamic : literal
|
12
|
+
url = process_url_extension(url)
|
13
|
+
url.empty? ? '/' : url
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return[String] the literal URL without token substitution.
|
17
|
+
def literal
|
18
|
+
@format.gsub(/^\//, '').split('/').map {|p| CGI::escape(p) }.join('/')
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return[String] the dynamic URL with token substitution.
|
22
|
+
def dynamic
|
23
|
+
data.inject(@format) { |result, token|
|
24
|
+
result.gsub(/:#{ Regexp.escape(token.first) }/, token.last)
|
25
|
+
}.gsub(/\/+/, "/")
|
26
|
+
end
|
27
|
+
|
28
|
+
def data
|
29
|
+
result = uses_date? ? date_data : {}
|
30
|
+
result.merge({
|
31
|
+
"title" => title,
|
32
|
+
"filename" => filename,
|
33
|
+
"path" => path,
|
34
|
+
"relative_path" => relative_path,
|
35
|
+
"categories" => category,
|
36
|
+
})
|
37
|
+
end
|
38
|
+
|
39
|
+
def date_data
|
40
|
+
date = Time.parse(@page_data['date'].to_s)
|
41
|
+
|
42
|
+
{
|
43
|
+
"year" => date.strftime("%Y"),
|
44
|
+
"month" => date.strftime("%m"),
|
45
|
+
"day" => date.strftime("%d"),
|
46
|
+
"i_day" => date.strftime("%d").to_i.to_s,
|
47
|
+
"i_month" => date.strftime("%m").to_i.to_s,
|
48
|
+
}
|
49
|
+
rescue ArgumentError, TypeError
|
50
|
+
Ruhoh.log.error(
|
51
|
+
"ArgumentError:" +
|
52
|
+
" The file '#{ @pointer["realpath"] }' has a permalink '#{ @format }'" +
|
53
|
+
" which is date dependant but the date '#{ @page_data['date'] }' could not be parsed." +
|
54
|
+
" Ensure the date's format is: 'YYYY-MM-DD'"
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def title
|
59
|
+
Ruhoh::StringFormat.clean_slug_and_escape(@page_data['title'])
|
60
|
+
end
|
61
|
+
|
62
|
+
def filename
|
63
|
+
File.basename(@page_data['id'], ext)
|
64
|
+
end
|
65
|
+
|
66
|
+
def ext
|
67
|
+
File.extname(@page_data['id'])
|
68
|
+
end
|
69
|
+
|
70
|
+
# Category is only the first one if multiple categories exist.
|
71
|
+
def category
|
72
|
+
string = Array(@page_data['categories'])[0]
|
73
|
+
return '' if string.to_s.empty?
|
74
|
+
|
75
|
+
string.split('/').map { |c|
|
76
|
+
Ruhoh::StringFormat.clean_slug_and_escape(c)
|
77
|
+
}.join('/')
|
78
|
+
end
|
79
|
+
|
80
|
+
def relative_path
|
81
|
+
string = File.dirname(@page_data['id'])
|
82
|
+
(string == ".") ? "" : string
|
83
|
+
end
|
84
|
+
|
85
|
+
def path
|
86
|
+
File.join(@pointer["resource"], relative_path)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def uses_date?
|
92
|
+
result = false
|
93
|
+
%w{ :year :month :day :i_day :i_month }.each do |token|
|
94
|
+
if @format.include?(token)
|
95
|
+
result = true
|
96
|
+
break
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
# Is an extension explicitly defined?
|
104
|
+
def uses_extension?
|
105
|
+
@format =~ /\.[^\.]+$/
|
106
|
+
end
|
107
|
+
|
108
|
+
# The url extension depends on multiple factors:
|
109
|
+
# user-config : preserve any extension set by the user in the format.
|
110
|
+
# converters : Automatically change convertable extensions to .html
|
111
|
+
# Non-convertable file-extensions should 'pass-through'
|
112
|
+
# 'pretty' : Automatically prettify urls (omit .html) unless user disabled.
|
113
|
+
#
|
114
|
+
# @return[String]
|
115
|
+
def process_url_extension(url)
|
116
|
+
return url if uses_extension?
|
117
|
+
|
118
|
+
url += Ruhoh::Converter.extensions.include?(ext) ? '.html' : ext
|
119
|
+
|
120
|
+
# Prettify by default
|
121
|
+
@page_data['permalink_ext'] ?
|
122
|
+
url :
|
123
|
+
url.gsub(/index|index.html$/, '').gsub(/\.html$/, '')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/ruhoh/utils.rb
CHANGED
@@ -21,18 +21,7 @@ class Ruhoh
|
|
21
21
|
parts = parts.unshift(base) if base
|
22
22
|
File.__send__(:join, parts)
|
23
23
|
end
|
24
|
-
|
25
|
-
def self.to_url_slug(title)
|
26
|
-
CGI::escape self.to_slug(title)
|
27
|
-
end
|
28
|
-
|
29
|
-
# My Post Title ===> my-post-title
|
30
|
-
def self.to_slug(title)
|
31
|
-
title = title.to_s.downcase.strip.gsub(/[^\p{Word}+]/u, '-')
|
32
|
-
title.gsub(/^\-+/, '').gsub(/\-+$/, '').gsub(/\-+/, '-')
|
33
|
-
end
|
34
|
-
|
35
|
-
|
24
|
+
|
36
25
|
def self.report(name, collection, invalid)
|
37
26
|
output = "#{collection.count}/#{collection.count + invalid.count} #{name} processed."
|
38
27
|
if collection.empty? && invalid.empty?
|
@@ -73,17 +62,5 @@ class Ruhoh
|
|
73
62
|
|
74
63
|
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
75
64
|
end
|
76
|
-
|
77
|
-
# Thanks ActiveSupport: http://stackoverflow.com/a/1509939/101940
|
78
|
-
def self.underscore(string)
|
79
|
-
string.
|
80
|
-
to_s.
|
81
|
-
gsub(/::/, '/').
|
82
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
83
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
84
|
-
tr("-", "_").
|
85
|
-
downcase
|
86
|
-
end
|
87
|
-
|
88
65
|
end
|
89
|
-
end
|
66
|
+
end
|
data/lib/ruhoh/version.rb
CHANGED
@@ -45,7 +45,11 @@ module Ruhoh::Views
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def urls
|
48
|
-
@ruhoh.collections.url_endpoints
|
48
|
+
@ruhoh.collections.url_endpoints.merge({
|
49
|
+
'base_path' => @ruhoh.base_path,
|
50
|
+
'production' => @ruhoh.config["production_url"],
|
51
|
+
'production_url' => @ruhoh.config["production_url"]
|
52
|
+
})
|
49
53
|
end
|
50
54
|
|
51
55
|
def content
|
@@ -93,7 +97,7 @@ module Ruhoh::Views
|
|
93
97
|
# Handy for transforming ids into css-classes in your views.
|
94
98
|
# @returns[String]
|
95
99
|
def to_slug(sub_context)
|
96
|
-
Ruhoh::
|
100
|
+
Ruhoh::StringFormat.clean_slug(sub_context)
|
97
101
|
end
|
98
102
|
|
99
103
|
# Public: Formats the path to the compiled file based on the URL.
|
data/lib/ruhoh.rb
CHANGED
@@ -23,6 +23,8 @@ require 'ruhoh/views/master_view'
|
|
23
23
|
require 'ruhoh/collections'
|
24
24
|
require 'ruhoh/cache'
|
25
25
|
require 'ruhoh/routes'
|
26
|
+
require 'ruhoh/string_format'
|
27
|
+
require 'ruhoh/url_slug'
|
26
28
|
require 'ruhoh/programs/preview'
|
27
29
|
|
28
30
|
class Ruhoh
|
@@ -60,7 +62,9 @@ class Ruhoh
|
|
60
62
|
@collections.load(resource)
|
61
63
|
end
|
62
64
|
|
63
|
-
def config
|
65
|
+
def config(reload=false)
|
66
|
+
return @config unless (reload or @config.nil?)
|
67
|
+
|
64
68
|
config = Ruhoh::Utils.parse_yaml_file(@base, "config.yml") || {}
|
65
69
|
config['compiled'] = config['compiled'] ? File.expand_path(config['compiled']) : "compiled"
|
66
70
|
|
@@ -255,4 +259,4 @@ class Ruhoh
|
|
255
259
|
def self.model(resource)
|
256
260
|
Collections.get_module_namespace_for(resource).const_get(:ModelView)
|
257
261
|
end
|
258
|
-
end
|
262
|
+
end
|
data/system/_scaffold.html
CHANGED
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.3'
|
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-08-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -152,14 +152,17 @@ files:
|
|
152
152
|
- bin/ruhoh
|
153
153
|
- cucumber.yml
|
154
154
|
- features/categories.feature
|
155
|
+
- features/config.feature
|
155
156
|
- features/conversion.feature
|
156
157
|
- features/data.feature
|
157
158
|
- features/drafts.feature
|
159
|
+
- features/ignore.feature
|
158
160
|
- features/javascripts.feature
|
159
161
|
- features/layouts.feature
|
160
162
|
- features/pagination.feature
|
161
163
|
- features/partials.feature
|
162
164
|
- features/permalinks.feature
|
165
|
+
- features/static.feature
|
163
166
|
- features/step_defs.rb
|
164
167
|
- features/stylesheets.feature
|
165
168
|
- features/summary.feature
|
@@ -194,6 +197,7 @@ files:
|
|
194
197
|
- lib/ruhoh/resources/dash/previewer.rb
|
195
198
|
- lib/ruhoh/resources/data/collection.rb
|
196
199
|
- lib/ruhoh/resources/data/collection_view.rb
|
200
|
+
- lib/ruhoh/resources/ignore/collection.rb
|
197
201
|
- lib/ruhoh/resources/javascripts/collection.rb
|
198
202
|
- lib/ruhoh/resources/javascripts/collection_view.rb
|
199
203
|
- lib/ruhoh/resources/javascripts/compiler.rb
|
@@ -209,6 +213,8 @@ files:
|
|
209
213
|
- lib/ruhoh/resources/pages/model_view.rb
|
210
214
|
- lib/ruhoh/resources/pages/previewer.rb
|
211
215
|
- lib/ruhoh/resources/partials/model.rb
|
216
|
+
- lib/ruhoh/resources/static/collection.rb
|
217
|
+
- lib/ruhoh/resources/static/compiler.rb
|
212
218
|
- lib/ruhoh/resources/stylesheets/collection.rb
|
213
219
|
- lib/ruhoh/resources/stylesheets/collection_view.rb
|
214
220
|
- lib/ruhoh/resources/stylesheets/compiler.rb
|
@@ -219,6 +225,9 @@ files:
|
|
219
225
|
- lib/ruhoh/resources/widgets/compiler.rb
|
220
226
|
- lib/ruhoh/resources/widgets/model.rb
|
221
227
|
- lib/ruhoh/routes.rb
|
228
|
+
- lib/ruhoh/string_format.rb
|
229
|
+
- lib/ruhoh/summarizer.rb
|
230
|
+
- lib/ruhoh/url_slug.rb
|
222
231
|
- lib/ruhoh/utils.rb
|
223
232
|
- lib/ruhoh/version.rb
|
224
233
|
- lib/ruhoh/views/helpers/categories.rb
|
@@ -267,7 +276,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
267
276
|
version: '0'
|
268
277
|
segments:
|
269
278
|
- 0
|
270
|
-
hash:
|
279
|
+
hash: 897888631782695949
|
271
280
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
272
281
|
none: false
|
273
282
|
requirements:
|
@@ -276,7 +285,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
276
285
|
version: '0'
|
277
286
|
segments:
|
278
287
|
- 0
|
279
|
-
hash:
|
288
|
+
hash: 897888631782695949
|
280
289
|
requirements: []
|
281
290
|
rubyforge_project:
|
282
291
|
rubygems_version: 1.8.24
|