ruhoh 2.5 → 2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/bin/ruhoh +10 -3
- data/features/_root.feature +11 -0
- data/features/data.feature +78 -0
- data/features/javascripts.feature +36 -0
- data/features/permalinks.feature +23 -0
- data/features/plugins.feature +84 -0
- data/features/sort_order.feature +121 -0
- data/features/step_defs.rb +3 -3
- data/features/support/helpers.rb +3 -5
- data/history.json +21 -0
- data/lib/ruhoh.rb +28 -123
- data/lib/ruhoh/base/collectable.rb +273 -0
- data/lib/ruhoh/base/compilable.rb +30 -0
- data/lib/ruhoh/base/compilable_asset.rb +30 -0
- data/lib/ruhoh/base/model_viewable.rb +30 -0
- data/lib/ruhoh/base/modelable.rb +44 -0
- data/lib/ruhoh/base/page_like.rb +111 -0
- data/lib/ruhoh/base/page_viewable.rb +92 -0
- data/lib/ruhoh/base/routable.rb +20 -0
- data/lib/ruhoh/base/watchable.rb +18 -0
- data/lib/ruhoh/cascade.rb +93 -0
- data/lib/ruhoh/client.rb +1 -3
- data/lib/ruhoh/collections.rb +2 -1
- data/lib/ruhoh/config.rb +67 -0
- data/lib/ruhoh/console_methods.rb +0 -2
- data/lib/ruhoh/parse.rb +7 -5
- data/lib/ruhoh/plugins/initializer.rb +24 -0
- data/lib/ruhoh/plugins/local_plugins_plugin.rb +10 -0
- data/lib/ruhoh/plugins/plugin.rb +27 -0
- data/lib/ruhoh/programs/compile.rb +2 -6
- data/lib/ruhoh/programs/preview.rb +5 -2
- data/lib/ruhoh/programs/watch.rb +4 -6
- data/lib/ruhoh/publish/rsync.rb +2 -2
- data/lib/ruhoh/resources/_base/collection.rb +6 -0
- data/lib/ruhoh/resources/_base/compiler.rb +3 -0
- data/lib/ruhoh/resources/_base/model.rb +3 -0
- data/lib/ruhoh/resources/_base/model_view.rb +3 -0
- data/lib/ruhoh/resources/_base/watcher.rb +4 -0
- data/lib/ruhoh/resources/data/collection.rb +30 -9
- data/lib/ruhoh/resources/javascripts/collection_view.rb +5 -1
- data/lib/ruhoh/resources/javascripts/model_view.rb +15 -0
- data/lib/ruhoh/resources/layouts/client.rb +1 -1
- data/lib/ruhoh/resources/pages/client.rb +2 -2
- data/lib/ruhoh/resources/pages/collection.rb +2 -21
- data/lib/ruhoh/resources/theme/compiler.rb +2 -2
- data/lib/ruhoh/resources/widgets/collection.rb +2 -2
- data/lib/ruhoh/routes.rb +1 -1
- data/lib/ruhoh/summarizer.rb +2 -2
- data/lib/ruhoh/ui/dashboard.rb +13 -0
- data/lib/ruhoh/ui/page_not_found.rb +3 -2
- data/lib/ruhoh/url_slug.rb +23 -9
- data/lib/ruhoh/version.rb +1 -1
- data/lib/ruhoh/views/master_view.rb +1 -1
- data/spec/lib/ruhoh/plugins/initializer_spec.rb +43 -0
- data/spec/lib/ruhoh/plugins/plugin_spec.rb +40 -0
- data/spec/spec_helper.rb +1 -0
- data/system/config.json +21 -0
- data/system/{dash/index.html → dashboard.html} +1 -1
- data/{lib/ruhoh/ui → system}/page_not_found.html +0 -0
- data/system/plugins/sprockets/compiler.rb +1 -0
- data/system/widgets/comments/disqus.html +1 -1
- metadata +34 -15
- data/lib/ruhoh/base/collection.rb +0 -284
- data/lib/ruhoh/base/compiler.rb +0 -67
- data/lib/ruhoh/base/model.rb +0 -161
- data/lib/ruhoh/base/model_view.rb +0 -129
- data/lib/ruhoh/base/watcher.rb +0 -25
- data/lib/ruhoh/resources/dash/collection.rb +0 -10
- data/lib/ruhoh/resources/dash/model.rb +0 -5
- data/lib/ruhoh/resources/dash/model_view.rb +0 -5
- data/lib/ruhoh/resources/dash/previewer.rb +0 -13
data/lib/ruhoh/base/compiler.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
module Ruhoh::Base
|
2
|
-
|
3
|
-
module Compilable
|
4
|
-
def self.included(klass)
|
5
|
-
__send__(:attr_reader, :collection)
|
6
|
-
end
|
7
|
-
|
8
|
-
def initialize(collection)
|
9
|
-
@ruhoh = collection.ruhoh
|
10
|
-
@collection = collection
|
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
|
32
|
-
end
|
33
|
-
|
34
|
-
module CompilableAsset
|
35
|
-
include Compilable
|
36
|
-
|
37
|
-
# A basic compiler task which copies each valid collection resource file to the compiled folder.
|
38
|
-
# This is different from the static compiler in that it supports fingerprinting.
|
39
|
-
# Valid files are identified by their pointers.
|
40
|
-
# Invalid files are files that are excluded from the resource's configuration settings.
|
41
|
-
# The collection's url_endpoint is used to determine the final compiled path.
|
42
|
-
#
|
43
|
-
# @returns Nothing.
|
44
|
-
def run
|
45
|
-
return unless setup_compilable
|
46
|
-
|
47
|
-
manifest = {}
|
48
|
-
@collection.files.values.each do |pointer|
|
49
|
-
digest = Digest::MD5.file(pointer['realpath']).hexdigest
|
50
|
-
digest_file = pointer['id'].sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
|
51
|
-
manifest[pointer['id']] = digest_file
|
52
|
-
|
53
|
-
compiled_file = File.join(@collection.compiled_path, digest_file)
|
54
|
-
FileUtils.mkdir_p File.dirname(compiled_file)
|
55
|
-
FileUtils.cp_r pointer['realpath'], compiled_file
|
56
|
-
Ruhoh::Friend.say { green " > #{pointer['id']}" }
|
57
|
-
end
|
58
|
-
|
59
|
-
# Update the paths to the digest format:
|
60
|
-
@collection.load_collection_view._cache.merge!(manifest)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
class Compiler
|
65
|
-
include Ruhoh::Base::Compilable
|
66
|
-
end
|
67
|
-
end
|
data/lib/ruhoh/base/model.rb
DELETED
@@ -1,161 +0,0 @@
|
|
1
|
-
module Ruhoh::Base
|
2
|
-
module Modelable
|
3
|
-
include Observable
|
4
|
-
|
5
|
-
def self.included(klass)
|
6
|
-
klass.__send__(:attr_reader, :pointer, :ruhoh)
|
7
|
-
end
|
8
|
-
|
9
|
-
def initialize(ruhoh, pointer)
|
10
|
-
raise "Cannot instantiate a model with a nil pointer" unless pointer
|
11
|
-
@ruhoh = ruhoh
|
12
|
-
@pointer = pointer
|
13
|
-
end
|
14
|
-
|
15
|
-
# @returns[Hash Object] Top page metadata
|
16
|
-
def data
|
17
|
-
return @data if @data
|
18
|
-
process
|
19
|
-
@data || {}
|
20
|
-
end
|
21
|
-
|
22
|
-
# @returns[String] Raw (unconverted) page content
|
23
|
-
def content
|
24
|
-
return @content if @content
|
25
|
-
process
|
26
|
-
@content || ''
|
27
|
-
end
|
28
|
-
|
29
|
-
def collection
|
30
|
-
@ruhoh.collection(@pointer['resource'])
|
31
|
-
end
|
32
|
-
|
33
|
-
# Override this to process custom data
|
34
|
-
def process
|
35
|
-
changed
|
36
|
-
notify_observers(@pointer)
|
37
|
-
@pointer
|
38
|
-
end
|
39
|
-
|
40
|
-
def try(method)
|
41
|
-
return __send__(method) if respond_to?(method)
|
42
|
-
return data[method.to_s] if data.key?(method.to_s)
|
43
|
-
false
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
module PageLike
|
48
|
-
include Modelable
|
49
|
-
|
50
|
-
DateMatcher = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
|
51
|
-
Matcher = /^(.+\/)*(.*)(\.[^.]+)$/
|
52
|
-
|
53
|
-
# Process this file. See #parse_page_file
|
54
|
-
# @return[Hash] the processed data from the file.
|
55
|
-
# ex:
|
56
|
-
# { "content" => "..", "data" => { "key" => "value" } }
|
57
|
-
def process
|
58
|
-
return {} unless file?
|
59
|
-
|
60
|
-
parsed_page = parse_page_file
|
61
|
-
data = parsed_page['data']
|
62
|
-
|
63
|
-
filename_data = parse_page_filename(@pointer['id'])
|
64
|
-
|
65
|
-
data['pointer'] = @pointer
|
66
|
-
data['id'] = @pointer['id']
|
67
|
-
|
68
|
-
data['title'] = data['title'] || filename_data['title']
|
69
|
-
data['date'] ||= filename_data['date']
|
70
|
-
|
71
|
-
# Parse and store date as an object
|
72
|
-
begin
|
73
|
-
data['date'] = Time.parse(data['date']) unless data['date'].nil? || data['date'].is_a?(Time)
|
74
|
-
rescue
|
75
|
-
Ruhoh.log.error(
|
76
|
-
"ArgumentError: The date '#{data['date']}' specified in '#{@pointer['id']}' is unparsable."
|
77
|
-
)
|
78
|
-
data['date'] = nil
|
79
|
-
end
|
80
|
-
data['url'] = url(data)
|
81
|
-
data['layout'] = collection.config['layout'] if data['layout'].nil?
|
82
|
-
|
83
|
-
parsed_page['data'] = data
|
84
|
-
|
85
|
-
changed
|
86
|
-
notify_observers(parsed_page)
|
87
|
-
data
|
88
|
-
end
|
89
|
-
|
90
|
-
protected
|
91
|
-
|
92
|
-
# Is the resource backed by a physical file in the filesystem?
|
93
|
-
# For example the pagination system uses a page-stub
|
94
|
-
# that has no reference to an actual file.
|
95
|
-
# @return[Boolean]
|
96
|
-
def file?
|
97
|
-
!!@pointer['realpath']
|
98
|
-
end
|
99
|
-
|
100
|
-
# See Ruhoh::Parse.page_file
|
101
|
-
# @returns[Hash Object] processed top meta-data, raw (unconverted) content body
|
102
|
-
def parse_page_file
|
103
|
-
raise "File not found: #{@pointer['realpath']}" unless File.exist?(@pointer['realpath'])
|
104
|
-
result = Ruhoh::Parse.page_file(@pointer['realpath'])
|
105
|
-
|
106
|
-
# variable cache
|
107
|
-
@data = result["data"]
|
108
|
-
@content = result['content']
|
109
|
-
|
110
|
-
result
|
111
|
-
end
|
112
|
-
|
113
|
-
def parse_page_filename(filename)
|
114
|
-
data = *filename.match(DateMatcher)
|
115
|
-
data = *filename.match(Matcher) if data.empty?
|
116
|
-
return {} if data.empty?
|
117
|
-
|
118
|
-
if filename =~ DateMatcher
|
119
|
-
{
|
120
|
-
"path" => data[1],
|
121
|
-
"date" => data[2],
|
122
|
-
"slug" => data[3],
|
123
|
-
"title" => self.to_title(data[3]),
|
124
|
-
"extension" => data[4]
|
125
|
-
}
|
126
|
-
else
|
127
|
-
{
|
128
|
-
"path" => data[1],
|
129
|
-
"slug" => data[2],
|
130
|
-
"title" => to_title(data[2]),
|
131
|
-
"extension" => data[3]
|
132
|
-
}
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# my-post-title ===> My Post Title
|
137
|
-
def to_title(file_slug)
|
138
|
-
if file_slug == 'index' && !@pointer['id'].index('/').nil?
|
139
|
-
file_slug = @pointer['id'].split('/')[-2]
|
140
|
-
end
|
141
|
-
|
142
|
-
Ruhoh::StringFormat.titleize(file_slug)
|
143
|
-
end
|
144
|
-
|
145
|
-
def url(page_data)
|
146
|
-
page_data['permalink_ext'] ||= collection.config['permalink_ext']
|
147
|
-
|
148
|
-
format = page_data['permalink'] ||
|
149
|
-
collection.config['permalink'] ||
|
150
|
-
"/:path/:filename"
|
151
|
-
|
152
|
-
slug = Ruhoh::UrlSlug.new(page_data: page_data, format: format)
|
153
|
-
|
154
|
-
@ruhoh.to_url(slug.generate)
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
class Model
|
159
|
-
include Modelable
|
160
|
-
end
|
161
|
-
end
|
@@ -1,129 +0,0 @@
|
|
1
|
-
require 'ruhoh/summarizer'
|
2
|
-
|
3
|
-
module Ruhoh::Base
|
4
|
-
module ModelViewable
|
5
|
-
def initialize(model)
|
6
|
-
super(model)
|
7
|
-
@model = model
|
8
|
-
@ruhoh = model.ruhoh
|
9
|
-
|
10
|
-
# Define direct access to the data Hash object
|
11
|
-
# but don't overwrite methods if already defined.
|
12
|
-
data.keys.each do |method|
|
13
|
-
(class << self; self; end).class_eval do
|
14
|
-
next if method_defined?(method)
|
15
|
-
define_method method do |*args, &block|
|
16
|
-
data[method]
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def <=>(other)
|
23
|
-
id <=> other.id
|
24
|
-
end
|
25
|
-
|
26
|
-
def [](attribute)
|
27
|
-
__send__(attribute)
|
28
|
-
end
|
29
|
-
|
30
|
-
def []=(key, value)
|
31
|
-
__send__("#{key}=", value)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
module PageViewable
|
36
|
-
include ModelViewable
|
37
|
-
|
38
|
-
# Default order by alphabetical title name.
|
39
|
-
def <=>(other)
|
40
|
-
sort = @model.collection.config["sort"] || []
|
41
|
-
attribute = sort[0] || "title"
|
42
|
-
direction = sort[1] || "asc"
|
43
|
-
|
44
|
-
this_data = __send__(attribute)
|
45
|
-
other_data = other.__send__(attribute)
|
46
|
-
if attribute == "date"
|
47
|
-
if this_data.nil? || other_data.nil?
|
48
|
-
Ruhoh.log.error(
|
49
|
-
"ArgumentError:" +
|
50
|
-
" The '#{ @model.collection.resource_name }' collection is configured to sort based on 'date'" +
|
51
|
-
" but '#{ @model.pointer['id'] }' has no parseable date in its metadata." +
|
52
|
-
" Add date: 'YYYY-MM-DD' to its YAML metadata."
|
53
|
-
)
|
54
|
-
end
|
55
|
-
direction = sort[1] || "desc" #default should be reverse
|
56
|
-
end
|
57
|
-
|
58
|
-
if direction == "asc"
|
59
|
-
this_data <=> other_data
|
60
|
-
else
|
61
|
-
other_data <=> this_data
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def categories
|
66
|
-
@model.collection.to_categories(data['categories'])
|
67
|
-
end
|
68
|
-
|
69
|
-
def tags
|
70
|
-
@model.collection.to_tags(data['tags'])
|
71
|
-
end
|
72
|
-
|
73
|
-
# Lazy-load the page body.
|
74
|
-
# Notes:
|
75
|
-
# @content is not used for caching, it's used to manually
|
76
|
-
# define content for a given page. Useful in the case that
|
77
|
-
# you want to model a resource that does not actually
|
78
|
-
# reference a file.
|
79
|
-
def content
|
80
|
-
return @content if @content
|
81
|
-
content = @model.collection.master.render(@model.content)
|
82
|
-
Ruhoh::Converter.convert(content, id)
|
83
|
-
end
|
84
|
-
|
85
|
-
def is_active_page
|
86
|
-
id == @model.collection.master.page_data['id']
|
87
|
-
end
|
88
|
-
|
89
|
-
def summary
|
90
|
-
model_data = @model.data
|
91
|
-
collection_config = @model.collection.config
|
92
|
-
|
93
|
-
line_limit = model_data['summary_lines'] ||
|
94
|
-
collection_config['summary_lines']
|
95
|
-
stop_at_header = model_data['summary_stop_at_header'] ||
|
96
|
-
collection_config['summary_stop_at_header']
|
97
|
-
|
98
|
-
Ruhoh::Summarizer.new({
|
99
|
-
content: @ruhoh.master_view(@model.pointer).render_content,
|
100
|
-
line_limit: line_limit,
|
101
|
-
stop_at_header: stop_at_header
|
102
|
-
}).generate
|
103
|
-
end
|
104
|
-
|
105
|
-
def next
|
106
|
-
return unless id
|
107
|
-
all_cache = @model.collection.all
|
108
|
-
index = all_cache.index {|p| p["id"] == id}
|
109
|
-
return unless index && (index-1 >= 0)
|
110
|
-
_next = all_cache[index-1]
|
111
|
-
return unless _next
|
112
|
-
_next
|
113
|
-
end
|
114
|
-
|
115
|
-
def previous
|
116
|
-
return unless id
|
117
|
-
all_cache = @model.collection.all
|
118
|
-
index = all_cache.index {|p| p["id"] == id}
|
119
|
-
return unless index && (index+1 >= 0)
|
120
|
-
prev = all_cache[index+1]
|
121
|
-
return unless prev
|
122
|
-
prev
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
class ModelView < SimpleDelegator
|
127
|
-
include ModelViewable
|
128
|
-
end
|
129
|
-
end
|
data/lib/ruhoh/base/watcher.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module Ruhoh::Base
|
2
|
-
module Watchable
|
3
|
-
def self.included(klass)
|
4
|
-
klass.__send__(:attr_accessor, :collection)
|
5
|
-
end
|
6
|
-
|
7
|
-
def initialize(collection)
|
8
|
-
@collection = collection
|
9
|
-
end
|
10
|
-
|
11
|
-
def update(path)
|
12
|
-
# Drop the resource namespace
|
13
|
-
matcher = File::ALT_SEPARATOR ?
|
14
|
-
%r{^.+(#{ File::SEPARATOR }|#{ File::ALT_SEPARATOR })} :
|
15
|
-
%r{^.+#{ File::SEPARATOR }}
|
16
|
-
|
17
|
-
collection.touch(path.gsub(matcher, ''))
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# Base watcher class that loads if no custom Watcher class is defined.
|
22
|
-
class Watcher
|
23
|
-
include Watchable
|
24
|
-
end
|
25
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
module Ruhoh::Resources::Dash
|
2
|
-
class Previewer
|
3
|
-
def initialize(ruhoh)
|
4
|
-
@ruhoh = ruhoh
|
5
|
-
end
|
6
|
-
|
7
|
-
def call(env)
|
8
|
-
pointer = @ruhoh.collection("dash").find_file('index')
|
9
|
-
view = @ruhoh.master_view(pointer)
|
10
|
-
[200, {'Content-Type' => 'text/html'}, [view.render_full]]
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|