ruhoh 2.1 → 2.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/Gemfile +7 -2
- data/README.md +50 -7
- data/Rakefile +1 -9
- data/cucumber.yml +1 -0
- data/features/categories.feature +38 -0
- data/features/conversion.feature +35 -0
- data/features/data.feature +32 -0
- data/features/drafts.feature +21 -0
- data/features/javascripts.feature +42 -0
- data/features/layouts.feature +41 -0
- data/features/pagination.feature +55 -0
- data/features/partials.feature +13 -0
- data/features/permalinks.feature +118 -0
- data/features/step_defs.rb +70 -0
- data/features/stylesheets.feature +42 -0
- data/features/summary.feature +119 -0
- data/features/support/env.rb +8 -0
- data/features/support/helpers.rb +74 -0
- data/features/tags.feature +39 -0
- data/features/themes.feature +0 -0
- data/features/widgets/google_prettify.feature +12 -0
- data/features/widgets/syntax.feature +35 -0
- data/features/widgets/widgets.feature +83 -0
- data/history.json +23 -0
- data/lib/ruhoh/base/model.rb +29 -9
- data/lib/ruhoh/base/model_view.rb +64 -32
- data/lib/ruhoh/programs/watch.rb +1 -0
- data/lib/ruhoh/resources/pages/collection.rb +2 -1
- data/lib/ruhoh/resources/pages/collection_view.rb +9 -7
- data/lib/ruhoh/resources/pages/previewer.rb +11 -10
- data/lib/ruhoh/resources/widgets/collection_view.rb +2 -4
- data/lib/ruhoh/resources/widgets/compiler.rb +19 -3
- data/lib/ruhoh/version.rb +1 -1
- data/ruhoh.gemspec +4 -0
- data/system/plugins/sprockets/compiler.rb +38 -0
- data/system/plugins/sprockets/javascripts/compiler.rb +3 -24
- data/system/plugins/sprockets/javascripts/previewer.rb +4 -12
- data/system/plugins/sprockets/previewer.rb +17 -0
- data/system/plugins/sprockets/stylesheets/compiler.rb +2 -25
- data/system/plugins/sprockets/stylesheets/previewer.rb +3 -12
- data/system/widgets/analytics/google.html +14 -6
- data/system/widgets/google_prettify/default.html +1 -1
- data/system/widgets/syntax/javascripts/prettify.js +30 -0
- data/system/widgets/syntax/prettify.html +18 -0
- metadata +80 -5
- data/spec/spec_helper.rb +0 -29
- data/spec/support/shared_contexts.rb +0 -25
data/history.json
CHANGED
@@ -1,4 +1,27 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"version" : "2.2",
|
4
|
+
"date" : "09.06.2013",
|
5
|
+
"changes" : [
|
6
|
+
"@binaryphile updates google analytics tracking script",
|
7
|
+
"@stebalien overhauls page.summary internal implementation",
|
8
|
+
],
|
9
|
+
"features" : [
|
10
|
+
"Cache the collection#all call to improve performance.",
|
11
|
+
"@stebalien introduces more powerful controls over page.summary",
|
12
|
+
"@lynnfaraday adds page.url to the previewer page data.",
|
13
|
+
"Add cucumber integration tests",
|
14
|
+
"@caspervonb adds system level widgets.syntax to act as unified syntax highlighting widget"
|
15
|
+
],
|
16
|
+
"bugs" : [
|
17
|
+
"@tlupfer fixes date metadata not being substituted in permalinks",
|
18
|
+
"@stebalien fixes sprockets plugin",
|
19
|
+
"@caspervonb makes widgets compiler respect model excludes",
|
20
|
+
"Avoid data parse errors by always converting to string",
|
21
|
+
"Improve error messages for a few parse error cases",
|
22
|
+
"Fix widget config not deep merging with default widget config",
|
23
|
+
]
|
24
|
+
},
|
2
25
|
{
|
3
26
|
"version" : "2.1",
|
4
27
|
"date" : "19.05.2013",
|
data/lib/ruhoh/base/model.rb
CHANGED
@@ -99,7 +99,13 @@ module Ruhoh::Base
|
|
99
99
|
|
100
100
|
page = File.open(@pointer['realpath'], 'r:UTF-8') {|f| f.read }
|
101
101
|
|
102
|
-
|
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
|
+
|
103
109
|
data = front_matter ?
|
104
110
|
(YAML.load(front_matter[0].gsub(/---\n/, "")) || {}) :
|
105
111
|
{}
|
@@ -122,10 +128,6 @@ module Ruhoh::Base
|
|
122
128
|
nil
|
123
129
|
end
|
124
130
|
|
125
|
-
def formatted_date(date)
|
126
|
-
Time.parse(date.to_s).strftime('%Y-%m-%d') rescue false
|
127
|
-
end
|
128
|
-
|
129
131
|
def parse_page_filename(filename)
|
130
132
|
data = *filename.match(DateMatcher)
|
131
133
|
data = *filename.match(Matcher) if data.empty?
|
@@ -179,9 +181,27 @@ module Ruhoh::Base
|
|
179
181
|
"categories" => category || '',
|
180
182
|
}
|
181
183
|
|
182
|
-
|
183
|
-
|
184
|
-
|
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!({
|
185
205
|
"year" => date.strftime("%Y"),
|
186
206
|
"month" => date.strftime("%m"),
|
187
207
|
"day" => date.strftime("%d"),
|
@@ -217,4 +237,4 @@ module Ruhoh::Base
|
|
217
237
|
class Model
|
218
238
|
include Modelable
|
219
239
|
end
|
220
|
-
end
|
240
|
+
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'set'
|
3
|
+
|
1
4
|
module Ruhoh::Base
|
2
5
|
module ModelViewable
|
3
6
|
def initialize(model)
|
@@ -5,7 +8,6 @@ module Ruhoh::Base
|
|
5
8
|
@model = model
|
6
9
|
@ruhoh = model.ruhoh
|
7
10
|
|
8
|
-
# TODO: THIS AUTOMATICALLY CALLS PROCESS ON THE MODEL =XXXX
|
9
11
|
# Define direct access to the data Hash object
|
10
12
|
# but don't overwrite methods if already defined.
|
11
13
|
data.keys.each do |method|
|
@@ -44,9 +46,9 @@ module Ruhoh::Base
|
|
44
46
|
other_data = other.__send__(attribute)
|
45
47
|
if attribute == "date"
|
46
48
|
begin
|
47
|
-
this_data =
|
48
|
-
other_data =
|
49
|
-
rescue ArgumentError
|
49
|
+
this_data = Time.parse(this_data.to_s)
|
50
|
+
other_data = Time.parse(other_data.to_s)
|
51
|
+
rescue ArgumentError, TypeError
|
50
52
|
Ruhoh.log.error(
|
51
53
|
"ArgumentError:" +
|
52
54
|
" The '#{ @model.collection.resource_name }' collection is configured to sort based on 'date'" +
|
@@ -88,41 +90,71 @@ module Ruhoh::Base
|
|
88
90
|
id == @model.collection.master.page_data['id']
|
89
91
|
end
|
90
92
|
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
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.
|
97
102
|
def summary
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
+
|
111
|
+
# Get the configuration parameters
|
112
|
+
# Default to the parameters provided in the page itself
|
113
|
+
model_data = @model.data
|
114
|
+
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
|
+
|
123
|
+
# All "heading" elements.
|
124
|
+
headings = Nokogiri::HTML::ElementDescription::HEADING + ["header", "hgroup"]
|
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"
|
106
139
|
break
|
107
140
|
end
|
108
|
-
else
|
109
|
-
line_count += 1
|
110
141
|
end
|
111
|
-
end
|
112
|
-
|
113
|
-
summary = content.lines.to_a[0, line_breakpoint].join
|
114
142
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
121
152
|
end
|
153
|
+
|
154
|
+
summary_doc << node
|
122
155
|
end
|
123
156
|
|
124
|
-
|
125
|
-
Ruhoh::Converter.convert(summary, id)
|
157
|
+
summary_doc.to_html
|
126
158
|
end
|
127
159
|
|
128
160
|
def next
|
@@ -149,4 +181,4 @@ module Ruhoh::Base
|
|
149
181
|
class ModelView < SimpleDelegator
|
150
182
|
include ModelViewable
|
151
183
|
end
|
152
|
-
end
|
184
|
+
end
|
data/lib/ruhoh/programs/watch.rb
CHANGED
@@ -42,6 +42,7 @@ module Ruhoh::Resources::Pages
|
|
42
42
|
hash['permalink'] ||= "/:path/:filename"
|
43
43
|
hash['summary_lines'] ||= 20
|
44
44
|
hash['summary_lines'] = hash['summary_lines'].to_i
|
45
|
+
hash['summary_stop_at_header'] ||= false
|
45
46
|
hash['latest'] ||= 2
|
46
47
|
hash['latest'] = hash['latest'].to_i
|
47
48
|
hash['ext'] ||= ".md"
|
@@ -83,4 +84,4 @@ module Ruhoh::Resources::Pages
|
|
83
84
|
hash
|
84
85
|
end
|
85
86
|
end
|
86
|
-
end
|
87
|
+
end
|
@@ -9,9 +9,11 @@ module Ruhoh::Resources::Pages
|
|
9
9
|
include Ruhoh::Views::Helpers::Paginator
|
10
10
|
|
11
11
|
def all
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
ruhoh.cache.get("#{ resource_name }-all") ||
|
13
|
+
ruhoh.cache.set("#{ resource_name }-all", dictionary.each_value.find_all { |model|
|
14
|
+
File.basename(File.dirname(model.id)) != "drafts"
|
15
|
+
}.sort
|
16
|
+
)
|
15
17
|
end
|
16
18
|
|
17
19
|
def drafts
|
@@ -39,11 +41,11 @@ module Ruhoh::Resources::Pages
|
|
39
41
|
collated = []
|
40
42
|
pages = all
|
41
43
|
pages.each_with_index do |page, i|
|
42
|
-
thisYear = Time.parse(page['date']).strftime('%Y')
|
43
|
-
thisMonth = Time.parse(page['date']).strftime('%B')
|
44
|
+
thisYear = Time.parse(page['date'].to_s).strftime('%Y')
|
45
|
+
thisMonth = Time.parse(page['date'].to_s).strftime('%B')
|
44
46
|
if (i-1 >= 0)
|
45
|
-
prevYear = Time.parse(pages[i-1]['date']).strftime('%Y')
|
46
|
-
prevMonth = Time.parse(pages[i-1]['date']).strftime('%B')
|
47
|
+
prevYear = Time.parse(pages[i-1]['date'].to_s).strftime('%Y')
|
48
|
+
prevMonth = Time.parse(pages[i-1]['date'].to_s).strftime('%B')
|
47
49
|
end
|
48
50
|
|
49
51
|
if(prevYear == thisYear)
|
@@ -13,17 +13,15 @@ module Ruhoh::Resources::Pages
|
|
13
13
|
env['PATH_INFO'].chomp!("/") unless env['PATH_INFO'] == "/"
|
14
14
|
|
15
15
|
pointer = @ruhoh.routes.find(env['PATH_INFO'])
|
16
|
+
Ruhoh::Friend.say {
|
17
|
+
plain "- previewing page:"
|
18
|
+
plain " #{pointer.inspect}"
|
19
|
+
}
|
20
|
+
|
16
21
|
view = pointer ? @ruhoh.master_view(pointer) : paginator_view(env)
|
22
|
+
|
17
23
|
if view
|
18
|
-
|
19
|
-
Ruhoh::Friend.say {
|
20
|
-
cyan "-> previewing page:"
|
21
|
-
plain " - #{pointer.inspect}"
|
22
|
-
plain " - sub-layout: #{ view.sub_layout }"
|
23
|
-
plain " - master-layout: #{ view.master_layout }"
|
24
|
-
plain " - meta-data: #{ view.page_data }"
|
25
|
-
}
|
26
|
-
[200, {'Content-Type' => 'text/html'}, [content]]
|
24
|
+
[200, {'Content-Type' => 'text/html'}, [view.render_full]]
|
27
25
|
else
|
28
26
|
message = "No generated page URL matches '#{ env['PATH_INFO'] }'" +
|
29
27
|
" using file pointer: '#{ pointer.inspect }'."
|
@@ -57,10 +55,13 @@ module Ruhoh::Resources::Pages
|
|
57
55
|
collection = @ruhoh.collection(resource)
|
58
56
|
config = collection.config["paginator"] || {}
|
59
57
|
|
58
|
+
url = "#{config["url"]}/#{page_number}"
|
59
|
+
|
60
60
|
view = @ruhoh.master_view({"resource" => resource})
|
61
61
|
view.page_data = {
|
62
62
|
"layout" => config["layout"],
|
63
|
-
"current_page" => page_number
|
63
|
+
"current_page" => page_number,
|
64
|
+
"url" => @ruhoh.to_url(url)
|
64
65
|
}
|
65
66
|
view
|
66
67
|
end
|
@@ -9,15 +9,13 @@ module Ruhoh::Resources::Widgets
|
|
9
9
|
model = find("#{ name }/#{ (widget_config['use'] || "default") }")
|
10
10
|
return '' unless model
|
11
11
|
|
12
|
-
view = ruhoh.master_view({})
|
13
|
-
|
14
12
|
# merge the config.yml data into the inline layout data.
|
15
13
|
# Note this is reversing the normal hierarchy
|
16
14
|
# in that inline should always override config level.
|
17
15
|
# However the inline in this case is set as implementation defaults
|
18
16
|
# and meant to be overridden by user specific data.
|
19
|
-
|
20
|
-
"this_config" => model.data
|
17
|
+
master.render(model.content, {
|
18
|
+
"this_config" => Ruhoh::Utils.deep_merge(model.data, widget_config),
|
21
19
|
"this_path" => ruhoh.to_url(url_endpoint, name)
|
22
20
|
})
|
23
21
|
end
|
@@ -13,9 +13,9 @@ module Ruhoh::Resources::Widgets
|
|
13
13
|
compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(@collection.url_endpoint), @ruhoh.paths.compiled)
|
14
14
|
FileUtils.mkdir_p compiled_path
|
15
15
|
|
16
|
-
files =
|
17
|
-
|
18
|
-
|
16
|
+
files = collection.files.values
|
17
|
+
files.delete_if { |p| !is_valid_file? (p['id']) }
|
18
|
+
|
19
19
|
files.each do |pointer|
|
20
20
|
compiled_file = File.join(compiled_path, pointer['id'])
|
21
21
|
FileUtils.mkdir_p File.dirname(compiled_file)
|
@@ -23,5 +23,21 @@ module Ruhoh::Resources::Widgets
|
|
23
23
|
Ruhoh::Friend.say { green " > #{pointer['id']}" }
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
def is_valid_file?(filepath)
|
28
|
+
return false if filepath.end_with?('.html')
|
29
|
+
|
30
|
+
collection.widgets.each do |name|
|
31
|
+
widget_config = collection.config[name] || {}
|
32
|
+
|
33
|
+
model = collection.find("#{ name }/#{ (widget_config['use'] || "default") }")
|
34
|
+
next unless model
|
35
|
+
|
36
|
+
excludes = Array(model.data['exclude']).map { |node| Regexp.new(node) }
|
37
|
+
excludes.each { |regex| return false if filepath =~ regex }
|
38
|
+
end
|
39
|
+
|
40
|
+
true
|
41
|
+
end
|
26
42
|
end
|
27
43
|
end
|
data/lib/ruhoh/version.rb
CHANGED
data/ruhoh.gemspec
CHANGED
@@ -20,6 +20,10 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_dependency 'redcarpet', "~> 2.1"
|
21
21
|
s.add_dependency 'nokogiri', "~> 1.5"
|
22
22
|
|
23
|
+
s.add_development_dependency 'cucumber'
|
24
|
+
s.add_development_dependency 'capybara'
|
25
|
+
s.add_development_dependency 'rspec-expectations'
|
26
|
+
|
23
27
|
s.files = `git ls-files`.
|
24
28
|
split("\n").
|
25
29
|
sort.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'sprockets'
|
2
|
+
|
3
|
+
module Ruhoh::SprocketsPlugin
|
4
|
+
module Compiler
|
5
|
+
extend Ruhoh::Base::CompilableAsset
|
6
|
+
def run
|
7
|
+
env = Sprockets::Environment.new
|
8
|
+
env.logger = Logger.new(STDOUT)
|
9
|
+
env.logger.level = Logger::WARN
|
10
|
+
|
11
|
+
collection = @collection
|
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 "#{collection.resource_name.capitalize}: (using sprockets)" }
|
19
|
+
|
20
|
+
collection.paths.reverse.each do |path|
|
21
|
+
env.append_path(path)
|
22
|
+
end
|
23
|
+
|
24
|
+
compiled_path = Ruhoh::Utils.url_to_path(@ruhoh.to_url(collection.url_endpoint), @ruhoh.paths.compiled)
|
25
|
+
FileUtils.mkdir_p compiled_path
|
26
|
+
|
27
|
+
manifest = Sprockets::Manifest.new(env, compiled_path)
|
28
|
+
assets = collection.files.values.map{ |p|
|
29
|
+
Ruhoh::Friend.say { green " > #{p['id']}" }
|
30
|
+
p["id"]
|
31
|
+
}
|
32
|
+
manifest.compile(assets)
|
33
|
+
|
34
|
+
# Update the paths to the digest format:
|
35
|
+
@collection._cache.merge!(manifest.assets)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,25 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
include Ruhoh::Base::Compilable
|
5
|
-
|
6
|
-
def run
|
7
|
-
Ruhoh::Friend.say { cyan "Javascripts: (using sprockets)" }
|
8
|
-
env = Sprockets::Environment.new
|
9
|
-
env.logger = Logger.new(STDOUT)
|
10
|
-
@collection.paths.reverse.each do |h|
|
11
|
-
env.append_path(File.join(h["path"], @collection.resource_name))
|
12
|
-
end
|
13
|
-
|
14
|
-
compiled_path = Ruhoh::Utils.url_to_path(@collection.url_endpoint, @ruhoh.paths.compiled)
|
15
|
-
FileUtils.mkdir_p compiled_path
|
16
|
-
|
17
|
-
manifest = Sprockets::Manifest.new(env, compiled_path)
|
18
|
-
assets = @collection.files.values.map{ |p| p["id"] }
|
19
|
-
manifest.compile(assets)
|
20
|
-
|
21
|
-
# Update the stylesheet paths to the digest format:
|
22
|
-
@collection._cache.merge!(manifest.assets)
|
23
|
-
end
|
24
|
-
end
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'compiler.rb')
|
2
|
+
class Ruhoh::Resources::Javascripts::Compiler
|
3
|
+
include Ruhoh::SprocketsPlugin::Compiler
|
25
4
|
end
|