mamemose 0.3.0 → 0.4.0
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.
- data/README.md +18 -1
- data/bin/mamemose +1 -1
- data/bin/mamemose_websocket +27 -0
- data/lib/mamemose/env.rb +19 -0
- data/lib/mamemose/path.rb +32 -0
- data/lib/mamemose/util.rb +5 -0
- data/lib/mamemose/version.rb +5 -1
- data/lib/mamemose/websocket.rb +108 -0
- data/lib/mamemose.rb +45 -64
- data/mamemose.gemspec +2 -1
- metadata +26 -4
data/README.md
CHANGED
@@ -50,7 +50,7 @@ $ gem install mamemose
|
|
50
50
|
パスを通す必要があるかも。
|
51
51
|
|
52
52
|
```bash
|
53
|
-
$ mamemose &> /dev/null &
|
53
|
+
$ nohup mamemose &> /dev/null &
|
54
54
|
```
|
55
55
|
|
56
56
|
するとローカルで HTTP サーバが立ち上がります。その後ブラウザから
|
@@ -69,6 +69,23 @@ http://localhost:PORT/
|
|
69
69
|
- 文字コードは UTF-8 で書くようにしてください。
|
70
70
|
- コマンドラインオプションは `mamemose help` で出ます。一応。
|
71
71
|
|
72
|
+
|
73
|
+
### 自動更新
|
74
|
+
|
75
|
+
WebSocket を使って自動更新できます。
|
76
|
+
mamemose サーバを立てた後アクセスされたファイルを監視しておき、
|
77
|
+
更新があればそのファイルを開いているブラウザのページを自動的にリロードします。
|
78
|
+
|
79
|
+
現在のところ、 WebSocket 用のサーバを別に立てておくという設計になっています。
|
80
|
+
以下のマンドを叩いて mamemose WebSocket サーバを起動しておいてください。
|
81
|
+
|
82
|
+
```bash
|
83
|
+
$ nohup mamemose_websocket &> /dev/null &
|
84
|
+
```
|
85
|
+
|
86
|
+
mamemose WebSocket サーバを立てなくても利用できます。
|
87
|
+
その場合は手動で更新してください。
|
88
|
+
|
72
89
|
### 一時ファイル閲覧
|
73
90
|
|
74
91
|
一時的に `DOCUMENT_ROOT` で指定したディレクトリ以外にあるファイルを
|
data/bin/mamemose
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
4
|
+
|
5
|
+
load 'mamemose/websocket.rb'
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'thor'
|
9
|
+
|
10
|
+
class Mamemose::WebSocket::CLI < Thor
|
11
|
+
desc "server", "run the mamemose websocket server"
|
12
|
+
def server
|
13
|
+
ws = Mamemose::WebSocket::Server.new
|
14
|
+
ws.start
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "s", "alias of server"
|
18
|
+
alias :s :server
|
19
|
+
|
20
|
+
desc "version", "print version"
|
21
|
+
def version
|
22
|
+
puts Mamemose::WebSocket::VERSION
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
args = ARGV == [] ? ["s"] : ARGV
|
27
|
+
Mamemose::WebSocket::CLI.start(args)
|
data/lib/mamemose/env.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
conf = File.expand_path("~" + File::SEPARATOR + ".mamemose.rb")
|
2
|
+
load conf if File.exists?(conf)
|
3
|
+
|
4
|
+
DOCUMENT_ROOT = "~/Dropbox/memo" if !defined?(DOCUMENT_ROOT)
|
5
|
+
PORT = 20000 if !defined?(PORT)
|
6
|
+
WS_PORT = 30000 if !defined?(WS_PORT)
|
7
|
+
RECENT_NUM = 10 if !defined?(RECENT_NUM)
|
8
|
+
RECENT_PATTERN = /.*/ if !defined?(RECENT_PATTERN)
|
9
|
+
IGNORE_FILES = ['.DS_Store','.AppleDouble','.LSOverride','Icon',/^\./,/~$/,
|
10
|
+
'.Spotlight-V100','.Trashes','Thumbs.db','ehthumbs.db',
|
11
|
+
'Desktop.ini','$RECYCLE.BIN',/^#/,'MathJax','syntaxhighlighter'] if !defined?(IGNORE_FILES)
|
12
|
+
MARKDOWN_PATTERN = /\.(md|markdown)$/ if !defined?(MARKDOWN_PATTERN)
|
13
|
+
INDEX_PATTERN = /^README/i if !defined?(INDEX_PATTERN)
|
14
|
+
CUSTOM_HEADER = '' if !defined?(CUSTOM_HEADER)
|
15
|
+
CUSTOM_BODY = '' if !defined?(CUSTOM_BODY)
|
16
|
+
CUSTOM_FOOTER = '' if !defined?(CUSTOM_FOOTER)
|
17
|
+
|
18
|
+
CONTENT_TYPE = "text/html; charset=utf-8"
|
19
|
+
DIR = File::expand_path(DOCUMENT_ROOT, '/')
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mamemose::Path
|
2
|
+
# returns escaped characters so that the markdown parser doesn't interpret it has special meaning.
|
3
|
+
def escape(text)
|
4
|
+
return text.gsub(/[\`*_{}\[\]()#+\-.!]/, "\\\\\\0")
|
5
|
+
end
|
6
|
+
|
7
|
+
# returns /-rooted path. eg. /path/to/my_document.md
|
8
|
+
def uri(path)
|
9
|
+
s = File::expand_path(path).gsub(DIR, "").gsub(File::SEPARATOR, '/')
|
10
|
+
return s == '' ? '/' : s
|
11
|
+
end
|
12
|
+
|
13
|
+
# returns fullpath. eg. /home/daimatz/Dropbox/memo/path/to/my_document.md
|
14
|
+
def fullpath(uri)
|
15
|
+
return File.join(DIR, uri.gsub(DIR, '').gsub('/', File::SEPARATOR))
|
16
|
+
end
|
17
|
+
|
18
|
+
# returns DOCUMENT_ROOT-rooted path. eg. ~/Dropbox/memo/path/to/my_document.md
|
19
|
+
def docpath(uri)
|
20
|
+
return File.join(DOCUMENT_ROOT, uri.gsub('/', File::SEPARATOR)).gsub(/#{File::SEPARATOR}$/, "")
|
21
|
+
end
|
22
|
+
|
23
|
+
# returns DOCUMENT_ROOT-rooted path, but escaped. eg. ~/Dropbox/memo/path/to/my\_document.md
|
24
|
+
# used in user-viewable (HTML) context.
|
25
|
+
def showpath(uri)
|
26
|
+
return escape(docpath(uri))
|
27
|
+
end
|
28
|
+
|
29
|
+
def escaped_basename(filename)
|
30
|
+
return escape(File::basename(filename))
|
31
|
+
end
|
32
|
+
end
|
data/lib/mamemose/version.rb
CHANGED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'em-websocket'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
require 'mamemose/version'
|
5
|
+
|
6
|
+
require 'mamemose/path'
|
7
|
+
require 'mamemose/util'
|
8
|
+
|
9
|
+
require 'mamemose/env'
|
10
|
+
|
11
|
+
class Mamemose::WebSocket::Server
|
12
|
+
include Mamemose::Util
|
13
|
+
|
14
|
+
@@update_send_message = "updated"
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@connections = []
|
18
|
+
@mutex = Mutex::new
|
19
|
+
@tag = "WebSocket"
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
Thread.new do
|
24
|
+
debug(@tag, "start watcher...")
|
25
|
+
watcher
|
26
|
+
end
|
27
|
+
|
28
|
+
EventMachine::WebSocket.start(:host => '0.0.0.0', :port => WS_PORT) do |ws|
|
29
|
+
ws.onopen {
|
30
|
+
debug(@tag, "connected.")
|
31
|
+
ws.send("connected.")
|
32
|
+
}
|
33
|
+
|
34
|
+
ws.onmessage { |fullpath|
|
35
|
+
# receive url from client
|
36
|
+
debug(@tag, "receive: #{fullpath}")
|
37
|
+
if File.exists?(fullpath)
|
38
|
+
# connections are managed as tuple of (socket, url, mtime_cache)
|
39
|
+
con = {:ws => ws, :fullpath => fullpath, :mtime_cache => get_mtime(fullpath)}
|
40
|
+
@mutex.synchronize do
|
41
|
+
@connections.push(con) unless @connections.index(con)
|
42
|
+
debug(@tag, "added path to watch: #{fullpath}. now watch #{fullpaths.to_s}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
}
|
46
|
+
|
47
|
+
ws.onclose {
|
48
|
+
debug(@tag, "closed.")
|
49
|
+
# when a connection is closed, delete it from @connections
|
50
|
+
@mutex.synchronize do
|
51
|
+
@connections.delete_if { |con| con[:ws] == ws }
|
52
|
+
debug(@tag, "closed and removed path. now watch #{fullpaths.to_s}")
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def watcher
|
59
|
+
loop do
|
60
|
+
# gather paths to watch using mutex
|
61
|
+
watch_fullpaths = []
|
62
|
+
@mutex.synchronize do
|
63
|
+
watch_fullpaths = fullpaths
|
64
|
+
end
|
65
|
+
|
66
|
+
# gather mtimes of watch_fullpaths
|
67
|
+
mtimes = {}
|
68
|
+
watch_fullpaths.uniq.each do |fullpath|
|
69
|
+
if File.exists?(fullpath)
|
70
|
+
# get mtime
|
71
|
+
mtimes[fullpath] = get_mtime(fullpath)
|
72
|
+
else
|
73
|
+
# file no longer exists. remove the entry
|
74
|
+
@mutex.synchronize do
|
75
|
+
@connections.delete_if { |con| con[:fullpath] == fullpath }
|
76
|
+
debug(@tag, "detected deletion: #{fullpath} and updated the list. now watch #{fullpaths.to_s}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# push notification if watching file is updated
|
82
|
+
to_notify = []
|
83
|
+
@mutex.synchronize do
|
84
|
+
@connections.each do |con|
|
85
|
+
fullpath = con[:fullpath]
|
86
|
+
if mtimes[fullpath] && con[:mtime_cache] < mtimes[fullpath]
|
87
|
+
debug(@tag, "detected update: #{fullpath}. pushing...")
|
88
|
+
con[:mtime_cache] = mtimes[fullpath]
|
89
|
+
to_notify << con
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
to_notify.each do |con|
|
94
|
+
con[:ws].send(@@update_send_message)
|
95
|
+
end
|
96
|
+
|
97
|
+
sleep 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def fullpaths
|
102
|
+
@connections.map{ |con| con[:fullpath] }
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_mtime(fullpath)
|
106
|
+
File.mtime(fullpath) if File.exists?(fullpath)
|
107
|
+
end
|
108
|
+
end
|
data/lib/mamemose.rb
CHANGED
@@ -5,26 +5,11 @@ require 'uri'
|
|
5
5
|
require 'redcarpet'
|
6
6
|
require 'htmlentities'
|
7
7
|
|
8
|
-
require
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
DOCUMENT_ROOT = "~/Dropbox/memo" if !defined?(DOCUMENT_ROOT)
|
14
|
-
PORT = 20000 if !defined?(PORT)
|
15
|
-
RECENT_NUM = 10 if !defined?(RECENT_NUM)
|
16
|
-
RECENT_PATTERN = /.*/ if !defined?(RECENT_PATTERN)
|
17
|
-
IGNORE_FILES = ['.DS_Store','.AppleDouble','.LSOverride','Icon',/^\./,/~$/,
|
18
|
-
'.Spotlight-V100','.Trashes','Thumbs.db','ehthumbs.db',
|
19
|
-
'Desktop.ini','$RECYCLE.BIN',/^#/,'MathJax','syntaxhighlighter'] if !defined?(IGNORE_FILES)
|
20
|
-
MARKDOWN_PATTERN = /\.(md|markdown)$/ if !defined?(MARKDOWN_PATTERN)
|
21
|
-
INDEX_PATTERN = /^README/i if !defined?(INDEX_PATTERN)
|
22
|
-
CUSTOM_HEADER = '' if !defined?(CUSTOM_HEADER)
|
23
|
-
CUSTOM_BODY = '' if !defined?(CUSTOM_BODY)
|
24
|
-
CUSTOM_FOOTER = '' if !defined?(CUSTOM_FOOTER)
|
25
|
-
|
26
|
-
CONTENT_TYPE = "text/html; charset=utf-8"
|
27
|
-
DIR = File::expand_path(DOCUMENT_ROOT, '/')
|
8
|
+
require 'mamemose/version'
|
9
|
+
|
10
|
+
require 'mamemose/path'
|
11
|
+
|
12
|
+
require 'mamemose/env'
|
28
13
|
|
29
14
|
class HTMLwithSyntaxHighlighter < Redcarpet::Render::XHTML
|
30
15
|
def block_code(code, lang)
|
@@ -35,21 +20,28 @@ class HTMLwithSyntaxHighlighter < Redcarpet::Render::XHTML
|
|
35
20
|
end
|
36
21
|
|
37
22
|
class Mamemose::Server
|
23
|
+
include Mamemose::Path
|
24
|
+
|
38
25
|
def initialize(port)
|
39
|
-
@
|
40
|
-
trap(:INT){
|
41
|
-
trap(:TERM){
|
26
|
+
@mamemose = WEBrick::HTTPServer.new({ :Port => port ? port.to_i : PORT })
|
27
|
+
trap(:INT){finalize}
|
28
|
+
trap(:TERM){finalize}
|
42
29
|
end
|
43
30
|
|
44
31
|
def start
|
45
|
-
@
|
32
|
+
@mamemose.start
|
33
|
+
end
|
34
|
+
|
35
|
+
def server
|
36
|
+
@mamemose.mount_proc('/') do |req, res|
|
46
37
|
res['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
47
38
|
res['Pragma'] = 'no-cache'
|
48
39
|
res['Expires'] = '0'
|
49
40
|
|
41
|
+
p fullpath(req.path)
|
50
42
|
if req.path =~ /^\/search/
|
51
43
|
res = req_search(req, res)
|
52
|
-
elsif File.directory?(fullpath(req.path))
|
44
|
+
elsif File.directory?(fullpath(req.path))
|
53
45
|
res = req_index(req, res)
|
54
46
|
elsif File.exists?(fullpath(req.path))
|
55
47
|
res = req_file(fullpath(req.path), res, false)
|
@@ -57,25 +49,28 @@ class Mamemose::Server
|
|
57
49
|
res.status = WEBrick::HTTPStatus::RC_NOT_FOUND
|
58
50
|
end
|
59
51
|
end
|
60
|
-
|
61
|
-
@server.start
|
52
|
+
start
|
62
53
|
end
|
63
54
|
|
64
55
|
def file(filename)
|
65
|
-
@
|
56
|
+
@mamemose.mount_proc('/') do |req, res|
|
66
57
|
res['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
67
58
|
res['Pragma'] = 'no-cache'
|
68
59
|
res['Expires'] = '0'
|
69
60
|
res = req_file(File.absolute_path(filename), res, true)
|
70
61
|
res.content_type = CONTENT_TYPE
|
71
62
|
end
|
72
|
-
|
73
|
-
@server.start
|
63
|
+
start
|
74
64
|
end
|
75
65
|
|
76
66
|
private
|
77
67
|
|
78
|
-
def
|
68
|
+
def finalize
|
69
|
+
Thread::list.each {|t| Thread::kill(t) if t != Thread::current}
|
70
|
+
@mamemose.shutdown
|
71
|
+
end
|
72
|
+
|
73
|
+
def header_html(title, fullpath)
|
79
74
|
html = <<HTML
|
80
75
|
<!DOCTYPE HTML>
|
81
76
|
<html>
|
@@ -221,6 +216,23 @@ function copy(text) {
|
|
221
216
|
prompt("Copy filepath below:", text);
|
222
217
|
}
|
223
218
|
</script>
|
219
|
+
<script>
|
220
|
+
(function(){
|
221
|
+
var fullpath = "#{fullpath}";
|
222
|
+
ws = new WebSocket("ws://localhost:#{WS_PORT}");
|
223
|
+
ws.onopen = function() {
|
224
|
+
console.log("WebSocket (port=#{WS_PORT}) connected: " + fullpath);
|
225
|
+
ws.send(fullpath);
|
226
|
+
};
|
227
|
+
ws.onmessage = function(evt) {
|
228
|
+
console.log("received: " + evt.data);
|
229
|
+
if (evt.data == "updated") {
|
230
|
+
console.log("update detected. reloading...");
|
231
|
+
location.reload();
|
232
|
+
}
|
233
|
+
};
|
234
|
+
})();
|
235
|
+
</script>
|
224
236
|
#{CUSTOM_HEADER}
|
225
237
|
</head>
|
226
238
|
<body>
|
@@ -288,7 +300,7 @@ HTML
|
|
288
300
|
value.each {|v| body += link_list(v[0], v[1])}
|
289
301
|
end
|
290
302
|
|
291
|
-
res.body = header_html(title,
|
303
|
+
res.body = header_html(title, path)\
|
292
304
|
+ search_form(uri(path), q)\
|
293
305
|
+ markdown(body)\
|
294
306
|
+ footer_html
|
@@ -321,7 +333,7 @@ HTML
|
|
321
333
|
body += File.read(index)
|
322
334
|
end
|
323
335
|
|
324
|
-
res.body = header_html(title,
|
336
|
+
res.body = header_html(title, directory)\
|
325
337
|
+ search_form(uri(req.path))\
|
326
338
|
+ markdown(body)\
|
327
339
|
+ footer_html(index)
|
@@ -407,37 +419,6 @@ HTML
|
|
407
419
|
return {:dirs=>dirs, :markdowns=>markdowns, :others=>others}
|
408
420
|
end
|
409
421
|
|
410
|
-
# returns escaped characters so that the markdown parser doesn't interpret it has special meaning.
|
411
|
-
def escape(text)
|
412
|
-
return text.gsub(/[\`*_{}\[\]()#+\-.!]/, "\\\\\\0")
|
413
|
-
end
|
414
|
-
|
415
|
-
# returns /-rooted path. eg. /path/to/my_document.md
|
416
|
-
def uri(path)
|
417
|
-
s = File::expand_path(path).gsub(DIR, "").gsub(File::SEPARATOR, '/')
|
418
|
-
return s == '' ? '/' : s
|
419
|
-
end
|
420
|
-
|
421
|
-
# returns fullpath. eg. /home/daimatz/Dropbox/memo/path/to/my_document.md
|
422
|
-
def fullpath(uri)
|
423
|
-
return File.join(DIR, uri.gsub('/', File::SEPARATOR))
|
424
|
-
end
|
425
|
-
|
426
|
-
# returns DOCUMENT_ROOT-rooted path. eg. ~/Dropbox/memo/path/to/my_document.md
|
427
|
-
def docpath(uri)
|
428
|
-
return File.join(DOCUMENT_ROOT, uri.gsub('/', File::SEPARATOR)).gsub(/#{File::SEPARATOR}$/, "")
|
429
|
-
end
|
430
|
-
|
431
|
-
# returns DOCUMENT_ROOT-rooted path, but escaped. eg. ~/Dropbox/memo/path/to/my\_document.md
|
432
|
-
# used in user-viewable (HTML) context.
|
433
|
-
def showpath(uri)
|
434
|
-
return escape(docpath(uri))
|
435
|
-
end
|
436
|
-
|
437
|
-
def escaped_basename(filename)
|
438
|
-
return escape(File::basename(filename))
|
439
|
-
end
|
440
|
-
|
441
422
|
def link_list(title, link)
|
442
423
|
file = fullpath(link)
|
443
424
|
str = filesize(file)
|
data/mamemose.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mamemose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redcarpet
|
@@ -50,7 +50,7 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - ! '>='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 0.17.0
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -58,12 +58,29 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.17.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: em-websocket
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.5.0
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.5.0
|
62
78
|
description: Markdown memo server
|
63
79
|
email:
|
64
80
|
- dai@daimatz.net
|
65
81
|
executables:
|
66
82
|
- mamemose
|
83
|
+
- mamemose_websocket
|
67
84
|
extensions: []
|
68
85
|
extra_rdoc_files: []
|
69
86
|
files:
|
@@ -73,9 +90,14 @@ files:
|
|
73
90
|
- README.md
|
74
91
|
- Rakefile
|
75
92
|
- bin/mamemose
|
93
|
+
- bin/mamemose_websocket
|
76
94
|
- index.png
|
77
95
|
- lib/mamemose.rb
|
96
|
+
- lib/mamemose/env.rb
|
97
|
+
- lib/mamemose/path.rb
|
98
|
+
- lib/mamemose/util.rb
|
78
99
|
- lib/mamemose/version.rb
|
100
|
+
- lib/mamemose/websocket.rb
|
79
101
|
- mamemose.gemspec
|
80
102
|
- sample.png
|
81
103
|
homepage: https://github.com/daimatz/mamemose
|