mamemose 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|