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/client.rb
CHANGED
@@ -40,8 +40,6 @@ class Ruhoh
|
|
40
40
|
return server if %w(s serve server).include?(cmd)
|
41
41
|
|
42
42
|
@ruhoh = Ruhoh.new
|
43
|
-
@ruhoh.setup
|
44
|
-
@ruhoh.setup_paths
|
45
43
|
@ruhoh.setup_plugins
|
46
44
|
|
47
45
|
return __send__(cmd) if respond_to?(cmd)
|
@@ -136,7 +134,7 @@ class Ruhoh
|
|
136
134
|
end
|
137
135
|
|
138
136
|
if Ruhoh::Publish.const_defined?(service.to_sym)
|
139
|
-
publish_config = Ruhoh::Parse.data_file(@ruhoh.base, "publish") || {}
|
137
|
+
publish_config = Ruhoh::Parse.data_file(@ruhoh.cascade.base, "publish") || {}
|
140
138
|
Ruhoh::Publish.const_get(service.to_sym).new.run(@args, publish_config[service.downcase])
|
141
139
|
else
|
142
140
|
Ruhoh::Friend.say {
|
data/lib/ruhoh/collections.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
module Ruhoh::Base ; end
|
1
2
|
module Ruhoh::Resources ; end
|
2
3
|
# Require all the resources
|
3
4
|
FileUtils.cd(File.join(File.dirname(__FILE__), 'base')) do
|
@@ -71,7 +72,7 @@ class Ruhoh
|
|
71
72
|
def discover
|
72
73
|
results = Set.new
|
73
74
|
|
74
|
-
@ruhoh.cascade.each do |h|
|
75
|
+
@ruhoh.cascade.paths.each do |h|
|
75
76
|
FileUtils.cd(h["path"]) do
|
76
77
|
results += Dir['*'].select { |x|
|
77
78
|
File.directory?(x) && !["plugins", 'compiled'].include?(x)
|
data/lib/ruhoh/config.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
class Ruhoh
|
2
|
+
class Config < SimpleDelegator
|
3
|
+
include Observable
|
4
|
+
|
5
|
+
def initialize(ruhoh)
|
6
|
+
@ruhoh = ruhoh
|
7
|
+
@data = {}
|
8
|
+
super(@data)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Regenerate the config data
|
12
|
+
def touch
|
13
|
+
data = @ruhoh.cascade.merge_data_file('config') || {}
|
14
|
+
data = Ruhoh::Utils.deep_merge(data, collections_config)
|
15
|
+
data = Ruhoh::Utils.deep_merge(data, find_theme_path)
|
16
|
+
|
17
|
+
@data.clear
|
18
|
+
@data.merge!(data)
|
19
|
+
|
20
|
+
Time.default_format = @data['date_format']
|
21
|
+
@data["compiled_path"] = File.expand_path(@data["compiled_path"])
|
22
|
+
|
23
|
+
changed
|
24
|
+
notify_observers(@data)
|
25
|
+
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def base_path
|
30
|
+
return '/' unless (@ruhoh.env == 'production')
|
31
|
+
|
32
|
+
@data['base_path'] += "/" unless @data['base_path'][-1] == '/'
|
33
|
+
string = @data['base_path'].chomp('/').reverse.chomp('/').reverse
|
34
|
+
return '/' if string.empty? || string == '/'
|
35
|
+
"/#{ string }/"
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def find_theme_path
|
41
|
+
theme_name = @data.find { |resource, data| data.is_a?(Hash) && data['use'] == "theme" }
|
42
|
+
if theme_name
|
43
|
+
Ruhoh::Friend.say { plain "Using theme: \"#{theme_name[0]}\""}
|
44
|
+
{ "_theme_collection" => theme_name[0] }
|
45
|
+
else
|
46
|
+
{ "_theme_collection" => nil }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Quick and dirty way to scan for config files in collection folders.
|
51
|
+
# This is needed because we don't know which collection defines itself as a theme
|
52
|
+
# so we'll scan for any configs and merge the data to find the theme folder.
|
53
|
+
def collections_config
|
54
|
+
data = {}
|
55
|
+
@ruhoh.cascade.paths.map{ |a| a['path'] }.each do |path|
|
56
|
+
FileUtils.cd(path) {
|
57
|
+
Dir["*/config.*"].each { |id|
|
58
|
+
next unless File.exist?(id) && FileTest.file?(id)
|
59
|
+
data = Ruhoh::Utils.deep_merge(data, (Ruhoh::Parse.data_file(File.realpath(id)) || {}))
|
60
|
+
}
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
data
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/ruhoh/parse.rb
CHANGED
@@ -42,13 +42,15 @@ class Ruhoh
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def self.data_file(*args)
|
45
|
-
|
45
|
+
filepath = File.__send__(:join, args)
|
46
|
+
if File.extname(filepath).to_s.empty?
|
47
|
+
path = nil
|
48
|
+
["#{ filepath }.json", "#{ filepath }.yml", "#{ filepath }.yaml"].each do |result|
|
49
|
+
filepath = path = result and break if File.exist?(result)
|
50
|
+
end
|
46
51
|
|
47
|
-
|
48
|
-
["#{ base }.json", "#{ base }.yml", "#{ base }.yaml"].each do |result|
|
49
|
-
filepath = result and break if File.exist?(result)
|
52
|
+
return nil unless path
|
50
53
|
end
|
51
|
-
return nil unless filepath
|
52
54
|
|
53
55
|
file = File.open(filepath, 'r:UTF-8') { |f| f.read }
|
54
56
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ruhoh::Plugins
|
2
|
+
class Initializer
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def initialize name, &block
|
6
|
+
raise ArgumentError, "block required for initializer '#{name}'" unless block_given?
|
7
|
+
@name, @block = name, block
|
8
|
+
end
|
9
|
+
|
10
|
+
def run *args
|
11
|
+
raise "Initializer '#{name}' need to be bound" unless context
|
12
|
+
context.instance_exec *args, &block
|
13
|
+
end
|
14
|
+
|
15
|
+
def bind ctx
|
16
|
+
@context = ctx
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :block, :context
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'ruhoh/plugins/initializer'
|
2
|
+
|
3
|
+
module Ruhoh::Plugins
|
4
|
+
module Plugin
|
5
|
+
def self.included base
|
6
|
+
base.send :extend, ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.run_all(context, *args)
|
10
|
+
initializers.each do |i|
|
11
|
+
i.bind(context).run *args
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def self.initializers
|
18
|
+
@initializers ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def initializer name, &block
|
23
|
+
Plugin.initializers << Initializer.new(name, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -5,15 +5,11 @@ class Ruhoh
|
|
5
5
|
# to properly omit drafts and other development-only settings.
|
6
6
|
def self.compile(target=nil)
|
7
7
|
ruhoh = Ruhoh.new
|
8
|
-
ruhoh.setup
|
9
8
|
ruhoh.env = 'production'
|
10
|
-
ruhoh.setup_paths
|
11
9
|
ruhoh.setup_plugins
|
12
|
-
|
10
|
+
|
13
11
|
if target
|
14
|
-
ruhoh.
|
15
|
-
elsif ruhoh.config["compiled"]
|
16
|
-
ruhoh.paths.compiled = ruhoh.config["compiled"]
|
12
|
+
ruhoh.config['compiled_path'] = File.expand_path(target)
|
17
13
|
end
|
18
14
|
|
19
15
|
ruhoh.compile
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'ruhoh/programs/watch'
|
2
|
+
require 'ruhoh/ui/dashboard'
|
2
3
|
class Ruhoh
|
3
4
|
module Program
|
4
5
|
# Public: A program for running ruhoh as a rack application
|
@@ -17,9 +18,7 @@ class Ruhoh
|
|
17
18
|
opts[:env] ||= 'development'
|
18
19
|
|
19
20
|
ruhoh = Ruhoh.new
|
20
|
-
ruhoh.setup
|
21
21
|
ruhoh.env = opts[:env]
|
22
|
-
ruhoh.setup_paths
|
23
22
|
ruhoh.setup_plugins unless opts[:enable_plugins] == false
|
24
23
|
|
25
24
|
# initialize the routes dictionary for all page resources.
|
@@ -52,6 +51,10 @@ class Ruhoh
|
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
54
|
+
map '/dash' do
|
55
|
+
run Ruhoh::UI::Dashboard.new(ruhoh)
|
56
|
+
end
|
57
|
+
|
55
58
|
# The generic Page::Previewer is used to render any/all page-like resources,
|
56
59
|
# since they likely have arbitrary urls based on permalink settings.
|
57
60
|
map '/' do
|
data/lib/ruhoh/programs/watch.rb
CHANGED
@@ -7,12 +7,10 @@ class Ruhoh
|
|
7
7
|
# The observer triggers data regeneration as files change
|
8
8
|
# in order to keep the data up to date in real time.
|
9
9
|
def self.watch(ruhoh)
|
10
|
-
ruhoh.ensure_setup
|
11
|
-
|
12
10
|
Ruhoh::Friend.say {
|
13
|
-
cyan "=> Start watching: #{ruhoh.
|
11
|
+
cyan "=> Start watching: #{ruhoh.cascade.base}"
|
14
12
|
}
|
15
|
-
dw = DirectoryWatcher.new(ruhoh.
|
13
|
+
dw = DirectoryWatcher.new(ruhoh.cascade.base, {
|
16
14
|
:glob => "**/*",
|
17
15
|
:pre_load => true
|
18
16
|
})
|
@@ -21,14 +19,14 @@ class Ruhoh
|
|
21
19
|
args.each do |event|
|
22
20
|
ruhoh.cache.delete(event['path'])
|
23
21
|
|
24
|
-
path = event['path'].gsub(ruhoh.
|
22
|
+
path = event['path'].gsub(ruhoh.cascade.base + '/', '')
|
25
23
|
|
26
24
|
Ruhoh::Friend.say {
|
27
25
|
yellow "Watch [#{Time.now.strftime("%H:%M:%S")}] [Update #{path}] : #{args.size} files changed"
|
28
26
|
}
|
29
27
|
|
30
28
|
if %w{ config.json config.yml config.yaml }.include?(path)
|
31
|
-
ruhoh.config
|
29
|
+
ruhoh.config.touch
|
32
30
|
else
|
33
31
|
separator = File::ALT_SEPARATOR ?
|
34
32
|
%r{#{ File::SEPARATOR }|#{ File::ALT_SEPARATOR }} :
|
data/lib/ruhoh/publish/rsync.rb
CHANGED
@@ -22,9 +22,9 @@ class Ruhoh
|
|
22
22
|
if @config["command"]
|
23
23
|
system(@config["command"])
|
24
24
|
else
|
25
|
-
system('rsync', File.join(ruhoh.
|
25
|
+
system('rsync', File.join(ruhoh.config['compiled_path'], '.'), '-avz', '--delete', '--exclude', '.git', remote)
|
26
26
|
end
|
27
|
-
FileUtils.rm_r(ruhoh.
|
27
|
+
FileUtils.rm_r(ruhoh.config['compiled_path'])
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
@@ -2,24 +2,45 @@ module Ruhoh::Resources::Data
|
|
2
2
|
class Collection
|
3
3
|
include Ruhoh::Base::Collectable
|
4
4
|
|
5
|
+
def glob
|
6
|
+
"*"
|
7
|
+
end
|
8
|
+
|
9
|
+
def dictionary
|
10
|
+
resource_name == "data" ?
|
11
|
+
_support_legacy_api :
|
12
|
+
_support_new_data_api
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def _support_new_data_api
|
18
|
+
data = {}
|
19
|
+
files.values.each do |pointer|
|
20
|
+
name = File.basename(pointer["id"], File.extname(pointer["id"]))
|
21
|
+
data[name] = Ruhoh::Parse.data_file(pointer["realpath"]) || {}
|
22
|
+
end
|
23
|
+
|
24
|
+
data
|
25
|
+
end
|
26
|
+
|
5
27
|
# TODO: This is ugly but it works.
|
6
28
|
# Should handle data extensions in the cascade more elegantly
|
7
|
-
def
|
8
|
-
|
29
|
+
def _support_legacy_api
|
30
|
+
found_paths = []
|
9
31
|
|
10
|
-
@ruhoh.cascade.
|
32
|
+
@ruhoh.cascade.paths.each do |h|
|
11
33
|
path_prefix = File.join(h["path"], resource_name)
|
12
34
|
|
13
35
|
["#{ path_prefix }.json", "#{ path_prefix }.yml", "#{ path_prefix }.yaml"].each do |file|
|
14
|
-
|
36
|
+
found_paths << path_prefix and break if File.exist?(file)
|
15
37
|
end
|
16
|
-
|
17
|
-
break if found_path_prefix
|
18
38
|
end
|
19
39
|
|
20
|
-
|
40
|
+
data = {}
|
41
|
+
found_paths.each { |path| data.merge!(Ruhoh::Parse.data_file(path) || {}) }
|
21
42
|
|
22
|
-
|
43
|
+
data
|
23
44
|
end
|
24
45
|
end
|
25
|
-
end
|
46
|
+
end
|
@@ -24,7 +24,7 @@ module Ruhoh::Resources::Layouts
|
|
24
24
|
exit
|
25
25
|
} if name.nil?
|
26
26
|
|
27
|
-
filename = File.join((@ruhoh.
|
27
|
+
filename = File.join((@ruhoh.cascade.theme || @ruhoh.cascade.base), "layouts", name.gsub(/\s/, '-').downcase) + ".html"
|
28
28
|
|
29
29
|
if File.exist?(filename)
|
30
30
|
abort("Create new layout: aborted!") if ask("#{filename} already exists. Do you want to overwrite?", ['y', 'n']) == 'n'
|
@@ -105,8 +105,8 @@ module Ruhoh::Resources::Pages
|
|
105
105
|
|
106
106
|
name = "#{name}-#{@iterator}" unless @iterator.zero?
|
107
107
|
filename = opts[:draft] ?
|
108
|
-
File.join(@ruhoh.
|
109
|
-
File.join(@ruhoh.
|
108
|
+
File.join(@ruhoh.cascade.base, @collection.resource_name, "drafts", "#{name}#{ext}") :
|
109
|
+
File.join(@ruhoh.cascade.base, @collection.resource_name, "#{name}#{ext}")
|
110
110
|
@iterator += 1
|
111
111
|
end while File.exist?(filename)
|
112
112
|
|
@@ -1,27 +1,8 @@
|
|
1
|
+
require 'ruhoh/base/routable'
|
1
2
|
module Ruhoh::Resources::Pages
|
2
|
-
module Routable
|
3
|
-
def routes
|
4
|
-
return @routes if @routes
|
5
|
-
@routes = {}
|
6
|
-
dictionary
|
7
|
-
@routes
|
8
|
-
end
|
9
|
-
|
10
|
-
def routes_add(route, pointer)
|
11
|
-
@routes ||= {}
|
12
|
-
@routes[route] = pointer
|
13
|
-
end
|
14
|
-
|
15
|
-
def routes_delete(pointer)
|
16
|
-
return unless @routes
|
17
|
-
route = @routes.find{ |k, v| v == pointer }
|
18
|
-
@routes.delete(route[0]) if route
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
3
|
class Collection
|
23
4
|
include Ruhoh::Base::Collectable
|
24
|
-
include Routable
|
5
|
+
include Ruhoh::Base::Routable
|
25
6
|
|
26
7
|
# model observer callback.
|
27
8
|
def update(model_data)
|