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/lib/ro/git.rb
DELETED
@@ -1,374 +0,0 @@
|
|
1
|
-
module Ro
|
2
|
-
class Git
|
3
|
-
attr_accessor :root
|
4
|
-
attr_accessor :branch
|
5
|
-
attr_accessor :patching
|
6
|
-
|
7
|
-
def initialize(root, options = {})
|
8
|
-
options = Map.for(options)
|
9
|
-
|
10
|
-
@root = root
|
11
|
-
@branch = options[:branch] || 'master'
|
12
|
-
end
|
13
|
-
|
14
|
-
# patch takes a block, allows abitrary edits (additions, modifications,
|
15
|
-
# deletions) to be performed by it, and then computes a single, atomic patch
|
16
|
-
# that is applied to the repo and pushed. the patch is returned. if the
|
17
|
-
# patch was not applied then patch.applied==false and it's up to client code
|
18
|
-
# to decide how to proceed, perhaps retrying or saving the patchfile for
|
19
|
-
# later manual application
|
20
|
-
#
|
21
|
-
def patch(*args, &block)
|
22
|
-
options = Map.options_for!(args)
|
23
|
-
|
24
|
-
user = options[:user] || ENV['USER'] || 'ro'
|
25
|
-
msg = options[:message] || "#{ user } edits on #{ File.basename(@root).inspect }"
|
26
|
-
add = options.has_key?(:add) ? options[:add] : true
|
27
|
-
|
28
|
-
patch = nil
|
29
|
-
|
30
|
-
Thread.exclusive do
|
31
|
-
@root.lock do
|
32
|
-
Dir.chdir(@root) do
|
33
|
-
# ensure .git-ness
|
34
|
-
#
|
35
|
-
status, stdout, stderr = spawn("git rev-parse --git-dir", :raise => true, :capture => true)
|
36
|
-
|
37
|
-
git_root = stdout.to_s.strip
|
38
|
-
|
39
|
-
dot_git = File.expand_path(git_root)
|
40
|
-
|
41
|
-
unless test(?d, dot_git)
|
42
|
-
raise Error.new("missing .git directory #{ dot_git }")
|
43
|
-
end
|
44
|
-
|
45
|
-
# calculate a tmp branch name
|
46
|
-
#
|
47
|
-
time = Coerce.time(options[:time] || Time.now).utc.iso8601(2).gsub(/[^\w]/, '')
|
48
|
-
branch = "#{ user }-#{ time }-#{ rand.to_s.gsub(/^0./, '') }"
|
49
|
-
|
50
|
-
# allow block to edit, compute the patch, attempt to apply it
|
51
|
-
#
|
52
|
-
begin
|
53
|
-
# get pristine
|
54
|
-
#
|
55
|
-
spawn("git checkout -f master", :raise => true)
|
56
|
-
spawn("git fetch --all", :raise => true)
|
57
|
-
spawn("git reset --hard origin/master", :raise => true)
|
58
|
-
|
59
|
-
# pull recent changes
|
60
|
-
#
|
61
|
-
trying('to pull'){ spawn("git pull origin master") }
|
62
|
-
|
63
|
-
# create a new temporary branch
|
64
|
-
#
|
65
|
-
spawn("git checkout -b #{ branch.inspect }", :raise => true)
|
66
|
-
|
67
|
-
# the block can perform arbitrary edits
|
68
|
-
#
|
69
|
-
block.call
|
70
|
-
|
71
|
-
# add all changes - additions, deletions, or modifications - unless :add => false was specified
|
72
|
-
#
|
73
|
-
if add
|
74
|
-
spawn("git add . --all", :raise => true)
|
75
|
-
end
|
76
|
-
|
77
|
-
# commit if anything changed
|
78
|
-
#
|
79
|
-
changes_to_apply =
|
80
|
-
spawn("git commit -m #{ msg.inspect }")
|
81
|
-
|
82
|
-
if changes_to_apply
|
83
|
-
# create the patch
|
84
|
-
#
|
85
|
-
status, stdout, stderr =
|
86
|
-
spawn("git format-patch master --stdout", :raise => true, :capture => true)
|
87
|
-
|
88
|
-
patch = Patch.new(:data => stdout, :name => branch)
|
89
|
-
|
90
|
-
unless stdout.to_s.strip.empty?
|
91
|
-
# apply the patch
|
92
|
-
#
|
93
|
-
spawn("git checkout master", :raise => true)
|
94
|
-
|
95
|
-
#
|
96
|
-
spawn("git rebase --abort")
|
97
|
-
spawn("git am --abort")
|
98
|
-
|
99
|
-
spawn("git am --abort")
|
100
|
-
spawn("git rebase --abort")
|
101
|
-
|
102
|
-
#
|
103
|
-
status, stdout, stderr =
|
104
|
-
spawn("git am --signoff --3way --ignore-space-change --ignore-whitespace", :capture => true, :stdin => patch.data)
|
105
|
-
|
106
|
-
#
|
107
|
-
patch.applied = !!(status == 0)
|
108
|
-
|
109
|
-
# commit the patch back to the repo
|
110
|
-
#
|
111
|
-
patch.committed =
|
112
|
-
begin
|
113
|
-
trying('to pull'){ spawn("git pull origin master") }
|
114
|
-
trying('to push'){ spawn("git push origin master") }
|
115
|
-
true
|
116
|
-
rescue Object
|
117
|
-
false
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
ensure
|
122
|
-
# get pristine
|
123
|
-
#
|
124
|
-
spawn("git checkout -f master", :raise => true)
|
125
|
-
spawn("git fetch --all", :raise => true)
|
126
|
-
spawn("git reset --hard origin/master", :raise => true)
|
127
|
-
|
128
|
-
spawn("git am --abort")
|
129
|
-
spawn("git rebase --abort")
|
130
|
-
|
131
|
-
# get changes
|
132
|
-
#
|
133
|
-
trying('to pull'){ spawn("git pull") }
|
134
|
-
|
135
|
-
# nuke the tmp branch
|
136
|
-
#
|
137
|
-
if patch and patch.applied and patch.committed
|
138
|
-
spawn("git branch -D #{ branch.inspect }")
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
patch
|
146
|
-
end
|
147
|
-
|
148
|
-
#
|
149
|
-
class Patch
|
150
|
-
fattr :data
|
151
|
-
fattr :name
|
152
|
-
fattr :applied
|
153
|
-
fattr :committed
|
154
|
-
fattr :status
|
155
|
-
fattr :stdout
|
156
|
-
fattr :stderr
|
157
|
-
|
158
|
-
def initialize(*args)
|
159
|
-
options = Map.options_for!(args)
|
160
|
-
|
161
|
-
self.class.fattrs.each do |key|
|
162
|
-
send(key, options.get(key)) if options.has?(key)
|
163
|
-
end
|
164
|
-
|
165
|
-
unless args.empty?
|
166
|
-
self.data = args.join
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def save(path)
|
171
|
-
return false unless data
|
172
|
-
path = path.to_s
|
173
|
-
FileUtils.mkdir_p(File.dirname(path))
|
174
|
-
IO.binwrite(path, data)
|
175
|
-
end
|
176
|
-
|
177
|
-
%w( to_s to_str ).each do |method|
|
178
|
-
class_eval <<-__, __FILE__, __LINE__
|
179
|
-
def #{ method }
|
180
|
-
data
|
181
|
-
end
|
182
|
-
__
|
183
|
-
end
|
184
|
-
|
185
|
-
%w( filename pathname basename ).each do |method|
|
186
|
-
class_eval <<-__, __FILE__, __LINE__
|
187
|
-
def #{ method }
|
188
|
-
name
|
189
|
-
end
|
190
|
-
__
|
191
|
-
end
|
192
|
-
|
193
|
-
%w( success success? applied applied? ).each do |method|
|
194
|
-
class_eval <<-__, __FILE__, __LINE__
|
195
|
-
def #{ method }
|
196
|
-
status && status == 0
|
197
|
-
end
|
198
|
-
__
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
def save(directory, options = {})
|
205
|
-
if directory.is_a?(Node)
|
206
|
-
directory = directory.path
|
207
|
-
end
|
208
|
-
|
209
|
-
options = Map.for(options)
|
210
|
-
|
211
|
-
dir = File.expand_path(directory.to_s)
|
212
|
-
|
213
|
-
relative_path = Ro.relative_path(dir, :from => @root)
|
214
|
-
|
215
|
-
exists = test(?d, dir)
|
216
|
-
|
217
|
-
action = exists ? 'edited' : 'created'
|
218
|
-
|
219
|
-
msg = options[:message] || "#{ ENV['USER'] } #{ action } #{ relative_path }"
|
220
|
-
|
221
|
-
@root.lock do
|
222
|
-
FileUtils.mkdir_p(dir) unless exists
|
223
|
-
|
224
|
-
Dir.chdir(dir) do
|
225
|
-
# .git
|
226
|
-
#
|
227
|
-
status, stdout, stderr = spawn("git rev-parse --git-dir", :raise => true, :capture => true)
|
228
|
-
|
229
|
-
git_root = stdout.to_s.strip
|
230
|
-
|
231
|
-
dot_git = File.expand_path(git_root)
|
232
|
-
|
233
|
-
unless test(?d, dot_git)
|
234
|
-
raise Error.new("missing .git directory #{ dot_git }")
|
235
|
-
end
|
236
|
-
|
237
|
-
# correct branch
|
238
|
-
#
|
239
|
-
spawn("git checkout #{ @branch.inspect }", :raise => true)
|
240
|
-
|
241
|
-
# return if nothing to do...
|
242
|
-
#
|
243
|
-
if `git status --porcelain`.strip.empty?
|
244
|
-
return true
|
245
|
-
end
|
246
|
-
|
247
|
-
# commit the work
|
248
|
-
#
|
249
|
-
trying "to commit" do
|
250
|
-
|
251
|
-
committed =
|
252
|
-
spawn("git add --all . && git commit -m #{ msg.inspect } -- .")
|
253
|
-
|
254
|
-
=begin
|
255
|
-
unless committed
|
256
|
-
spawn "git reset --hard"
|
257
|
-
end
|
258
|
-
=end
|
259
|
-
|
260
|
-
#require 'pry'
|
261
|
-
#binding.pry
|
262
|
-
=begin
|
263
|
-
retried = false
|
264
|
-
begin
|
265
|
-
spawn "git add --all . && git commit -m #{ msg.inspect } -- ."
|
266
|
-
committed = true
|
267
|
-
rescue
|
268
|
-
raise if retried
|
269
|
-
spawn "git reset --hard", :raise => false
|
270
|
-
retry
|
271
|
-
end
|
272
|
-
=end
|
273
|
-
end
|
274
|
-
|
275
|
-
|
276
|
-
trying "to push" do
|
277
|
-
pushed = nil
|
278
|
-
|
279
|
-
unless spawn("git push origin master")
|
280
|
-
# merge
|
281
|
-
#
|
282
|
-
unless spawn("git pull")
|
283
|
-
spawn("git checkout --ours -- .")
|
284
|
-
spawn("git add --all .")
|
285
|
-
spawn("git commit -F #{ dot_git }/MERGE_MSG")
|
286
|
-
else
|
287
|
-
raise 'wtf!?'
|
288
|
-
end
|
289
|
-
|
290
|
-
pushed = spawn("git push origin master")
|
291
|
-
else
|
292
|
-
pushed = true
|
293
|
-
end
|
294
|
-
|
295
|
-
pushed
|
296
|
-
end
|
297
|
-
|
298
|
-
=begin
|
299
|
-
git push
|
300
|
-
|
301
|
-
git pull
|
302
|
-
|
303
|
-
# publish
|
304
|
-
git checkout --ours -- .
|
305
|
-
git add --all .
|
306
|
-
git commit -F .git/MERGE_MSG
|
307
|
-
git push
|
308
|
-
=end
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
end
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
class Error < ::StandardError;end
|
317
|
-
|
318
|
-
def trying(*args, &block)
|
319
|
-
options = Map.options_for!(args)
|
320
|
-
label = ['trying', *args].join(' - ')
|
321
|
-
|
322
|
-
n = Integer(options[:n] || 3)
|
323
|
-
timeout = options[:timeout]
|
324
|
-
e = nil
|
325
|
-
done = nil
|
326
|
-
not_done = Object.new.freeze
|
327
|
-
|
328
|
-
result =
|
329
|
-
catch(:trying) do
|
330
|
-
n.times do |i|
|
331
|
-
done = block.call
|
332
|
-
if done
|
333
|
-
throw(:trying, done)
|
334
|
-
else
|
335
|
-
unless timeout == false
|
336
|
-
sleep( (i + 1) * (timeout || (1 + rand)) )
|
337
|
-
end
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
not_done
|
342
|
-
end
|
343
|
-
|
344
|
-
if result == not_done
|
345
|
-
raise(Error.new("#{ label } failed #{ n } times"))
|
346
|
-
else
|
347
|
-
done
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
|
-
def spawn(command, options = {})
|
352
|
-
options = Map.for(options)
|
353
|
-
|
354
|
-
status, stdout, stderr = systemu(command, :stdin => options[:stdin])
|
355
|
-
|
356
|
-
Ro.log(:debug, "command: #{ command }")
|
357
|
-
Ro.log(:debug, "status: #{ status }")
|
358
|
-
Ro.log(:debug, "stdout:\n#{ stdout }")
|
359
|
-
Ro.log(:debug, "stderr:\n#{ stderr }")
|
360
|
-
|
361
|
-
if options[:raise] == true
|
362
|
-
unless status == 0
|
363
|
-
raise "command (#{ command }) failed with #{ status }"
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
if options[:capture]
|
368
|
-
[status, stdout, stderr]
|
369
|
-
else
|
370
|
-
status == 0
|
371
|
-
end
|
372
|
-
end
|
373
|
-
end
|
374
|
-
end
|
data/lib/ro/initializers/env.rb
DELETED
data/lib/ro/initializers/tilt.rb
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'tilt'
|
2
|
-
|
3
|
-
module Tilt
|
4
|
-
class SyntaxHighlightingRedcarpetTemplate < Template
|
5
|
-
self.default_mime_type = 'text/html'
|
6
|
-
|
7
|
-
def self.engine_initialized?
|
8
|
-
defined?(::Redcarpet) && defined?(::Pygments) && defined?(::ERB)
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize_engine
|
12
|
-
require_template_library('redcarpet')
|
13
|
-
require_template_library('pygments')
|
14
|
-
require_template_library('erb')
|
15
|
-
end
|
16
|
-
|
17
|
-
def prepare
|
18
|
-
@engine =
|
19
|
-
Redcarpet::Markdown.new(
|
20
|
-
syntax_highlighting_renderer,
|
21
|
-
|
22
|
-
:no_intra_emphasis => true,
|
23
|
-
:tables => true,
|
24
|
-
:fenced_code_blocks => true,
|
25
|
-
:autolink => true,
|
26
|
-
:disable_indented_code_blocks => true,
|
27
|
-
:strikethrough => true,
|
28
|
-
:lax_spacing => true,
|
29
|
-
:space_after_headers => false,
|
30
|
-
:superscript => true,
|
31
|
-
:underline => true,
|
32
|
-
:highlight => true,
|
33
|
-
:quote => true,
|
34
|
-
|
35
|
-
:with_toc_data => true,
|
36
|
-
:hard_wrap => true,
|
37
|
-
)
|
38
|
-
|
39
|
-
@output = nil
|
40
|
-
end
|
41
|
-
|
42
|
-
def syntax_highlighting_renderer
|
43
|
-
Class.new(Redcarpet::Render::HTML) do
|
44
|
-
def block_code(code, language)
|
45
|
-
language = 'ruby' if language.to_s.strip.empty?
|
46
|
-
::Pygments.highlight(code, :lexer => language, :options => {:encoding => 'utf-8'})
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def evaluate(scope, locals, &block)
|
52
|
-
binding =
|
53
|
-
if scope.is_a?(::Binding)
|
54
|
-
scope
|
55
|
-
else
|
56
|
-
scope.instance_eval{ ::Kernel.binding }
|
57
|
-
end
|
58
|
-
|
59
|
-
@engine.render(erb(data, binding))
|
60
|
-
end
|
61
|
-
|
62
|
-
def erb(string, binding)
|
63
|
-
string
|
64
|
-
end
|
65
|
-
|
66
|
-
def allows_script?
|
67
|
-
true
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class ERBSyntaxHighlightingRedcarpetTemplate < SyntaxHighlightingRedcarpetTemplate
|
72
|
-
def erb(string, binding)
|
73
|
-
::ERB.new(string).result(binding)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
Tilt.prefer(Tilt::SyntaxHighlightingRedcarpetTemplate, 'md')
|
79
|
-
Tilt.prefer(Tilt::SyntaxHighlightingRedcarpetTemplate, 'markdown')
|
80
|
-
Tilt.prefer(Tilt::ERBSyntaxHighlightingRedcarpetTemplate, 'md.erb')
|
81
|
-
Tilt.prefer(Tilt::ERBSyntaxHighlightingRedcarpetTemplate, 'markdown.erb')
|
82
|
-
|
83
|
-
|
84
|
-
if $0 == __FILE__
|
85
|
-
|
86
|
-
markdown = <<-__
|
87
|
-
* one
|
88
|
-
* two
|
89
|
-
* <%= :three %>
|
90
|
-
* <%= @x %>
|
91
|
-
|
92
|
-
```rb
|
93
|
-
@a = 42
|
94
|
-
yield
|
95
|
-
```
|
96
|
-
__
|
97
|
-
|
98
|
-
template = Tilt['md.erb'].new{ markdown }
|
99
|
-
|
100
|
-
object = Object.new.instance_eval{ @x = :four; self }
|
101
|
-
|
102
|
-
puts template.render(object)
|
103
|
-
|
104
|
-
end
|
data/lib/ro/lock.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
module Ro
|
2
|
-
class Lock
|
3
|
-
def initialize(path)
|
4
|
-
@path = path.to_s
|
5
|
-
@fd = false
|
6
|
-
end
|
7
|
-
|
8
|
-
def lock(&block)
|
9
|
-
open!
|
10
|
-
|
11
|
-
if block
|
12
|
-
begin
|
13
|
-
lock!
|
14
|
-
block.call
|
15
|
-
ensure
|
16
|
-
unlock!
|
17
|
-
end
|
18
|
-
else
|
19
|
-
self
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def open!
|
24
|
-
@fd ||= (
|
25
|
-
fd =
|
26
|
-
begin
|
27
|
-
open(@path, 'ab+')
|
28
|
-
rescue
|
29
|
-
unless test(?e, @path)
|
30
|
-
FileUtils.mkdir_p(@path)
|
31
|
-
FileUtils.touch(@path)
|
32
|
-
end
|
33
|
-
|
34
|
-
open(@path, 'ab+')
|
35
|
-
end
|
36
|
-
|
37
|
-
fd.close_on_exec = true
|
38
|
-
|
39
|
-
fd
|
40
|
-
)
|
41
|
-
end
|
42
|
-
|
43
|
-
def lock!
|
44
|
-
open!
|
45
|
-
@fd.flock File::LOCK_EX
|
46
|
-
end
|
47
|
-
|
48
|
-
def unlock!
|
49
|
-
open!
|
50
|
-
@fd.flock File::LOCK_UN
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
data/lib/ro/node/list.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
module Ro
|
2
|
-
class Node
|
3
|
-
class List < ::Array
|
4
|
-
fattr :root
|
5
|
-
fattr :type
|
6
|
-
fattr :index
|
7
|
-
|
8
|
-
def initialize(*args, &block)
|
9
|
-
options = Map.options_for!(args)
|
10
|
-
|
11
|
-
root = args.shift || options[:root]
|
12
|
-
type = args.shift || options[:type]
|
13
|
-
|
14
|
-
@root = Root.new(root)
|
15
|
-
@type = type.nil? ? nil : String(type)
|
16
|
-
@index = {}
|
17
|
-
|
18
|
-
block.call(self) if block
|
19
|
-
end
|
20
|
-
|
21
|
-
def nodes
|
22
|
-
self
|
23
|
-
end
|
24
|
-
|
25
|
-
def load(path)
|
26
|
-
add( node = Node.new(path) )
|
27
|
-
end
|
28
|
-
|
29
|
-
def add(node)
|
30
|
-
return nil if node.nil?
|
31
|
-
|
32
|
-
unless index.has_key?(node.identifier)
|
33
|
-
push(node)
|
34
|
-
index[node.identifier] = node
|
35
|
-
node
|
36
|
-
else
|
37
|
-
false
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def related(*args, &block)
|
42
|
-
related = List.new(root)
|
43
|
-
|
44
|
-
each do |node|
|
45
|
-
node.related(*args, &block).each do |related_node|
|
46
|
-
related.add(related_node)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
related
|
51
|
-
end
|
52
|
-
|
53
|
-
def [](*args, &block)
|
54
|
-
key = args.first
|
55
|
-
|
56
|
-
case key
|
57
|
-
when String, Symbol
|
58
|
-
if @type.nil?
|
59
|
-
type = key.to_s
|
60
|
-
list = select{|node| type == node._type}
|
61
|
-
list.type = type
|
62
|
-
list
|
63
|
-
else
|
64
|
-
id = Slug.for(key.to_s)
|
65
|
-
detect{|node| id == node.id}
|
66
|
-
end
|
67
|
-
else
|
68
|
-
super(*args, &block)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def select(*args, &block)
|
73
|
-
List.new(root){|list| list.replace(super)}
|
74
|
-
end
|
75
|
-
|
76
|
-
def where(*args, &block)
|
77
|
-
case
|
78
|
-
when !args.empty? && block
|
79
|
-
raise ArgumentError.new
|
80
|
-
|
81
|
-
when args.empty? && block
|
82
|
-
select{|node| node.instance_eval(&block)}
|
83
|
-
|
84
|
-
when !args.empty?
|
85
|
-
ids = args.flatten.compact.uniq.map{|arg| Slug.for(arg.to_s)}
|
86
|
-
index = ids.inject(Hash.new){|h,id| h.update(id => id)}
|
87
|
-
select{|node| index[node.id]}
|
88
|
-
|
89
|
-
else
|
90
|
-
raise ArgumentError.new
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def find(*args, &block)
|
95
|
-
case
|
96
|
-
when !args.empty? && block
|
97
|
-
raise ArgumentError.new
|
98
|
-
when args.empty? && block
|
99
|
-
detect{|node| node.instance_eval(&block)}
|
100
|
-
|
101
|
-
when args.size == 1
|
102
|
-
id = args.first.to_s
|
103
|
-
detect{|node| node.id == id}
|
104
|
-
|
105
|
-
when args.size > 1
|
106
|
-
where(*args, &block)
|
107
|
-
|
108
|
-
else
|
109
|
-
raise ArgumentError.new
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def identifier
|
114
|
-
[root, type].compact.join('/')
|
115
|
-
end
|
116
|
-
|
117
|
-
include Pagination
|
118
|
-
|
119
|
-
def method_missing(method, *args, &block)
|
120
|
-
Ro.log "Ro::List(#{ identifier })#method_missing(#{ method.inspect }, #{ args.inspect })"
|
121
|
-
|
122
|
-
if @type.nil?
|
123
|
-
type = method.to_s
|
124
|
-
list = self[type]
|
125
|
-
super unless list
|
126
|
-
list.empty? ? super : list
|
127
|
-
else
|
128
|
-
node = self[Slug.for(method, :join => '-')] || self[Slug.for(method, :join => '_')]
|
129
|
-
node.nil? ? super : node
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def binding
|
134
|
-
Kernel.binding
|
135
|
-
end
|
136
|
-
|
137
|
-
def _binding
|
138
|
-
Kernel.binding
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|