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/lib/ro/script.rb ADDED
@@ -0,0 +1,189 @@
1
+ module Ro
2
+ def self.script(*args, &block)
3
+ Script.run!(*args, &block)
4
+ end
5
+
6
+ class Script
7
+ attr_accessor :cls, :env, :argv, :options, :mode, :stdout, :stdin, :stderr, :help
8
+
9
+ def run!(env = ENV, argv = ARGV)
10
+ initialize!(env, argv)
11
+ parse_command_line!
12
+ run_mode!
13
+ end
14
+
15
+ def initialize!(env, argv)
16
+ @cls = self.class
17
+ @env = env.to_hash.dup
18
+ @argv = argv.map { |arg| arg.dup }
19
+ @options = {}
20
+ @mode = nil
21
+ @stdout = $stdout.dup
22
+ @stdin = $stdin.dup
23
+ @stderr = $stderr.dup
24
+ @help = @cls.help
25
+ end
26
+
27
+ def parse_command_line!
28
+ argv = []
29
+ head = []
30
+ tail = []
31
+
32
+ %w[--].each do |stop|
33
+ next unless (i = @argv.index(stop))
34
+
35
+ head = @argv.slice(0...i)
36
+ tail = @argv.slice((i + 1)...@argv.size)
37
+ @argv = head
38
+ break
39
+ end
40
+
41
+ @argv.each do |arg|
42
+ if arg =~ /^\s*--([^\s-]+)=(.+)/
43
+ key = ::Regexp.last_match(1)
44
+ val = ::Regexp.last_match(2)
45
+ @options[key.to_sym] = val
46
+ elsif arg =~ /^\s*(-+)(.+)/
47
+ switch = ::Regexp.last_match(1)
48
+ key = ::Regexp.last_match(2)
49
+ val = switch.size.even?
50
+ @options[key.to_sym] = val
51
+ else
52
+ argv.push(arg)
53
+ end
54
+ end
55
+
56
+ argv += tail
57
+
58
+ @argv.replace(argv)
59
+
60
+ @mode = (@argv.shift if respond_to?("run_#{@argv[0]}!"))
61
+ end
62
+
63
+ def run_mode!
64
+ if @mode
65
+ send("run_#{@mode}!")
66
+ else
67
+ run
68
+ end
69
+ end
70
+
71
+ def run
72
+ help!
73
+ end
74
+
75
+ def run_help!
76
+ @stdout.puts(@help)
77
+ end
78
+
79
+ def help!
80
+ @stderr.puts(@help)
81
+ abort
82
+ end
83
+
84
+ def log(*messages, io: $stdout)
85
+ ts = Time.now.utc.iso8601
86
+ prefix = File.basename($0)
87
+ msg = messages.join("\n\s\s").strip
88
+
89
+ io.write "---\n[#{prefix}@#{ts}]\n\s\s#{msg}\n\n\n"
90
+
91
+ begin
92
+ io.flush
93
+ rescue StandardError
94
+ nil
95
+ end
96
+ end
97
+
98
+ def err(*messages)
99
+ log(*messages, io: @stderr)
100
+ end
101
+
102
+ @@SAY = {
103
+ clear: "\e[0m",
104
+ reset: "\e[0m",
105
+ erase_line: "\e[K",
106
+ erase_char: "\e[P",
107
+ bold: "\e[1m",
108
+ dark: "\e[2m",
109
+ underline: "\e[4m",
110
+ underscore: "\e[4m",
111
+ blink: "\e[5m",
112
+ reverse: "\e[7m",
113
+ concealed: "\e[8m",
114
+ black: "\e[30m",
115
+ red: "\e[31m",
116
+ green: "\e[32m",
117
+ yellow: "\e[33m",
118
+ blue: "\e[34m",
119
+ magenta: "\e[35m",
120
+ cyan: "\e[36m",
121
+ white: "\e[37m",
122
+ on_black: "\e[40m",
123
+ on_red: "\e[41m",
124
+ on_green: "\e[42m",
125
+ on_yellow: "\e[43m",
126
+ on_blue: "\e[44m",
127
+ on_magenta: "\e[45m",
128
+ on_cyan: "\e[46m",
129
+ on_white: "\e[47m"
130
+ }
131
+
132
+ def say(phrase, *args)
133
+ options = args.last.is_a?(Hash) ? args.pop : {}
134
+ options[:color] = args.shift.to_s.to_sym unless args.empty?
135
+ keys = options.keys
136
+ keys.each { |key| options[key.to_s.to_sym] = options.delete(key) }
137
+
138
+ color = options[:color]
139
+ bold = options.has_key?(:bold)
140
+
141
+ parts = [phrase]
142
+ parts.unshift(@@SAY[color]) if color
143
+ parts.unshift(@@SAY[:bold]) if bold
144
+ parts.push(@@SAY[:clear]) if parts.size > 1
145
+
146
+ method = options[:method] || :puts
147
+
148
+ if STDOUT.tty?
149
+ ::Kernel.send(method, parts.join)
150
+ else
151
+ ::Kernel.send(method, phrase)
152
+ end
153
+ end
154
+
155
+ def self.help(*args)
156
+ @help = args.join("\n") unless args.empty?
157
+ @help
158
+ end
159
+
160
+ def self.run(*args, &block)
161
+ modes =
162
+ if args.empty?
163
+ [nil]
164
+ else
165
+ args
166
+ end
167
+
168
+ modes.each do |mode|
169
+ method_name =
170
+ if mode
171
+ "run_#{mode}!"
172
+ else
173
+ 'run'
174
+ end
175
+
176
+ define_method(method_name, &block)
177
+ end
178
+ end
179
+
180
+ def self.run!(&block)
181
+ STDOUT.sync = true
182
+ STDERR.sync = true
183
+
184
+ %w[PIPE INT].each { |signal| Signal.trap(signal, 'EXIT') }
185
+
186
+ Class.new(Script, &block).new.run!(ENV, ARGV)
187
+ end
188
+ end
189
+ end
data/lib/ro/slug.rb CHANGED
@@ -1,27 +1,28 @@
1
1
  module Ro
2
2
  class Slug < ::String
3
- Join = '-'
3
+ @@JOIN = '-'
4
4
 
5
- def Slug.for(*args)
5
+ class << Slug
6
+ def for(arg, *args, &block)
7
+ return arg if arg.is_a?(Slug) && args.empty? && block.nil?
8
+ new(arg, *args, &block)
9
+ end
10
+ end
11
+
12
+ def initialize(*args)
6
13
  options = args.last.is_a?(Hash) ? args.pop : {}
7
- join = (options[:join]||options['join']||Join).to_s
14
+
15
+ join = (options[:join] || options['join'] || @@JOIN).to_s
16
+
8
17
  string = args.flatten.compact.join(join)
9
- string = unidecode(string)
10
- words = string.to_s.scan(%r|[/\w]+|)
11
- words.map!{|word| word.gsub %r|[_-]|, join}
12
- words.map!{|word| word.gsub %r|[^/0-9a-zA-Z_-]|, ''}
13
- words.delete_if{|word| word.nil? or word.strip.empty?}
14
- new(words.join(join).downcase.gsub('/', (join * 2)))
15
- end
18
+ words = string.to_s.scan(%r{[/\w]+})
19
+ words.map! { |word| word.gsub(/[_-]/, join) }
20
+ words.map! { |word| word.gsub %r{[^/0-9a-zA-Z_-]}, '' }
21
+ words.delete_if { |word| word.nil? or word.strip.empty? }
16
22
 
17
- unless defined?(Stringex::Unidecoder)
18
- def Slug.unidecode(string)
19
- string
20
- end
21
- else
22
- def Slug.unidecode(string)
23
- Stringex::Unidecoder.decode(string)
24
- end
23
+ slug = words.join(join).downcase.gsub('/', (join * 2))
24
+
25
+ super(slug)
25
26
  end
26
27
  end
27
28
  end
@@ -0,0 +1,42 @@
1
+ module Ro
2
+ class Template
3
+ require 'rouge'
4
+
5
+ class RougeFormatter < ::Rouge::Formatters::HTMLInline
6
+ def initialize(opts)
7
+ theme = if opts.is_a?(Hash)
8
+ opts[:theme] || opts['theme']
9
+ elsif opts.is_a?(Symbol)
10
+ opts.to_s
11
+ else
12
+ opts
13
+ end
14
+
15
+ super(theme)
16
+ end
17
+
18
+ def stream(tokens)
19
+ lineno = 1
20
+ yield "<code class='code' style='white-space:pre;'>"
21
+ token_lines(tokens).each do |line_tokens|
22
+ yield "<div class='code-line code-line-#{lineno}'>"
23
+ line_tokens.each do |token, value|
24
+ yield span(token, value)
25
+ end
26
+ yield "\n"
27
+ yield '</div>'
28
+ lineno += 1
29
+ end
30
+ yield '</code>'
31
+ end
32
+
33
+ def safe_span(tok, safe_val)
34
+ # return safe_val if tok == ::Rouge::Token::Tokens::Text
35
+ return safe_val.gsub(/\s/, '&nbsp;').gsub(/\n/, '<br />') if tok == ::Rouge::Token::Tokens::Text
36
+
37
+ rules = @theme.style_for(tok).rendered_rules
38
+ "<span style=\"#{rules.to_a.join(';')}\">#{safe_val}</span>"
39
+ end
40
+ end
41
+ end
42
+ end
data/lib/ro/template.rb CHANGED
@@ -1,77 +1,131 @@
1
1
  module Ro
2
2
  class Template
3
- def Template.render(path, node = nil)
4
- parts = File.basename(path).split('.')
5
- base = parts.shift
6
- exts = parts.reverse
3
+ require 'erb'
4
+ require 'rouge'
5
+ require 'kramdown'
6
+ require 'kramdown-parser-gfm'
7
+
8
+ require_relative 'template/rouge_formatter.rb'
9
+
10
+ class HTML < ::String
11
+ end
12
+
13
+ def self.render(*args, &block)
14
+ render_file(*args, &block)
15
+ end
16
+
17
+ def self.render_file(path, options = {})
18
+ path = File.expand_path(path.to_s.strip)
19
+ options = Map.for(options.is_a?(Hash) ? options : { context: options })
7
20
 
8
21
  content = IO.binread(path).force_encoding('utf-8')
22
+ engines = File.basename(path).split('.')[1..-1]
23
+ context = options[:context]
9
24
 
10
- loop do
11
- break if exts.empty?
12
- ext = exts.shift
25
+ render_string(content, path: path, engines: engines, context: context)
26
+ end
13
27
 
14
- case ext.to_s.downcase
15
- when 'erb', 'eruby'
16
- content = Ro.erb(content, node)
28
+ def self.render_string(content, options = {})
29
+ content = String(content).force_encoding('utf-8')
30
+ options = Map.for(options.is_a?(Hash) ? options : { context: options })
31
+ engines = Array(options.fetch(:engines) { ['md'] }).flatten.compact
32
+ path = options.fetch(:path) { '(string)' }
33
+ context = options[:context]
17
34
 
18
- when 'yml'
19
- content = YAML.load(content)
35
+ loop do
36
+ break if engines.empty?
20
37
 
21
- else
22
- tilt = Tilt[ext] || Tilt['txt']
38
+ engine = engines.shift.to_s.strip.downcase
23
39
 
24
- if tilt.nil?
40
+ content =
41
+ case engine
42
+ when 'txt', 'text', 'html'
25
43
  content
44
+ when 'erb'
45
+ render_erb(content, context:)
46
+ when 'md', 'markdown'
47
+ render_markdown(content)
48
+ when 'yml'
49
+ data = YAML.load(content)
50
+ pod = Ro.pod(data)
51
+ Ro.mapify(data)
52
+ when 'json'
53
+ data = JSON.parse(content)
54
+ pod = Ro.pod(data)
55
+ Ro.mapify(data)
56
+ when 'rb'
57
+ eval(content)
26
58
  else
27
- content = tilt.new{ content }.render(node)
28
- end
29
- end
59
+ Ro.error!("no engine found for engine=#{ engine.inspect } engines=#{ engines.inspect }")
60
+ end
30
61
  end
31
62
 
32
63
  content
33
64
  end
34
65
 
35
- def Template.render_source(path, node = nil)
36
- parts = File.basename(path).split('.')
37
- base = parts.shift
38
- exts = parts.reverse
66
+ def self.render_erb(content, options = {})
67
+ content = String(content).force_encoding('utf-8')
68
+ options = Map.for(options.is_a?(Hash) ? options : { context: options })
69
+ context = options[:context]
39
70
 
40
- content = IO.binread(path).force_encoding('utf-8')
71
+ erb = ERB.new(content, trim_mode: '%<>')
41
72
 
42
- loop do
43
- break if exts.empty?
44
- ext = exts.shift
45
-
46
- if exts.empty?
47
- code = content
48
- language = ext
49
- content =
50
- begin
51
- ::Pygments.highlight(code, :lexer => language, :options => {:encoding => 'utf-8'})
52
- rescue
53
- content
54
- end
73
+ html =
74
+ if context.respond_to?(:to_hash)
75
+ hash = context.to_hash
76
+ erb.result_with_hash(hash)
55
77
  else
56
- case ext.to_s.downcase
57
- when 'erb', 'eruby'
58
- content = Ro.erb(content, node)
59
- when 'yml'
60
- content = YAML.load(content)
61
- else
62
- tilt = Tilt[ext].new{ content }
63
- content = tilt.render(node)
64
- end
78
+ binding = context ? context.instance_eval{ binding } : ::Kernel.binding
79
+ erb.result(binding)
65
80
  end
66
- end
67
81
 
68
- content
82
+ HTML.new(html)
69
83
  end
70
84
 
71
- fattr :path
85
+ def self.render_markdown(content, options = {})
86
+ content = String(content).force_encoding('utf-8')
87
+ options = Map.for(options.is_a?(Hash) ? options : { context: options })
88
+
89
+ theme = options.fetch(:theme) { Ro.config.md_theme }
90
+
91
+ opts = {
92
+ input: 'GFM',
93
+ syntax_highlighter_opts: { formatter: RougeFormatter, theme: theme }
94
+ }
95
+
96
+ HTML.new(
97
+ <<~_____
98
+ <div class="ro markdown">
99
+ #{ ::Kramdown::Document.new(content, opts).to_html }
100
+ </div>
101
+ _____
102
+ )
103
+ end
104
+
105
+ def self.render_src(path, options = {})
106
+ path = File.expand_path(path.to_s.strip)
107
+ options = Map.for(options.is_a?(Hash) ? options : { context: options })
108
+
109
+ content = IO.binread(path).force_encoding('utf-8')
110
+ engines = File.basename(path).split('.')[1..-1].reverse
111
+ context = options[:context]
112
+
113
+ theme = options.fetch(:theme) { Ro.config.md_theme }
114
+ formatter = RougeFormatter.new(theme: theme)
115
+
116
+ language = engines.shift
117
+
118
+ lexer = Rouge::Lexer.find(language) || Ro.error!('no lexer found for ')
119
+
120
+ content = render_string(content, path: path, engines: engines, context: context) if engines.size.nonzero?
72
121
 
73
- def initialize(path)
74
- @path = Ro.realpath(path)
122
+ HTML.new(
123
+ <<~_____
124
+ <div class="ro markdown src">
125
+ #{ formatter.format(lexer.lex(content)) }
126
+ </div>
127
+ _____
128
+ )
75
129
  end
76
130
  end
77
131
  end