docwatch-bin 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e525886a59746464f8ba5986e89bdcb343acbd1a96378bb56521f8e20bcf0288
4
+ data.tar.gz: a6829868d17db749392a11db8418519298e80bbfa386add54e2258bf72e59079
5
+ SHA512:
6
+ metadata.gz: e7e12d6b4188d3d29964d9c842bad38454c383aec54b2a55d38fe8ace8de68a21065abb7f1ade4cabe5ec88263d4790d1d3a8e63bccb40bd3bc4ee22f7095172
7
+ data.tar.gz: 270e381c2bbf7f2b28ecfbfccc407050a6aab5ed2c44342ba0f60a2c16d0227f00d33d8ba14be11e4820e27b60c085af83e039be2cd777fd7c54e9fb3bed641f
data/bin/docwatch ADDED
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/docwatch'
3
+ include Docwatch
4
+
5
+ DOCS = <<~EOF
6
+ #{'Usage:'.bold}
7
+ #{File.basename($0)} [options] [<file-path>]
8
+
9
+ #{'Options:'.bold}
10
+ -p, --port=VALUE Listen port [default: 8888]
11
+ -v, --verbose Verbose
12
+ -h, --help Help
13
+ -V, --version Version
14
+
15
+ #{'Renderers:'.bold}
16
+ markdown (.md)
17
+ html (.html)
18
+
19
+ If #{'--port'.bold} is #{'random'.bold} a random port will be chosen, otherwise
20
+ the specified one will be used. The default is 8888.
21
+
22
+ In #{'--verbose'.bold} mode, incoming HTTP requests and file change event
23
+ notifications will be printed to standard output.
24
+ EOF
25
+
26
+ class App
27
+ def initialize(opts)
28
+ @opts = opts
29
+ end
30
+
31
+ def self.run(opts)
32
+ new(opts).run
33
+ end
34
+
35
+ def params
36
+ @params ||= OpenStruct.new({
37
+ verbose: @opts['--verbose'],
38
+ version: @opts['--version'],
39
+ help: @opts['--help'],
40
+ file_path: @opts['<file-path>']
41
+ })
42
+ end
43
+
44
+ def port
45
+ @port ||= if @opts['--port'] == 'random'
46
+ # TODO: check if this port is in use
47
+ 10000 + Random.random_number(50000)
48
+ else
49
+ @opts['--port']
50
+ end
51
+ end
52
+
53
+ def url
54
+ 'http://localhost:%s' % port
55
+ end
56
+
57
+ def port_valid
58
+ true
59
+ end
60
+
61
+ def exit_usage
62
+ puts DOCS
63
+ exit 0
64
+ end
65
+
66
+ def exit_version
67
+ puts Docwatch::VERSION
68
+ exit 0
69
+ end
70
+
71
+ def exit_invalid_arguments
72
+ puts 'Invalid arguments'.red
73
+ exit 1
74
+ end
75
+
76
+ def exit_invalid_file
77
+ puts 'File does not exist: %s'.red % params.file_path
78
+ exit 2
79
+ end
80
+
81
+ def exit_unknown_renderer
82
+ puts 'No suitable renderer found for %s'.red % params.file_path
83
+ exit 3
84
+ end
85
+
86
+ def exit_invalid_port
87
+ puts 'Invalid port %s'.red % port
88
+ end
89
+
90
+ def run
91
+ exit_version if params.version
92
+ exit_usage if params.help
93
+ exit_invalid_arguments unless params.file_path
94
+ exit_invalid_file unless File.exist?(params.file_path)
95
+ exit_invalid_port unless port_valid
96
+
97
+ renderer = Renderer.by_filetype(params.file_path)
98
+ exit_unknown_renderer if renderer.nil?
99
+
100
+ server = TCPServer.new(port)
101
+ logger = Logger.new(params.verbose)
102
+ watcher = Watcher.new(params.file_path)
103
+
104
+ Thread.new do
105
+ while socket = server.accept
106
+ Thread.new do
107
+ Connection.handle(
108
+ renderer,
109
+ watcher,
110
+ logger,
111
+ Session.new(socket, logger)
112
+ )
113
+ end
114
+ end
115
+ end
116
+
117
+ puts ('Started server on %s, renderer %s' % [url, renderer.class]).yellow
118
+ puts 'Press enter to open page in the default handler (xdg-open)'.green
119
+
120
+ begin
121
+ while STDIN.gets
122
+ system('xdg-open %s' % url)
123
+ end
124
+ rescue Interrupt => e
125
+ puts
126
+ puts 'Bye'
127
+ end
128
+ end
129
+ end
130
+
131
+ begin
132
+ App.run(Docopt::docopt(DOCS, help: false))
133
+ rescue Docopt::Exit => e
134
+ puts 'Invalid arguments.'.red
135
+ exit 1
136
+ end
@@ -0,0 +1,24 @@
1
+ module Docwatch
2
+ class Connection
3
+ def initialize(renderer, watcher, logger, session)
4
+ @renderer = renderer
5
+ @watcher = watcher
6
+ @logger = logger
7
+ @session = session
8
+ end
9
+
10
+ def self.handle(*opts)
11
+ new(*opts).handle
12
+ end
13
+
14
+ def handle
15
+ case @session.path
16
+ when '/'
17
+ @session.respond_with_html @renderer.to_html
18
+ when '/wait'
19
+ @watcher.wait
20
+ @session.respond_with_text 'OK'
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Docwatch
2
+ class Logger
3
+ def initialize(verbose)
4
+ @verbose = verbose
5
+ end
6
+
7
+ def log(str)
8
+ puts str if @verbose
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module Docwatch
2
+ class HtmlRenderer < Renderer
3
+ extension :html
4
+
5
+ def doc
6
+ Nokogiri::HTML(contents)
7
+ end
8
+
9
+ def head
10
+ doc.at('head')&.inner_html
11
+ end
12
+
13
+ def body
14
+ doc.at('body')&.inner_html
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ module Docwatch
2
+ class MarkdownRenderer < Renderer
3
+ extension :md
4
+
5
+ def head
6
+ return <<~EOF
7
+ <title>#{file_path} - docwatch</title>
8
+ <style>
9
+ body {
10
+ font-family: Ubuntu;
11
+ font-size: 15px;
12
+ }
13
+ </style>
14
+ EOF
15
+ end
16
+
17
+ def body
18
+ Redcarpet::Markdown.new(
19
+ Redcarpet::Render::HTML,
20
+ fenced_code_blocks: true,
21
+ autolink: true,
22
+ tables: true,
23
+ footnotes: true,
24
+ ).render(contents)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ module Docwatch
2
+ class Renderer
3
+ @@extensions = {}
4
+
5
+ # Set an extension (by symbol) as being supported by this class.
6
+ def self.extension(sym)
7
+ (@@extensions[sym] ||= []) << self
8
+ end
9
+
10
+ def self.by_filetype(file_path)
11
+ extname = File.extname(file_path)[1..]
12
+ return if extname.length == 0
13
+ @@extensions[extname.to_sym].first.new(file_path)
14
+ end
15
+
16
+ def initialize(file_path)
17
+ @file_path = file_path
18
+ end
19
+
20
+ def js
21
+ File.read(Docwatch.root + '/res/inject.js')
22
+ end
23
+
24
+ def to_html
25
+ return <<~EOF
26
+ <!doctype html>
27
+ <html>
28
+ <head>
29
+ #{head}
30
+ </head>
31
+ <body>
32
+ #{body}
33
+ <script>
34
+ (function() {
35
+ #{js}
36
+ })()
37
+ </script>
38
+ </body>
39
+ </html>
40
+ EOF
41
+ end
42
+
43
+ protected
44
+
45
+ def file_path
46
+ @file_path
47
+ end
48
+
49
+ def contents
50
+ File.read(@file_path)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,54 @@
1
+ module Docwatch
2
+ class Session
3
+ def initialize(socket, logger)
4
+ @socket = socket
5
+ @logger = logger
6
+
7
+ logger.log first_request_line
8
+ end
9
+
10
+ def close
11
+ @socket.close
12
+ end
13
+
14
+ def path
15
+ first_request_line.split(' ')[1]
16
+ end
17
+
18
+ def respond_with_text(str)
19
+ respond_with(str, 'text/plain')
20
+ end
21
+
22
+ def respond_with_html(str)
23
+ respond_with(str, 'text/html')
24
+ end
25
+
26
+ def respond_with(str, content_type)
27
+ println 'HTTP/1.1 200'
28
+ println 'Content-Type: %s; charset=utf8' % content_type
29
+ # println 'Connection: close'
30
+ println
31
+ println str
32
+
33
+ close
34
+ end
35
+
36
+ private
37
+
38
+ def input_lines
39
+ @input_lines ||= @socket.recvmsg[0].lines rescue []
40
+ end
41
+
42
+ def first_request_line
43
+ input_lines.first.chomp
44
+ end
45
+
46
+ def print(msg)
47
+ @socket.print msg rescue nil
48
+ end
49
+
50
+ def println(msg = '')
51
+ print msg + "\r\n"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,3 @@
1
+ module Docwatch
2
+ VERSION = "1.0.8"
3
+ end
@@ -0,0 +1,16 @@
1
+ module Docwatch
2
+ class Watcher
3
+ def initialize(file_path)
4
+ @file_path = file_path
5
+ end
6
+
7
+ def mtime
8
+ File.mtime(@file_path)
9
+ end
10
+
11
+ def wait
12
+ now = mtime
13
+ sleep 0.2 while mtime <= now
14
+ end
15
+ end
16
+ end
data/lib/docwatch.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'require_all'
2
+ require 'colorize'
3
+ require 'docopt'
4
+ require 'redcarpet'
5
+ require 'nokogiri'
6
+
7
+ require 'socket'
8
+ require 'ostruct'
9
+
10
+ require_rel 'docwatch'
11
+
12
+ module Docwatch
13
+ def self.root
14
+ File.expand_path('../..', __FILE__)
15
+ end
16
+ end
data/res/inject.js ADDED
@@ -0,0 +1,10 @@
1
+ async function run() {
2
+ try {
3
+ await fetch('/wait')
4
+ document.location.reload()
5
+ } catch (e) {
6
+ setTimeout(run, 1000)
7
+ }
8
+ }
9
+
10
+ run()
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docwatch-bin
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.8
5
+ platform: ruby
6
+ authors:
7
+ - crdx
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-09-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: require_all
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: colorize
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.8.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.8.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: docopt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.6.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.6.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: redcarpet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.5'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.5'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.10'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.10'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.17.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.17.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.8'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.8'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '12.3'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '12.3'
125
+ description:
126
+ email:
127
+ executables:
128
+ - docwatch
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - bin/docwatch
133
+ - lib/docwatch.rb
134
+ - lib/docwatch/connection.rb
135
+ - lib/docwatch/logger.rb
136
+ - lib/docwatch/renderer.rb
137
+ - lib/docwatch/renderer/html.rb
138
+ - lib/docwatch/renderer/markdown.rb
139
+ - lib/docwatch/session.rb
140
+ - lib/docwatch/version.rb
141
+ - lib/docwatch/watcher.rb
142
+ - res/inject.js
143
+ homepage: https://github.com/crdx/docwatch
144
+ licenses:
145
+ - MIT
146
+ metadata: {}
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubygems_version: 3.0.6
163
+ signing_key:
164
+ specification_version: 4
165
+ summary: preview markdown documents in the browser with reload on change
166
+ test_files: []