ro 1.4.6 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +6 -14
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +43 -0
  4. data/LICENSE +1 -0
  5. data/README.md +120 -112
  6. data/README.md.erb +159 -0
  7. data/Rakefile +129 -78
  8. data/bin/ro +241 -68
  9. data/lib/ro/_lib.rb +95 -0
  10. data/lib/ro/asset.rb +45 -0
  11. data/lib/ro/collection/list.rb +23 -0
  12. data/lib/ro/collection.rb +168 -0
  13. data/lib/ro/config.rb +68 -0
  14. data/lib/ro/error.rb +8 -0
  15. data/lib/ro/klass.rb +25 -0
  16. data/lib/ro/methods.rb +238 -0
  17. data/lib/ro/model.rb +83 -114
  18. data/lib/ro/node.rb +177 -360
  19. data/lib/ro/pagination.rb +7 -4
  20. data/lib/ro/path.rb +225 -0
  21. data/lib/ro/root.rb +52 -31
  22. data/lib/ro/script/builder.rb +221 -0
  23. data/lib/ro/script/console.rb +47 -0
  24. data/lib/ro/script/server.rb +76 -0
  25. data/lib/ro/script.rb +189 -0
  26. data/lib/ro/slug.rb +19 -18
  27. data/lib/ro/template/rouge_formatter.rb +42 -0
  28. data/lib/ro/template.rb +104 -50
  29. data/lib/ro.rb +85 -317
  30. data/public/api/ro/index-1.json +147 -0
  31. data/public/api/ro/index.json +137 -0
  32. data/public/api/ro/posts/first_post/index.json +52 -0
  33. data/public/api/ro/posts/index-1.json +145 -0
  34. data/public/api/ro/posts/index.json +135 -0
  35. data/public/api/ro/posts/second_post/index.json +51 -0
  36. data/public/api/ro/posts/third_post/index.json +51 -0
  37. data/public/ro/posts/first_post/assets/foo/bar/baz.jpg +0 -0
  38. data/public/ro/posts/first_post/assets/foo.jpg +0 -0
  39. data/public/ro/posts/first_post/assets/src/foo/bar.rb +3 -0
  40. data/public/ro/posts/first_post/attributes.yml +2 -0
  41. data/public/ro/posts/first_post/blurb.erb.md +7 -0
  42. data/public/ro/posts/first_post/body.md +16 -0
  43. data/public/ro/posts/first_post/testing.txt +3 -0
  44. data/public/ro/posts/second_post/assets/foo/bar/baz.jpg +0 -0
  45. data/public/ro/posts/second_post/assets/foo.jpg +0 -0
  46. data/public/ro/posts/second_post/assets/src/foo/bar.rb +3 -0
  47. data/public/ro/posts/second_post/attributes.yml +2 -0
  48. data/public/ro/posts/second_post/blurb.erb.md +5 -0
  49. data/public/ro/posts/second_post/body.md +16 -0
  50. data/public/ro/posts/third_post/assets/foo/bar/baz.jpg +0 -0
  51. data/public/ro/posts/third_post/assets/foo.jpg +0 -0
  52. data/public/ro/posts/third_post/assets/src/foo/bar.rb +3 -0
  53. data/public/ro/posts/third_post/attributes.yml +2 -0
  54. data/public/ro/posts/third_post/blurb.erb.md +5 -0
  55. data/public/ro/posts/third_post/body.md +16 -0
  56. data/ro.gemspec +89 -29
  57. metadata +106 -90
  58. data/TODO.md +0 -50
  59. data/lib/ro/blankslate.rb +0 -7
  60. data/lib/ro/cache.rb +0 -26
  61. data/lib/ro/git.rb +0 -374
  62. data/lib/ro/initializers/env.rb +0 -5
  63. data/lib/ro/initializers/tilt.rb +0 -104
  64. data/lib/ro/lock.rb +0 -53
  65. data/lib/ro/node/list.rb +0 -142
  66. 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
- Main {
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
- argument('root'){
6
- optional
7
- }
16
+ TL;DR;
17
+ ------
18
+ # boot a console for the ro a ./public/ro (the default)
19
+ #
20
+ ~> ro console
8
21
 
9
- def run
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
- mode(:console){
14
- def run
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
- basename = File.basename(Ro.root)
90
+ console!
91
+ end
26
92
 
27
- ARGV.clear
28
- require 'irb'
93
+ run(:build) do
94
+ setup!(:root, :build)
29
95
 
30
- Kernel.module_eval do
31
- def reload!
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
- $HACK = IRB.method(:load_modules)
99
+ run(:watch) do
100
+ setup!(:root, :build)
39
101
 
40
- def IRB.load_modules
41
- $HACK.call()
102
+ watch!
103
+ end
104
+
105
+ run(:server) do
106
+ setup!(:root, :build)
42
107
 
43
- basename = File.basename(Ro.root)
108
+ server!
109
+ end
44
110
 
45
- IRB.conf[:PROMPT][:RO] = {
46
- :PROMPT_I=>"Ro(./#{ basename }):%03n:%i> ",
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
- IRB.conf[:PROMPT_MODE] = :RO
54
- IRB.conf[:AUTO_INDENT] = true
55
- end
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
- ::IRB.start
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
- =begin
62
- module Console
63
- def reload!
64
- Object.send(:remove_const, :Ro)
65
- load "#{ $libdir }/ro.rb"
66
- "#{ $libdir }/ro.rb"
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
- class Console < Ro::BlankSlate
72
- def initialize(nodes)
73
- @nodes = nodes
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
- def binding
77
- Kernel.binding
78
- end
161
+ def setup_env!
162
+ argv.dup.each_with_index do |arg, i|
163
+ key, val = arg.split('=', 2)
79
164
 
80
- def reload!
81
- Object.send(:remove_const, :Ro)
82
- load "#{ $libdir }/ro.rb"
83
- "#{ $libdir }/ro.rb"
84
- end
165
+ if key && val
166
+ ENV[key.upcase] = val
85
167
 
86
- def method_missing(method, *args, &block)
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
- require 'main'
265
+ $stdout.sync = true
266
+ $stderr.sync = true
96
267
 
97
- $bindir = File.dirname(__FILE__)
98
- $libdir = File.expand_path("#{ $bindir }/../lib/")
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 "#{ $libdir }/ro.rb"
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