docwatch-bin 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
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: []