ro 1.4.6 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/Gemfile +2 -0
- data/Gemfile.lock +43 -0
- data/LICENSE +1 -0
- data/README.md +120 -112
- data/README.md.erb +159 -0
- data/Rakefile +129 -78
- data/bin/ro +241 -68
- data/lib/ro/_lib.rb +95 -0
- data/lib/ro/asset.rb +45 -0
- data/lib/ro/collection/list.rb +23 -0
- data/lib/ro/collection.rb +168 -0
- data/lib/ro/config.rb +68 -0
- data/lib/ro/error.rb +8 -0
- data/lib/ro/klass.rb +25 -0
- data/lib/ro/methods.rb +238 -0
- data/lib/ro/model.rb +83 -114
- data/lib/ro/node.rb +177 -360
- data/lib/ro/pagination.rb +7 -4
- data/lib/ro/path.rb +225 -0
- data/lib/ro/root.rb +52 -31
- data/lib/ro/script/builder.rb +221 -0
- data/lib/ro/script/console.rb +47 -0
- data/lib/ro/script/server.rb +76 -0
- data/lib/ro/script.rb +189 -0
- data/lib/ro/slug.rb +19 -18
- data/lib/ro/template/rouge_formatter.rb +42 -0
- data/lib/ro/template.rb +104 -50
- data/lib/ro.rb +85 -317
- data/public/api/ro/index-1.json +147 -0
- data/public/api/ro/index.json +137 -0
- data/public/api/ro/posts/first_post/index.json +52 -0
- data/public/api/ro/posts/index-1.json +145 -0
- data/public/api/ro/posts/index.json +135 -0
- data/public/api/ro/posts/second_post/index.json +51 -0
- data/public/api/ro/posts/third_post/index.json +51 -0
- data/public/ro/posts/first_post/assets/foo/bar/baz.jpg +0 -0
- data/public/ro/posts/first_post/assets/foo.jpg +0 -0
- data/public/ro/posts/first_post/assets/src/foo/bar.rb +3 -0
- data/public/ro/posts/first_post/attributes.yml +2 -0
- data/public/ro/posts/first_post/blurb.erb.md +7 -0
- data/public/ro/posts/first_post/body.md +16 -0
- data/public/ro/posts/first_post/testing.txt +3 -0
- data/public/ro/posts/second_post/assets/foo/bar/baz.jpg +0 -0
- data/public/ro/posts/second_post/assets/foo.jpg +0 -0
- data/public/ro/posts/second_post/assets/src/foo/bar.rb +3 -0
- data/public/ro/posts/second_post/attributes.yml +2 -0
- data/public/ro/posts/second_post/blurb.erb.md +5 -0
- data/public/ro/posts/second_post/body.md +16 -0
- data/public/ro/posts/third_post/assets/foo/bar/baz.jpg +0 -0
- data/public/ro/posts/third_post/assets/foo.jpg +0 -0
- data/public/ro/posts/third_post/assets/src/foo/bar.rb +3 -0
- data/public/ro/posts/third_post/attributes.yml +2 -0
- data/public/ro/posts/third_post/blurb.erb.md +5 -0
- data/public/ro/posts/third_post/body.md +16 -0
- data/ro.gemspec +89 -29
- metadata +106 -90
- data/TODO.md +0 -50
- data/lib/ro/blankslate.rb +0 -7
- data/lib/ro/cache.rb +0 -26
- data/lib/ro/git.rb +0 -374
- data/lib/ro/initializers/env.rb +0 -5
- data/lib/ro/initializers/tilt.rb +0 -104
- data/lib/ro/lock.rb +0 -53
- data/lib/ro/node/list.rb +0 -142
- data/notes/ara.txt +0 -215
data/bin/ro
CHANGED
@@ -1,102 +1,275 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
2
3
|
|
3
|
-
|
4
|
+
Ro.script do
|
5
|
+
help <<~____
|
6
|
+
NAME
|
7
|
+
----
|
8
|
+
ro
|
9
|
+
|
10
|
+
SYNOPSIS
|
11
|
+
--------
|
12
|
+
* boot an interactive r.e.p.l. over yer ro data
|
13
|
+
* build a json api for your ro data
|
14
|
+
* run a local dev server for your ro data
|
4
15
|
|
5
|
-
|
6
|
-
|
7
|
-
|
16
|
+
TL;DR;
|
17
|
+
------
|
18
|
+
# boot a console for the ro a ./public/ro (the default)
|
19
|
+
#
|
20
|
+
~> ro console
|
8
21
|
|
9
|
-
|
22
|
+
# build a static API into ./public/api/ro (the default)
|
23
|
+
#
|
24
|
+
~> ro build
|
25
|
+
|
26
|
+
# keep building that static a.p.i when any file changes
|
27
|
+
#
|
28
|
+
~> ro watch
|
29
|
+
|
30
|
+
# run a local http server that re-builds that static a.p.i on change
|
31
|
+
#
|
32
|
+
~> ro server
|
33
|
+
|
34
|
+
# show defaults
|
35
|
+
#
|
36
|
+
~> ro defaults
|
37
|
+
|
38
|
+
# show env
|
39
|
+
#
|
40
|
+
~> ro env
|
41
|
+
|
42
|
+
# show config
|
43
|
+
#
|
44
|
+
~> ro config
|
45
|
+
|
46
|
+
ENV
|
47
|
+
---
|
48
|
+
- all sub commands can be affect by the following env vars
|
49
|
+
- 12 factor ftw
|
50
|
+
|
51
|
+
- RO_ROOT
|
52
|
+
- the root directory of your ro data
|
53
|
+
- RO_BUILD
|
54
|
+
- the build directory for your ro data api
|
55
|
+
- RO_URL
|
56
|
+
- the url prefix where your ro data will be found on the interwebs
|
57
|
+
- you may use a relative or absolute url
|
58
|
+
- please make sure this will resolve
|
59
|
+
- RO_PAGE_SIZE
|
60
|
+
- the built a.p.i.'s page size
|
61
|
+
- RO_LOG
|
62
|
+
- log on, or log off
|
63
|
+
- RO_DEBUG
|
64
|
+
- debug or no debug
|
65
|
+
- RO_PORT
|
66
|
+
- le port to to serve on
|
67
|
+
|
68
|
+
ARGV
|
69
|
+
----
|
70
|
+
- for *all* sub commands
|
71
|
+
- you may also specifiy RO_ROOT as the 1'st argument
|
72
|
+
- you may also specifiy RO_BUILD as the 2'nd argument
|
73
|
+
- you may specify env vars in argv as 'RO_XXX=YYY' pairs, for example:
|
74
|
+
~ `ro console RO_ROOT=./public/ro`
|
75
|
+
- or
|
76
|
+
~ `ro console ro_root=./public/ro`
|
77
|
+
|
78
|
+
API
|
79
|
+
- the a.p.i is trivially simple, examine the output.
|
80
|
+
- if you can't figure out how to use it your probably should not.
|
81
|
+
____
|
82
|
+
|
83
|
+
run do
|
10
84
|
help!
|
11
85
|
end
|
12
86
|
|
13
|
-
|
14
|
-
|
15
|
-
if params['root'].given?
|
16
|
-
Ro.root = params['root'].value
|
17
|
-
else
|
18
|
-
if ENV['RO_ROOT']
|
19
|
-
Ro.root = ENV['RO_ROOT']
|
20
|
-
else
|
21
|
-
Ro.root = './ro'
|
22
|
-
end
|
23
|
-
end
|
87
|
+
run(:console) do
|
88
|
+
setup!(:root)
|
24
89
|
|
25
|
-
|
90
|
+
console!
|
91
|
+
end
|
26
92
|
|
27
|
-
|
28
|
-
|
93
|
+
run(:build) do
|
94
|
+
setup!(:root, :build)
|
29
95
|
|
30
|
-
|
31
|
-
|
32
|
-
Object.send(:remove_const, :Ro)
|
33
|
-
load "#{ $libdir }/ro.rb"
|
34
|
-
"#{ $libdir }/ro.rb"
|
35
|
-
end
|
36
|
-
end
|
96
|
+
build!
|
97
|
+
end
|
37
98
|
|
38
|
-
|
99
|
+
run(:watch) do
|
100
|
+
setup!(:root, :build)
|
39
101
|
|
40
|
-
|
41
|
-
|
102
|
+
watch!
|
103
|
+
end
|
104
|
+
|
105
|
+
run(:server) do
|
106
|
+
setup!(:root, :build)
|
42
107
|
|
43
|
-
|
108
|
+
server!
|
109
|
+
end
|
44
110
|
|
45
|
-
|
46
|
-
|
47
|
-
:PROMPT_N=>"Ro(./#{ basename }):%03n:%i> ",
|
48
|
-
:PROMPT_S=>"Ro(./#{ basename }):%03n:%i%l ",
|
49
|
-
:PROMPT_C=>"Ro(./#{ basename }):%03n:%i* ",
|
50
|
-
:RETURN=>"=> %s\n"
|
51
|
-
}
|
111
|
+
run(:defaults) do
|
112
|
+
setup!
|
52
113
|
|
53
|
-
|
54
|
-
|
55
|
-
|
114
|
+
defaults!
|
115
|
+
end
|
116
|
+
|
117
|
+
run(:env) do
|
118
|
+
setup!
|
119
|
+
|
120
|
+
env!
|
121
|
+
end
|
122
|
+
|
123
|
+
run(:config) do
|
124
|
+
setup!
|
125
|
+
|
126
|
+
config!
|
127
|
+
end
|
128
|
+
|
129
|
+
run(:site) do
|
130
|
+
site!
|
131
|
+
end
|
56
132
|
|
133
|
+
def setup!(*which)
|
134
|
+
setup_env!
|
57
135
|
|
58
|
-
|
136
|
+
parse_argv!(*which)
|
137
|
+
|
138
|
+
validate!
|
139
|
+
|
140
|
+
if which.include?(:ro)
|
141
|
+
@ro = Ro.root
|
59
142
|
end
|
143
|
+
end
|
60
144
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
145
|
+
attr_reader :ro
|
146
|
+
|
147
|
+
def parse_argv!(*which)
|
148
|
+
if which.include?(:root)
|
149
|
+
if argv.size > 0
|
150
|
+
Ro.config.root = argv.shift
|
67
151
|
end
|
68
152
|
end
|
69
|
-
=end
|
70
153
|
|
71
|
-
|
72
|
-
|
73
|
-
|
154
|
+
if which.include?(:build)
|
155
|
+
if argv.size > 0
|
156
|
+
Ro.config.build = argv.shift
|
74
157
|
end
|
158
|
+
end
|
159
|
+
end
|
75
160
|
|
76
|
-
|
77
|
-
|
78
|
-
|
161
|
+
def setup_env!
|
162
|
+
argv.dup.each_with_index do |arg, i|
|
163
|
+
key, val = arg.split('=', 2)
|
79
164
|
|
80
|
-
|
81
|
-
|
82
|
-
load "#{ $libdir }/ro.rb"
|
83
|
-
"#{ $libdir }/ro.rb"
|
84
|
-
end
|
165
|
+
if key && val
|
166
|
+
ENV[key.upcase] = val
|
85
167
|
|
86
|
-
|
87
|
-
@nodes.send(method, *args, &block)
|
168
|
+
argv.delete_at(i)
|
88
169
|
end
|
89
170
|
end
|
90
|
-
}
|
91
171
|
|
92
|
-
|
172
|
+
if ENV['PORT']
|
173
|
+
Ro.config.port = ENV['PORT']
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def validate!
|
178
|
+
abort("Ro.root = #{ Ro.config.root.expand } is not a directory") unless test(?d, Ro.config.root)
|
179
|
+
abort("Ro.root = #{ Ro.config.root.expand } is empty") unless test(?s, Ro.config.root)
|
180
|
+
end
|
181
|
+
|
182
|
+
def console!
|
183
|
+
require "#{$libdir}/ro/script/console.rb"
|
184
|
+
|
185
|
+
Ro::Script::Console.run!(script: self)
|
186
|
+
end
|
187
|
+
|
188
|
+
def build!
|
189
|
+
require "#{$libdir}/ro/script/builder.rb"
|
190
|
+
|
191
|
+
Ro::Script::Builder.run!(script: self)
|
192
|
+
end
|
193
|
+
|
194
|
+
def server!
|
195
|
+
require "#{$libdir}/ro/script/server.rb"
|
196
|
+
|
197
|
+
Ro::Script::Server.run!(script: self)
|
198
|
+
end
|
199
|
+
|
200
|
+
def watch!
|
201
|
+
watch(Ro.config.root) { build! }
|
202
|
+
end
|
203
|
+
|
204
|
+
def watch(directory, &block)
|
205
|
+
require 'ak47'
|
206
|
+
|
207
|
+
def File.exists?(...) # monkey patch for Ak47 ;-/
|
208
|
+
File.exist?(...)
|
209
|
+
end
|
210
|
+
|
211
|
+
Ak47(watch_dirs: directory) do
|
212
|
+
block.call
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def show(hash)
|
217
|
+
puts JSON.parse(hash.to_json).to_yaml
|
218
|
+
end
|
219
|
+
|
220
|
+
def defaults!
|
221
|
+
show Ro.defaults
|
222
|
+
end
|
223
|
+
|
224
|
+
def env!
|
225
|
+
show Ro.env
|
226
|
+
end
|
227
|
+
|
228
|
+
def config!
|
229
|
+
show Ro.config
|
230
|
+
end
|
231
|
+
|
232
|
+
def site!
|
233
|
+
document_root = Ro::Path.for(argv[0] || './public')
|
234
|
+
|
235
|
+
files = document_root.files.sort
|
236
|
+
hrefs = files.map{|file| file.relative_to(document_root).relative}
|
237
|
+
anchors = hrefs.map{|href| "<a href='./#{ href }' target='_blank'>./#{ href }</a>"}
|
238
|
+
lis = anchors.map{|anchor| "<li>#{ anchor }</li>"}
|
239
|
+
|
240
|
+
html = <<-____
|
241
|
+
<html>
|
242
|
+
<body style='padding:2em;'>
|
243
|
+
<br><br>
|
244
|
+
<strong>tl;dr;</strong>
|
245
|
+
<br><br>
|
246
|
+
<em>
|
247
|
+
see <a href="#{ Ro.repo }">#{ Ro.repo }</a> for moar deets
|
248
|
+
</em>
|
249
|
+
<br><br>
|
250
|
+
<hr><hr>
|
251
|
+
<ul>
|
252
|
+
#{ lis.join("\n") }
|
253
|
+
</ul>
|
254
|
+
</body>
|
255
|
+
</html>
|
256
|
+
____
|
257
|
+
|
258
|
+
index_html = document_root.join('index.html')
|
259
|
+
index_html.binwrite(html)
|
260
|
+
puts index_html
|
261
|
+
end
|
262
|
+
end
|
93
263
|
|
94
264
|
BEGIN {
|
95
|
-
|
265
|
+
$stdout.sync = true
|
266
|
+
$stderr.sync = true
|
96
267
|
|
97
|
-
$
|
98
|
-
$
|
268
|
+
$script = File.expand_path(__FILE__)
|
269
|
+
$bindir = File.dirname($script)
|
270
|
+
$root = File.dirname($bindir)
|
271
|
+
$libdir = File.join($root, 'lib')
|
99
272
|
|
100
|
-
require "#{
|
273
|
+
require "#{$libdir}/ro"
|
274
|
+
require "#{$libdir}/ro/script.rb"
|
101
275
|
}
|
102
|
-
|
data/lib/ro/_lib.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module Ro
|
2
|
+
VERSION = '4.2.0' unless defined?(VERSION)
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def version
|
6
|
+
VERSION
|
7
|
+
end
|
8
|
+
|
9
|
+
def repo
|
10
|
+
'https://github.com/ahoward/ro'
|
11
|
+
end
|
12
|
+
|
13
|
+
def summary
|
14
|
+
<<~____
|
15
|
+
all your content in github, as god intended
|
16
|
+
____
|
17
|
+
end
|
18
|
+
|
19
|
+
def description
|
20
|
+
<<~____
|
21
|
+
the worlds tiniest, bestest, most minmialist headless cms - powered by github
|
22
|
+
|
23
|
+
ro is a minimalist toolkit for managing heterogeneous collections of rich web
|
24
|
+
content on github, and providing both programatic and api access to it with zero
|
25
|
+
heavy lifting
|
26
|
+
____
|
27
|
+
end
|
28
|
+
|
29
|
+
def libs
|
30
|
+
%w[
|
31
|
+
fileutils pathname yaml json logger erb cgi rexml time date thread
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
def dependencies
|
36
|
+
{
|
37
|
+
'map' =>
|
38
|
+
['map', '~> 6.6', '>= 6.6.0'],
|
39
|
+
|
40
|
+
'kramdown' =>
|
41
|
+
['kramdown', '~> 2.4', ' >= 2.4.0'],
|
42
|
+
|
43
|
+
'kramdown-parser-gfm' =>
|
44
|
+
['kramdown-parser-gfm', '~> 1.1', ' >= 1.1.0'],
|
45
|
+
|
46
|
+
'rouge' =>
|
47
|
+
['rouge', '~> 4.1', ' >= 4.1.1'],
|
48
|
+
|
49
|
+
'ak47' =>
|
50
|
+
['ak47', '~> 0.2'],
|
51
|
+
|
52
|
+
'webrick' =>
|
53
|
+
['webrick', '~> 1.8.1'],
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def libdir(*args, &block)
|
58
|
+
@libdir ||= File.dirname(File.expand_path(__FILE__))
|
59
|
+
args.empty? ? @libdir : File.join(@libdir, *args)
|
60
|
+
ensure
|
61
|
+
if block
|
62
|
+
begin
|
63
|
+
$LOAD_PATH.unshift(@libdir)
|
64
|
+
block.call
|
65
|
+
ensure
|
66
|
+
$LOAD_PATH.shift
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def load(*libs)
|
72
|
+
libs = libs.join(' ').scan(/[^\s+]+/)
|
73
|
+
libdir { libs.each { |lib| Kernel.load(lib) } }
|
74
|
+
end
|
75
|
+
|
76
|
+
def load_dependencies!
|
77
|
+
libs.each do |lib|
|
78
|
+
require lib
|
79
|
+
end
|
80
|
+
|
81
|
+
begin
|
82
|
+
require 'rubygems'
|
83
|
+
rescue LoadError
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
has_rubygems = defined?(gem)
|
88
|
+
|
89
|
+
dependencies.each do |lib, dependency|
|
90
|
+
gem(*dependency) if has_rubygems
|
91
|
+
require(lib)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/ro/asset.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Ro
|
2
|
+
class Asset < ::String
|
3
|
+
include Klass
|
4
|
+
|
5
|
+
DEFAULT_IMAGE_PATTERNS = [
|
6
|
+
/[.](webp|jpg|jpeg|png|gif|tif|tiff|svg)$/i
|
7
|
+
]
|
8
|
+
|
9
|
+
def Asset.image_patterns
|
10
|
+
@image_patterns ||= DEFAULT_IMAGE_PATTERNS.dup
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :path, :node, :relative_path, :name, :url
|
14
|
+
|
15
|
+
def initialize(arg, *args)
|
16
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
17
|
+
|
18
|
+
@path = Path.for(arg, *args)
|
19
|
+
|
20
|
+
@node = options.fetch(:node) { Node.for(@path.split('/assets/').first) }
|
21
|
+
|
22
|
+
@relative_path = @path.relative_to(@node.path)
|
23
|
+
|
24
|
+
@name = @relative_path
|
25
|
+
|
26
|
+
@url = @node.url_for(@relative_path)
|
27
|
+
|
28
|
+
super(@path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def image?
|
32
|
+
@path.file? && Asset.image_patterns.any? { |pattern| pattern === @path.basename }
|
33
|
+
end
|
34
|
+
|
35
|
+
def src
|
36
|
+
key = relative_path.parts
|
37
|
+
subdir = key.size > 2 ? key[1] : nil
|
38
|
+
is_src = subdir == 'src'
|
39
|
+
|
40
|
+
return unless is_src
|
41
|
+
|
42
|
+
Ro.render_src(path, node)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ro
|
2
|
+
class Collection
|
3
|
+
class List < ::Array
|
4
|
+
include Klass
|
5
|
+
|
6
|
+
attr_reader :root
|
7
|
+
|
8
|
+
def initialize(root, ...)
|
9
|
+
@root = root
|
10
|
+
super(...)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get(name)
|
14
|
+
@root.get(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](index, ...)
|
18
|
+
return get(index) if [String, Symbol].include?(index.class)
|
19
|
+
super(index, ...)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module Ro
|
2
|
+
class Collection
|
3
|
+
include Klass
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :path, :root
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = Path.for(path)
|
10
|
+
@root = Root.for(@path.parent)
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
@path.name
|
15
|
+
end
|
16
|
+
|
17
|
+
def id
|
18
|
+
name
|
19
|
+
end
|
20
|
+
|
21
|
+
def type
|
22
|
+
name
|
23
|
+
end
|
24
|
+
|
25
|
+
def identifier
|
26
|
+
type
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
identifier
|
31
|
+
end
|
32
|
+
|
33
|
+
def node_for(path)
|
34
|
+
Node.new(path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def subdirectories(...)
|
38
|
+
@path.subdirectories(...)
|
39
|
+
end
|
40
|
+
|
41
|
+
def subdirectory_for(name)
|
42
|
+
@path.subdirectory_for(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def each(&block)
|
46
|
+
accum = []
|
47
|
+
|
48
|
+
subdirectories do |subdirectory|
|
49
|
+
node = node_for(subdirectory)
|
50
|
+
|
51
|
+
block ? block.call(node) : accum.push(node)
|
52
|
+
end
|
53
|
+
|
54
|
+
block ? self : accum
|
55
|
+
end
|
56
|
+
|
57
|
+
def load(&block)
|
58
|
+
n = 8
|
59
|
+
q = Queue.new # FIXME
|
60
|
+
o = Queue.new # FIXME
|
61
|
+
|
62
|
+
producer =
|
63
|
+
Thread.new do
|
64
|
+
Thread.current.abort_on_exception = true
|
65
|
+
|
66
|
+
subdirectories do |subdirectory|
|
67
|
+
q.push(subdirectory)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
loaders =
|
72
|
+
n.times.map do
|
73
|
+
Thread.new do
|
74
|
+
Thread.current.abort_on_exception = true
|
75
|
+
|
76
|
+
loop do
|
77
|
+
subdirectory = q.pop
|
78
|
+
|
79
|
+
begin
|
80
|
+
node = node_for(subdirectory)
|
81
|
+
o.push(node)
|
82
|
+
rescue => e
|
83
|
+
o.push(e) # FIXME
|
84
|
+
nil # FIXME
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
accum = []
|
91
|
+
|
92
|
+
consumer =
|
93
|
+
Thread.new do
|
94
|
+
Thread.current.abort_on_exception = true
|
95
|
+
loop do
|
96
|
+
node = o.pop
|
97
|
+
block ? block.call(node) : accum.push(node)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
producer.join
|
102
|
+
loaders.map{|loader| loader.join}
|
103
|
+
consumer.join
|
104
|
+
|
105
|
+
block ? self : accum
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_array(...)
|
109
|
+
each(...)
|
110
|
+
end
|
111
|
+
|
112
|
+
alias to_a to_array
|
113
|
+
|
114
|
+
alias all to_array
|
115
|
+
|
116
|
+
alias nodes to_array
|
117
|
+
|
118
|
+
def first(*args)
|
119
|
+
if args.size.zero?
|
120
|
+
node_for(subdirectories.first)
|
121
|
+
else
|
122
|
+
subdirectories.first(*args).map{|subdirectory| node_for(subdirectory)}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def last(*args)
|
127
|
+
if args.size.zero?
|
128
|
+
node_for(subdirectories.last)
|
129
|
+
else
|
130
|
+
subdirectories.last(*args).map{|subdirectory| node_for(subdirectory)}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def size
|
135
|
+
subdirectories.size
|
136
|
+
end
|
137
|
+
|
138
|
+
def paths_for(name)
|
139
|
+
[
|
140
|
+
subdirectory_for(name),
|
141
|
+
subdirectory_for(Slug.for(name, :join => '-')),
|
142
|
+
subdirectory_for(Slug.for(name, :join => '_')),
|
143
|
+
]
|
144
|
+
end
|
145
|
+
|
146
|
+
def get(name)
|
147
|
+
paths_for(name).each do |path|
|
148
|
+
next unless path.exist?
|
149
|
+
|
150
|
+
return node_for(path)
|
151
|
+
end
|
152
|
+
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
|
156
|
+
def [](name)
|
157
|
+
get(name)
|
158
|
+
end
|
159
|
+
|
160
|
+
def slice(...)
|
161
|
+
subdirectories.slice(...).map{|subdirectory| node_for(subdirectory)}
|
162
|
+
end
|
163
|
+
|
164
|
+
def method_missing(name, *args, **kws, &block)
|
165
|
+
get(name) || super
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|