ruhoh 1.1 → 2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -3
- data/README.md +3 -2
- data/Rakefile +1 -22
- data/bin/ruhoh +1 -5
- data/history.json +16 -0
- data/lib/ruhoh.rb +229 -84
- data/lib/ruhoh/base/collection.rb +280 -0
- data/lib/ruhoh/base/compiler.rb +55 -0
- data/lib/ruhoh/base/model.rb +220 -0
- data/lib/ruhoh/base/model_view.rb +152 -0
- data/lib/ruhoh/base/watcher.rb +25 -0
- data/lib/ruhoh/cache.rb +46 -0
- data/lib/ruhoh/client.rb +162 -0
- data/lib/ruhoh/collections.rb +172 -0
- data/lib/ruhoh/console_methods.rb +21 -0
- data/lib/ruhoh/{converters/converter.rb → converter.rb} +4 -1
- data/lib/ruhoh/programs/compile.rb +22 -0
- data/lib/ruhoh/programs/preview.rb +63 -0
- data/lib/ruhoh/programs/watch.rb +45 -0
- data/lib/ruhoh/resources/dash/collection.rb +10 -0
- data/lib/ruhoh/resources/dash/model.rb +5 -0
- data/lib/ruhoh/resources/dash/model_view.rb +5 -0
- data/lib/ruhoh/resources/dash/previewer.rb +13 -0
- data/lib/ruhoh/resources/data/collection.rb +9 -0
- data/lib/ruhoh/resources/data/collection_view.rb +23 -0
- data/lib/ruhoh/resources/javascripts/collection.rb +9 -0
- data/lib/ruhoh/resources/javascripts/collection_view.rb +46 -0
- data/lib/ruhoh/resources/javascripts/compiler.rb +5 -0
- data/lib/ruhoh/resources/layouts/client.rb +45 -0
- data/lib/ruhoh/resources/layouts/model.rb +16 -0
- data/lib/ruhoh/resources/media/collection.rb +9 -0
- data/lib/ruhoh/resources/media/compiler.rb +27 -0
- data/lib/ruhoh/resources/pages/client.rb +124 -0
- data/lib/ruhoh/resources/pages/collection.rb +86 -0
- data/lib/ruhoh/resources/pages/collection_view.rb +73 -0
- data/lib/ruhoh/resources/pages/compiler.rb +101 -0
- data/lib/ruhoh/resources/pages/model.rb +5 -0
- data/lib/ruhoh/resources/pages/model_view.rb +5 -0
- data/lib/ruhoh/resources/pages/previewer.rb +72 -0
- data/lib/ruhoh/resources/partials/model.rb +11 -0
- data/lib/ruhoh/resources/stylesheets/collection.rb +9 -0
- data/lib/ruhoh/resources/stylesheets/collection_view.rb +45 -0
- data/lib/ruhoh/resources/stylesheets/compiler.rb +5 -0
- data/lib/ruhoh/resources/theme/collection.rb +14 -0
- data/lib/ruhoh/resources/theme/compiler.rb +54 -0
- data/lib/ruhoh/resources/widgets/collection.rb +26 -0
- data/lib/ruhoh/resources/widgets/collection_view.rb +34 -0
- data/lib/ruhoh/resources/widgets/compiler.rb +27 -0
- data/lib/ruhoh/resources/widgets/model.rb +16 -0
- data/lib/ruhoh/routes.rb +29 -0
- data/lib/ruhoh/utils.rb +32 -49
- data/lib/ruhoh/version.rb +2 -2
- data/lib/ruhoh/views/helpers/categories.rb +38 -0
- data/lib/ruhoh/views/helpers/paginator.rb +39 -0
- data/lib/ruhoh/views/helpers/tags.rb +37 -0
- data/lib/ruhoh/views/master_view.rb +183 -0
- data/lib/ruhoh/views/rmustache.rb +24 -0
- data/ruhoh.gemspec +6 -82
- data/spec/spec_helper.rb +1 -1
- data/spec/support/shared_contexts.rb +6 -5
- data/system/{scaffolds/post.html → _scaffold.html} +1 -1
- data/system/{dash.html → dash/index.html} +37 -51
- data/system/{scaffolds/layout.html → layouts/_scaffold.html} +0 -0
- data/system/layouts/paginator.html +28 -0
- data/system/plugins/sprockets/javascripts/compiler.rb +25 -0
- data/system/plugins/sprockets/javascripts/previewer.rb +17 -0
- data/system/plugins/sprockets/stylesheets/compiler.rb +26 -0
- data/system/plugins/sprockets/stylesheets/previewer.rb +17 -0
- data/system/widgets/analytics/{layouts/getclicky.html → getclicky.html} +6 -2
- data/system/widgets/analytics/{layouts/google.html → google.html} +5 -1
- data/system/widgets/comments/{layouts/disqus.html → disqus.html} +6 -2
- data/system/widgets/comments/{layouts/facebook.html → facebook.html} +9 -2
- data/system/widgets/comments/{layouts/intensedebate.html → intensedebate.html} +5 -1
- data/system/widgets/comments/{layouts/livefyre.html → livefyre.html} +5 -1
- data/system/widgets/google_prettify/{layouts/google_prettify.html → default.html} +6 -2
- metadata +69 -66
- data/lib/ruhoh/client/client.rb +0 -306
- data/lib/ruhoh/client/console_methods.rb +0 -9
- data/lib/ruhoh/client/help.yml +0 -56
- data/lib/ruhoh/compiler.rb +0 -72
- data/lib/ruhoh/compilers/rss.rb +0 -39
- data/lib/ruhoh/compilers/theme.rb +0 -46
- data/lib/ruhoh/config.rb +0 -62
- data/lib/ruhoh/db.rb +0 -50
- data/lib/ruhoh/deployers/s3.rb +0 -71
- data/lib/ruhoh/page.rb +0 -106
- data/lib/ruhoh/parsers/javascripts.rb +0 -55
- data/lib/ruhoh/parsers/layouts.rb +0 -32
- data/lib/ruhoh/parsers/pages.rb +0 -79
- data/lib/ruhoh/parsers/partials.rb +0 -42
- data/lib/ruhoh/parsers/payload.rb +0 -49
- data/lib/ruhoh/parsers/posts.rb +0 -259
- data/lib/ruhoh/parsers/routes.rb +0 -20
- data/lib/ruhoh/parsers/scaffolds.rb +0 -35
- data/lib/ruhoh/parsers/site.rb +0 -19
- data/lib/ruhoh/parsers/stylesheets.rb +0 -63
- data/lib/ruhoh/parsers/theme_config.rb +0 -30
- data/lib/ruhoh/parsers/widgets.rb +0 -104
- data/lib/ruhoh/paths.rb +0 -83
- data/lib/ruhoh/previewer.rb +0 -48
- data/lib/ruhoh/program.rb +0 -68
- data/lib/ruhoh/templaters/asset_helpers.rb +0 -66
- data/lib/ruhoh/templaters/base_helpers.rb +0 -147
- data/lib/ruhoh/templaters/helpers.rb +0 -8
- data/lib/ruhoh/templaters/rmustache.rb +0 -70
- data/lib/ruhoh/urls.rb +0 -50
- data/lib/ruhoh/watch.rb +0 -78
- data/spec/config_spec.rb +0 -50
- data/spec/db_spec.rb +0 -91
- data/spec/page_spec.rb +0 -164
- data/spec/parsers/layouts_spec.rb +0 -41
- data/spec/parsers/pages_spec.rb +0 -120
- data/spec/parsers/posts_spec.rb +0 -309
- data/spec/parsers/routes_spec.rb +0 -39
- data/spec/parsers/site_spec.rb +0 -28
- data/spec/setup_spec.rb +0 -12
- data/system/scaffolds/draft.html +0 -9
- data/system/scaffolds/page.html +0 -4
- data/system/widgets/analytics/config.yml +0 -5
- data/system/widgets/comments/config.yml +0 -13
- data/system/widgets/google_prettify/config.yml +0 -1
data/Gemfile
CHANGED
@@ -2,12 +2,12 @@ source "http://rubygems.org"
|
|
2
2
|
gemspec
|
3
3
|
|
4
4
|
gem 'rack', "~> 1.4"
|
5
|
-
gem 'directory_watcher', "~> 1.4"
|
6
|
-
gem 'psych', "~> 1.3"
|
5
|
+
gem 'directory_watcher', "~> 1.4.0"
|
6
|
+
gem 'psych', "~> 1.3"#, :platforms => [:ruby_18, :mingw_18]
|
7
7
|
gem 'redcarpet', "~> 2.1"
|
8
8
|
gem 'nokogiri', "~> 1.5"
|
9
9
|
|
10
10
|
group :development do
|
11
|
-
gem 'rspec', "~> 2
|
11
|
+
gem 'rspec', "~> 2"
|
12
12
|
gem 'rake'
|
13
13
|
end
|
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -17,33 +17,12 @@ task :release => :build do
|
|
17
17
|
sh "gem push pkg/#{name}-#{Ruhoh::VERSION}.gem"
|
18
18
|
end
|
19
19
|
|
20
|
-
task :build
|
20
|
+
task :build do
|
21
21
|
sh "mkdir -p pkg"
|
22
22
|
sh "gem build #{gemspec_file}"
|
23
23
|
sh "mv #{gem_file} pkg"
|
24
24
|
end
|
25
25
|
|
26
|
-
task :gemspec do
|
27
|
-
# read spec file and split out manifest section
|
28
|
-
spec = File.read(gemspec_file)
|
29
|
-
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
30
|
-
|
31
|
-
# determine file list from git ls-files
|
32
|
-
files = `git ls-files`.
|
33
|
-
split("\n").
|
34
|
-
sort.
|
35
|
-
reject { |file| file =~ /^\./ }.
|
36
|
-
reject { |file| file =~ /^(rdoc|pkg|coverage)/ }.
|
37
|
-
map { |file| " #{file}" }.
|
38
|
-
join("\n")
|
39
|
-
|
40
|
-
# piece file back together and write
|
41
|
-
manifest = " s.files = %w[\n#{files}\n ]\n"
|
42
|
-
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
43
|
-
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
44
|
-
puts "Updated #{gemspec_file}"
|
45
|
-
end
|
46
|
-
|
47
26
|
## Tests
|
48
27
|
|
49
28
|
RSpec::Core::RakeTask.new('spec')
|
data/bin/ruhoh
CHANGED
@@ -4,7 +4,7 @@ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
|
4
4
|
|
5
5
|
require 'ruhoh'
|
6
6
|
require 'ruhoh/version'
|
7
|
-
require 'ruhoh/client
|
7
|
+
require 'ruhoh/client'
|
8
8
|
|
9
9
|
require 'optparse'
|
10
10
|
|
@@ -14,10 +14,6 @@ options = Options.new
|
|
14
14
|
opt_parser = OptionParser.new do |opts|
|
15
15
|
opts.banner = 'Use `ruhoh help` for full command list.'
|
16
16
|
|
17
|
-
opts.on("-e", "--ext [EXT]", "Specify filename extension. Defaults to '.md' ") do |ext|
|
18
|
-
options.ext = ext
|
19
|
-
end
|
20
|
-
|
21
17
|
opts.on("--hg", "Use mercurial (hg) instead of git for source control.") do
|
22
18
|
options.hg = true
|
23
19
|
end
|
data/history.json
CHANGED
@@ -1,4 +1,20 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"version" : "2.1",
|
4
|
+
"date" : "19.05.2013",
|
5
|
+
"changes" : [
|
6
|
+
"Major release. Internal logic has been drastically changed."
|
7
|
+
],
|
8
|
+
"features" : [
|
9
|
+
"Support for arbitrary custom collections, e.g. ability to model essays, snippets, posts, etc.",
|
10
|
+
"Page finders can omit file extensions so you can reference the about.md file using 'about'.",
|
11
|
+
"All collections can have their own paginator.",
|
12
|
+
"themes and config.yml are optional implying very basic websites can be built without configuration overhead.",
|
13
|
+
"ruhoh environment is not instantiated, meaning everything exists in its own instance.",
|
14
|
+
"Improved cache management",
|
15
|
+
"Support for asset pipeline."
|
16
|
+
]
|
17
|
+
},
|
2
18
|
{
|
3
19
|
"version" : "1.1",
|
4
20
|
"date" : "26.08.2012",
|
data/lib/ruhoh.rb
CHANGED
@@ -3,111 +3,256 @@ Encoding.default_internal = 'UTF-8'
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'psych'
|
5
5
|
YAML::ENGINE.yamler = 'psych'
|
6
|
-
|
7
6
|
require 'json'
|
8
7
|
require 'time'
|
9
8
|
require 'cgi'
|
10
9
|
require 'fileutils'
|
11
10
|
require 'ostruct'
|
11
|
+
require 'delegate'
|
12
|
+
require 'digest'
|
13
|
+
require 'observer'
|
12
14
|
|
13
15
|
require 'mustache'
|
14
16
|
|
15
17
|
require 'ruhoh/logger'
|
16
18
|
require 'ruhoh/utils'
|
17
19
|
require 'ruhoh/friend'
|
18
|
-
|
19
|
-
require 'ruhoh/
|
20
|
-
require 'ruhoh/
|
21
|
-
require 'ruhoh/
|
22
|
-
require 'ruhoh/
|
23
|
-
require 'ruhoh/
|
24
|
-
require 'ruhoh/
|
25
|
-
require 'ruhoh/previewer'
|
26
|
-
require 'ruhoh/watch'
|
27
|
-
require 'ruhoh/program'
|
20
|
+
|
21
|
+
require 'ruhoh/converter'
|
22
|
+
require 'ruhoh/views/master_view'
|
23
|
+
require 'ruhoh/collections'
|
24
|
+
require 'ruhoh/cache'
|
25
|
+
require 'ruhoh/routes'
|
26
|
+
require 'ruhoh/programs/preview'
|
28
27
|
|
29
28
|
class Ruhoh
|
30
|
-
|
31
29
|
class << self
|
32
30
|
attr_accessor :log
|
33
|
-
attr_reader :
|
31
|
+
attr_reader :names, :root
|
34
32
|
end
|
35
|
-
|
36
|
-
|
33
|
+
|
34
|
+
attr_accessor :log, :env
|
35
|
+
attr_reader :config, :paths, :root, :base, :cache, :collections, :routes
|
36
|
+
|
37
37
|
Root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
38
|
-
|
39
|
-
:assets => 'assets',
|
40
|
-
:config_data => 'config.yml',
|
41
|
-
:compiled => 'compiled',
|
42
|
-
:dashboard_file => 'dash.html',
|
43
|
-
:layouts => 'layouts',
|
44
|
-
:media => 'media',
|
45
|
-
:pages => 'pages',
|
46
|
-
:partials => 'partials',
|
47
|
-
:plugins => 'plugins',
|
48
|
-
:posts => 'posts',
|
49
|
-
:javascripts => 'javascripts',
|
50
|
-
:scaffolds => 'scaffolds',
|
51
|
-
:site_data => 'site.yml',
|
52
|
-
:stylesheets => 'stylesheets',
|
53
|
-
:system => 'system',
|
54
|
-
:themes => 'themes',
|
55
|
-
:theme_config => 'theme.yml',
|
56
|
-
:widgets => 'widgets',
|
57
|
-
:widget_config => 'config.yml'
|
58
|
-
}
|
59
|
-
@names = OpenStruct.new(Names)
|
38
|
+
@log = Ruhoh::Logger.new
|
60
39
|
@root = Root
|
61
|
-
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@collections = Ruhoh::Collections.new(self)
|
43
|
+
@cache = Ruhoh::Cache.new(self)
|
44
|
+
@routes = Ruhoh::Routes.new(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
def master_view(pointer)
|
48
|
+
Ruhoh::Views::MasterView.new(self, pointer)
|
49
|
+
end
|
50
|
+
|
62
51
|
# Public: Setup Ruhoh utilities relative to the current application directory.
|
63
52
|
# Returns boolean on success/failure
|
64
|
-
def
|
65
|
-
self.
|
66
|
-
@
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
53
|
+
def setup(opts={})
|
54
|
+
self.class.log.log_file = opts[:log_file] if opts[:log_file] #todo
|
55
|
+
@base = opts[:source] ? opts[:source] : Dir.getwd
|
56
|
+
!!config
|
57
|
+
end
|
58
|
+
|
59
|
+
def collection(resource)
|
60
|
+
@collections.load(resource)
|
61
|
+
end
|
62
|
+
|
63
|
+
def config
|
64
|
+
config = Ruhoh::Utils.parse_yaml_file(@base, "config.yml") || {}
|
65
|
+
config['compiled'] = config['compiled'] ? File.expand_path(config['compiled']) : "compiled"
|
66
|
+
|
67
|
+
config['_root'] ||= {}
|
68
|
+
config['_root']['permalink'] ||= "/:relative_path/:filename"
|
69
|
+
config['_root']['paginator'] ||= {}
|
70
|
+
config['_root']['paginator']['url'] ||= "/index/"
|
71
|
+
config['_root']['rss'] ||= {}
|
72
|
+
config['_root']['rss']['url'] ||= "/"
|
73
|
+
|
74
|
+
config['base_path'] = config['base_path'].to_s.strip
|
75
|
+
if config['base_path'].empty?
|
76
|
+
config['base_path'] = '/'
|
77
|
+
else
|
78
|
+
config['base_path'] += "/" unless config['base_path'][-1] == '/'
|
79
|
+
end
|
80
|
+
|
81
|
+
@config = config
|
82
|
+
end
|
83
|
+
|
84
|
+
Paths = Struct.new(:base, :theme, :system, :compiled)
|
85
|
+
def setup_paths
|
86
|
+
@paths = Paths.new
|
87
|
+
@paths.base = @base
|
88
|
+
@paths.system = File.join(Ruhoh::Root, "system")
|
89
|
+
@paths.compiled = @config["compiled"]
|
90
|
+
|
91
|
+
theme = @config.find{ |resource, data| data['use'] == "theme" }
|
92
|
+
if theme
|
93
|
+
Ruhoh::Friend.say { plain "Using theme: \"#{theme[0]}\""}
|
94
|
+
@paths.theme = File.join(@base, theme[0])
|
95
|
+
end
|
96
|
+
|
97
|
+
@paths
|
98
|
+
end
|
99
|
+
|
100
|
+
# Default paths to the 3 levels of the cascade.
|
101
|
+
def cascade
|
102
|
+
a = [
|
103
|
+
{
|
104
|
+
"name" => "system",
|
105
|
+
"path" => paths.system
|
106
|
+
},
|
107
|
+
{
|
108
|
+
"name" => "base",
|
109
|
+
"path" => paths.base
|
110
|
+
}
|
111
|
+
]
|
112
|
+
a << {
|
113
|
+
"name" => "theme",
|
114
|
+
"path" => paths.theme
|
115
|
+
} if paths.theme
|
116
|
+
|
117
|
+
a
|
118
|
+
end
|
119
|
+
|
120
|
+
def setup_plugins
|
121
|
+
ensure_paths
|
122
|
+
|
123
|
+
enable_sprockets = @config['asset_pipeline']['enable'] rescue false
|
124
|
+
if enable_sprockets
|
125
|
+
Ruhoh::Friend.say { green "=> Oh boy! Asset pipeline enabled by way of sprockets =D" }
|
126
|
+
sprockets = Dir[File.join(@paths.system, "plugins", "sprockets", "**/*.rb")]
|
127
|
+
sprockets.each {|f| require f }
|
128
|
+
end
|
129
|
+
|
130
|
+
plugins = Dir[File.join(@base, "plugins", "**/*.rb")]
|
89
131
|
plugins.each {|f| require f } unless plugins.empty?
|
90
132
|
end
|
91
|
-
|
92
|
-
def
|
93
|
-
|
133
|
+
|
134
|
+
def env
|
135
|
+
@env || 'development'
|
136
|
+
end
|
137
|
+
|
138
|
+
def base_path
|
139
|
+
(env == 'production') ?
|
140
|
+
config['base_path'] :
|
141
|
+
'/'
|
142
|
+
end
|
143
|
+
|
144
|
+
# @config['base_path'] is assumed to be well-formed.
|
145
|
+
# Always remove trailing slash.
|
146
|
+
# Returns String - normalized url with prepended base_path
|
147
|
+
def to_url(*args)
|
148
|
+
url = base_path + args.join('/')
|
149
|
+
url = url.gsub(/\/{2,}/, '/')
|
150
|
+
(url == "/") ? url : url.chomp('/')
|
151
|
+
end
|
152
|
+
|
153
|
+
def relative_path(filename)
|
154
|
+
filename.gsub(Regexp.new("^#{@base}/"), '')
|
155
|
+
end
|
156
|
+
|
157
|
+
# Compile the ruhoh instance (save to disk).
|
158
|
+
# Note: This method recursively removes the target directory. Should there be a warning?
|
159
|
+
#
|
160
|
+
# Extending:
|
161
|
+
# TODO: Deprecate this functionality and come up with a 2.0-friendly interface.
|
162
|
+
# The Compiler module is a namespace for all compile "tasks".
|
163
|
+
# A "task" is a ruby Class that accepts @ruhoh instance via initialize.
|
164
|
+
# At compile time all classes in the Ruhoh::Compiler namespace are initialized and run.
|
165
|
+
# To add your own compile task simply namespace a class under Ruhoh::Compiler
|
166
|
+
# and provide initialize and run methods:
|
167
|
+
#
|
168
|
+
# class Ruhoh
|
169
|
+
# module Compiler
|
170
|
+
# class CustomTask
|
171
|
+
# def initialize(ruhoh)
|
172
|
+
# @ruhoh = ruhoh
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# def run
|
176
|
+
# # do something here
|
177
|
+
# end
|
178
|
+
# end
|
179
|
+
# end
|
180
|
+
# end
|
181
|
+
def compile
|
182
|
+
ensure_paths
|
183
|
+
Ruhoh::Friend.say { plain "Compiling for environment: '#{@env}'" }
|
184
|
+
FileUtils.rm_r @paths.compiled if File.exist?(@paths.compiled)
|
185
|
+
FileUtils.mkdir_p @paths.compiled
|
186
|
+
|
187
|
+
# Run the resource compilers
|
188
|
+
compilers = @collections.all
|
189
|
+
# Hack to ensure assets are processed first so post-processing logic reflects in the templates.
|
190
|
+
compilers.delete('stylesheets')
|
191
|
+
compilers.unshift('stylesheets')
|
192
|
+
compilers.delete('javascripts')
|
193
|
+
compilers.unshift('javascripts')
|
194
|
+
|
195
|
+
compilers.each do |name|
|
196
|
+
collection = collection(name)
|
197
|
+
next unless collection.compiler?
|
198
|
+
collection.load_compiler.run
|
199
|
+
end
|
200
|
+
|
201
|
+
# Run extra compiler tasks if available:
|
202
|
+
if Ruhoh.const_defined?(:Compiler)
|
203
|
+
Ruhoh::Compiler.constants.each {|c|
|
204
|
+
compiler = Ruhoh::Compiler.const_get(c)
|
205
|
+
next unless compiler.respond_to?(:new)
|
206
|
+
task = compiler.new(self)
|
207
|
+
next unless task.respond_to?(:run)
|
208
|
+
task.run
|
209
|
+
}
|
210
|
+
end
|
211
|
+
|
212
|
+
true
|
213
|
+
end
|
214
|
+
|
215
|
+
# Find a file in the base cascade directories
|
216
|
+
# @return[Hash, nil] a single file pointer
|
217
|
+
def find_file(key)
|
218
|
+
dict = _all_files
|
219
|
+
dict[key] || dict.values.find{ |a| key == a['id'].gsub(/.[^.]+$/, '') }
|
220
|
+
end
|
221
|
+
|
222
|
+
# Collect all files from the base bascade directories.
|
223
|
+
# @return[Hash] dictionary of file pointers
|
224
|
+
def _all_files
|
225
|
+
dict = {}
|
226
|
+
cascade.map{ |a| a['path'] }.each do |path|
|
227
|
+
FileUtils.cd(path) {
|
228
|
+
Dir["*"].each { |id|
|
229
|
+
next unless File.exist?(id) && FileTest.file?(id)
|
230
|
+
dict[id] = {
|
231
|
+
"id" => id,
|
232
|
+
"realpath" => File.realpath(id),
|
233
|
+
}
|
234
|
+
}
|
235
|
+
}
|
236
|
+
end
|
237
|
+
|
238
|
+
dict
|
239
|
+
end
|
240
|
+
|
241
|
+
def ensure_setup
|
242
|
+
return if @config && @paths
|
94
243
|
raise 'Ruhoh has not been fully setup. Please call: Ruhoh.setup'
|
95
|
-
end
|
96
|
-
|
97
|
-
def
|
98
|
-
return if
|
99
|
-
raise 'Ruhoh has not setup config. Please call: Ruhoh.setup'
|
100
|
-
end
|
101
|
-
|
102
|
-
def self.ensure_paths
|
103
|
-
return if Ruhoh.config && Ruhoh.paths
|
244
|
+
end
|
245
|
+
|
246
|
+
def ensure_paths
|
247
|
+
return if @config && @paths
|
104
248
|
raise 'Ruhoh has not setup paths. Please call: Ruhoh.setup'
|
105
|
-
end
|
106
|
-
|
107
|
-
def self.
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.collection(resource)
|
252
|
+
Collections.load(resource)
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.model(resource)
|
256
|
+
Collections.get_module_namespace_for(resource).const_get(:ModelView)
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,280 @@
|
|
1
|
+
module Ruhoh::Base
|
2
|
+
|
3
|
+
module Collectable
|
4
|
+
|
5
|
+
def self.included(klass)
|
6
|
+
klass.__send__(:attr_accessor, :resource_name, :master)
|
7
|
+
klass.__send__(:attr_reader, :ruhoh)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(ruhoh)
|
11
|
+
@ruhoh = ruhoh
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public API for finding a resource from this collection
|
15
|
+
# @param name_or_pointer [String, Hash]
|
16
|
+
# Hash - File pointer
|
17
|
+
# String - id (filename) with full extension, e.g: about-me.md
|
18
|
+
# String - name (filename) without the extension e.g: about-me
|
19
|
+
# Returns the first matched filename.
|
20
|
+
# See implementation for how match is determined.
|
21
|
+
# @param opts [Hash] Optional options
|
22
|
+
# opts[:all] - true to search all files as some may be invalid as resources
|
23
|
+
#
|
24
|
+
# @return[model, nil] the model is always wrapped in its view.
|
25
|
+
def find(name_or_pointer, opts={})
|
26
|
+
pointer = find_file(name_or_pointer, opts)
|
27
|
+
return nil unless pointer
|
28
|
+
|
29
|
+
@ruhoh.cache.get(pointer['realpath']) ||
|
30
|
+
@ruhoh.cache.set(pointer['realpath'], load_model_view(pointer))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public API
|
34
|
+
# @return[Hash] dictionary of models { "id" => Model }
|
35
|
+
def dictionary
|
36
|
+
dict = {}
|
37
|
+
files.values.each { |pointer|
|
38
|
+
dict.merge!({
|
39
|
+
pointer['id'] => find(pointer)
|
40
|
+
})
|
41
|
+
}
|
42
|
+
dict
|
43
|
+
end
|
44
|
+
|
45
|
+
def resource_name
|
46
|
+
@resource_name ||= self.class.name.split("::").pop
|
47
|
+
end
|
48
|
+
|
49
|
+
# Implemented via Observable module
|
50
|
+
# See http://ruby-doc.org/stdlib-1.9.3/libdoc/observer/rdoc/Observable.html
|
51
|
+
# Collection subscribes to its child models.
|
52
|
+
# #update is called on model #process.
|
53
|
+
# noop
|
54
|
+
def update(model_data)
|
55
|
+
end
|
56
|
+
|
57
|
+
# The default glob for finding files.
|
58
|
+
# Every file in all child directories.
|
59
|
+
def glob
|
60
|
+
"**/*"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Default paths to the 3 levels of the cascade.
|
64
|
+
def paths
|
65
|
+
Array(@ruhoh.cascade.map{|h| h["path"]}).map { |path|
|
66
|
+
collection_path = File.join(path, resource_name)
|
67
|
+
next unless File.directory?(collection_path)
|
68
|
+
|
69
|
+
collection_path
|
70
|
+
}.compact
|
71
|
+
end
|
72
|
+
|
73
|
+
# Does this resource have any valid paths to process?
|
74
|
+
# A valid path may exist on any of the cascade levels.
|
75
|
+
# False means there are no directories on any cascade level.
|
76
|
+
# @returns[Boolean]
|
77
|
+
def paths?
|
78
|
+
!paths.empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
def config
|
82
|
+
config = @ruhoh.config[resource_name] || {}
|
83
|
+
unless config.is_a?(Hash)
|
84
|
+
Ruhoh.log.error("'#{resource_name}' config key in config.yml" +
|
85
|
+
" is a #{config.class}; it needs to be a Hash (object).")
|
86
|
+
end
|
87
|
+
config
|
88
|
+
end
|
89
|
+
|
90
|
+
# NOOP
|
91
|
+
# touch a model.
|
92
|
+
# Used to perform custom regeneration logic against a model.
|
93
|
+
def touch(name_or_pointer)
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param key [String, Hash]
|
97
|
+
# String - id (filename) with full extension, e.g: about-me.md
|
98
|
+
# String - name (filename) without the extension e.g: about-me
|
99
|
+
# Returns the first matched filename.
|
100
|
+
# See implementation for how match is determined.
|
101
|
+
# Hash - File pointer
|
102
|
+
#
|
103
|
+
# @param opts [Hash] Optional options
|
104
|
+
# opts[:all] - true to search all files as some may be invalid as resources
|
105
|
+
#
|
106
|
+
# @return [pointer, nil]
|
107
|
+
def find_file(key, opts={})
|
108
|
+
return key if key.is_a?(Hash) # assume valid pointer
|
109
|
+
|
110
|
+
dict = opts[:all] ? _all_files : files
|
111
|
+
|
112
|
+
dict[key] || dict.values.find{ |a| key == a['id'].gsub(/.[^.]+$/, '') }
|
113
|
+
end
|
114
|
+
|
115
|
+
# Collect all files (as mapped by data resources) for this data endpoint.
|
116
|
+
# Each resource can have 3 file references, one per each cascade level.
|
117
|
+
# The file hashes are collected in order
|
118
|
+
# so they will overwrite eachother if found.
|
119
|
+
|
120
|
+
# @param id [String, Array] Optional.
|
121
|
+
# Collect all files for a single data resource.
|
122
|
+
# Can be many files due to the cascade.
|
123
|
+
# @param [block] Optional.
|
124
|
+
# Implement custom validation logic by passing in a block.
|
125
|
+
# The block is given (id, self) as args.
|
126
|
+
# Return true/false for whether the file is valid/invalid.
|
127
|
+
#
|
128
|
+
# @return[Hash] dictionary of pointers.
|
129
|
+
def files(id=nil, &block)
|
130
|
+
return @ruhoh.cache.get(files_cache_key) if @ruhoh.cache.get(files_cache_key)
|
131
|
+
|
132
|
+
dict = _all_files
|
133
|
+
dict.keep_if do |id, pointer|
|
134
|
+
block_given? ? yield(id, self) : valid_file?(id)
|
135
|
+
end
|
136
|
+
|
137
|
+
@ruhoh.cache.set(files_cache_key, dict)
|
138
|
+
dict
|
139
|
+
end
|
140
|
+
|
141
|
+
# Collect all files within this collection, valid or otherwise.
|
142
|
+
# Each resource can have 3 file references, one per each cascade level.
|
143
|
+
# The file hashes are collected in order and overwrite eachother if found.
|
144
|
+
# This is a low-level method, see #files for the public interface.
|
145
|
+
#
|
146
|
+
# @return[Hash] dictionary of pointers.
|
147
|
+
def _all_files
|
148
|
+
dict = {}
|
149
|
+
paths.each do |path|
|
150
|
+
FileUtils.cd(path) {
|
151
|
+
Dir[glob].each { |id|
|
152
|
+
next unless File.exist?(id) && FileTest.file?(id)
|
153
|
+
dict[id] = {
|
154
|
+
"id" => id,
|
155
|
+
"realpath" => File.realpath(id),
|
156
|
+
"resource" => resource_name,
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
dict
|
163
|
+
end
|
164
|
+
|
165
|
+
def valid_file?(filepath)
|
166
|
+
return false if filepath.start_with?('.')
|
167
|
+
return false if filepath.start_with?('_')
|
168
|
+
excludes = Array(config['exclude']).map { |node| Regexp.new(node) }
|
169
|
+
excludes.each { |regex| return false if filepath =~ regex }
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
%w{
|
174
|
+
collection_view
|
175
|
+
model
|
176
|
+
model_view
|
177
|
+
client
|
178
|
+
compiler
|
179
|
+
watcher
|
180
|
+
previewer
|
181
|
+
}.each do |method_name|
|
182
|
+
define_method(method_name) do
|
183
|
+
get_module_namespace.const_get(camelize(method_name).to_sym)
|
184
|
+
end
|
185
|
+
|
186
|
+
define_method("#{method_name}?") do
|
187
|
+
get_module_namespace.const_defined?(camelize(method_name).to_sym)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def load_collection_view
|
192
|
+
@_collection_view ||= collection_view? ?
|
193
|
+
collection_view.new(self) :
|
194
|
+
self
|
195
|
+
end
|
196
|
+
|
197
|
+
def load_model(pointer)
|
198
|
+
_model = model? ?
|
199
|
+
model.new(@ruhoh, pointer) :
|
200
|
+
Ruhoh::Base::Model.new(@ruhoh, pointer)
|
201
|
+
_model.add_observer(self)
|
202
|
+
_model
|
203
|
+
end
|
204
|
+
|
205
|
+
def load_model_view(pointer)
|
206
|
+
model_view? ?
|
207
|
+
model_view.new(load_model(pointer)) :
|
208
|
+
Ruhoh::Base::ModelView.new(load_model(pointer))
|
209
|
+
end
|
210
|
+
|
211
|
+
def load_client(opts)
|
212
|
+
@_client ||= client.new(load_collection_view, opts)
|
213
|
+
end
|
214
|
+
|
215
|
+
def load_compiler
|
216
|
+
@_compiler ||= compiler.new(load_collection_view)
|
217
|
+
end
|
218
|
+
|
219
|
+
def load_watcher(*args)
|
220
|
+
@_watcher ||= watcher? ?
|
221
|
+
watcher.new(load_collection_view) :
|
222
|
+
Ruhoh::Base::Watcher.new(load_collection_view)
|
223
|
+
end
|
224
|
+
|
225
|
+
def load_previewer(*args)
|
226
|
+
@_previewer ||= previewer.new(@ruhoh)
|
227
|
+
end
|
228
|
+
|
229
|
+
def files_cache_key
|
230
|
+
"#{ resource_name }-files"
|
231
|
+
end
|
232
|
+
|
233
|
+
def scaffold
|
234
|
+
pointer = find_file('_scaffold', all: true) || @ruhoh.find_file('_scaffold')
|
235
|
+
return '' unless pointer
|
236
|
+
|
237
|
+
File.open(pointer['realpath'], 'r:UTF-8') { |f| f.read }
|
238
|
+
end
|
239
|
+
|
240
|
+
protected
|
241
|
+
|
242
|
+
# Load the registered resource else default to Pages if not configured.
|
243
|
+
# @returns[Constant] the resource's module namespace
|
244
|
+
def get_module_namespace
|
245
|
+
type = @ruhoh.config[resource_name]["use"] rescue nil
|
246
|
+
if type
|
247
|
+
if @ruhoh.collections.registered.include?(type)
|
248
|
+
Ruhoh::Resources.const_get(camelize(type))
|
249
|
+
elsif @ruhoh.collections.base.include?(type)
|
250
|
+
Ruhoh::Base.const_get(camelize(type))
|
251
|
+
else
|
252
|
+
klass = camelize(type)
|
253
|
+
Friend.say {
|
254
|
+
red "#{resource_name} resource set to use:'#{type}' in config.yml" +
|
255
|
+
" but Ruhoh::Resources::#{klass} does not exist."
|
256
|
+
}
|
257
|
+
abort
|
258
|
+
end
|
259
|
+
else
|
260
|
+
if @ruhoh.collections.registered.include?(resource_name)
|
261
|
+
Ruhoh::Resources.const_get(camelize(resource_name))
|
262
|
+
else
|
263
|
+
Ruhoh::Resources.const_get(:Pages)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def camelize(name)
|
269
|
+
name.to_s.split('_').map { |a| a.capitalize }.join
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# Generic base implementation of a Collection class.
|
274
|
+
# All collections use this class by default
|
275
|
+
# unless the Collection class is explicitly defined for the resource.
|
276
|
+
class Collection
|
277
|
+
include Collectable
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|