memorack 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +28 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +108 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/bin/memorack +9 -0
- data/etc/tools/update_version.rb +14 -0
- data/lib/memorack/cli.rb +263 -0
- data/lib/memorack/locales/en.yml +7 -0
- data/lib/memorack/locales/ja.yml +7 -0
- data/lib/memorack/mdmenu.rb +185 -0
- data/lib/memorack/memoapp.rb +350 -0
- data/lib/memorack/sinatra-mustache.rb +13 -0
- data/lib/memorack/template/.gitignore +12 -0
- data/lib/memorack/template/.powenv +1 -0
- data/lib/memorack/template/Gemfile +7 -0
- data/lib/memorack/template/config.ru +5 -0
- data/lib/memorack/template/content/README.md +3 -0
- data/lib/memorack/template/themes/custom/config.json +15 -0
- data/lib/memorack/template/themes/custom/index.md +3 -0
- data/lib/memorack/themes/basic/2-column.scss +60 -0
- data/lib/memorack/themes/basic/basic-styles.scss +82 -0
- data/lib/memorack/themes/basic/config.json +14 -0
- data/lib/memorack/themes/basic/index.html +45 -0
- data/lib/memorack/themes/basic/styles.scss +9 -0
- data/lib/memorack/themes/oreilly/config.json +3 -0
- data/lib/memorack/themes/oreilly/oreilly/base.scss +33 -0
- data/lib/memorack/themes/oreilly/oreilly/styles.scss +138 -0
- data/lib/memorack/themes/oreilly/styles.scss +13 -0
- data/lib/memorack/tilt-mustache.rb +29 -0
- data/lib/memorack/version.rb +6 -0
- data/lib/memorack.rb +6 -0
- data/memorack.gemspec +45 -0
- data/spec/memorack_spec.rb +239 -0
- data/spec/spec_helper.rb +9 -0
- metadata +230 -0
data/.gitignore
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
/VERSION
|
2
|
+
*.gem
|
3
|
+
*.rbc
|
4
|
+
.bundle
|
5
|
+
.config
|
6
|
+
.yardoc
|
7
|
+
Gemfile.lock
|
8
|
+
InstalledFiles
|
9
|
+
_yardoc
|
10
|
+
coverage
|
11
|
+
doc/
|
12
|
+
lib/bundler/man
|
13
|
+
pkg
|
14
|
+
rdoc
|
15
|
+
spec/reports
|
16
|
+
test/tmp
|
17
|
+
test/version_tmp
|
18
|
+
tmp
|
19
|
+
|
20
|
+
# editor noise
|
21
|
+
*~
|
22
|
+
|
23
|
+
# old skool
|
24
|
+
.svn
|
25
|
+
|
26
|
+
# osx noise
|
27
|
+
.DS_Store
|
28
|
+
Icon
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 gnue
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# MemoRack
|
2
|
+
|
3
|
+
Rack Application for markdown memo
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'memorack'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install memorack
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
$ memorack create PATH # Generate template folder
|
22
|
+
$ memorack theme # Show theme list
|
23
|
+
$ memorack theme THEME # Show theme info
|
24
|
+
$ memorack theme -c THEME # Copy theme
|
25
|
+
$ memorack server PATH # Instant Server
|
26
|
+
|
27
|
+
Standard startup
|
28
|
+
|
29
|
+
$ memorack create memo
|
30
|
+
$ cd memo
|
31
|
+
(Customizing...)
|
32
|
+
$ rackup
|
33
|
+
|
34
|
+
Instant server
|
35
|
+
|
36
|
+
$ mkdir content
|
37
|
+
$ echo '# Hello World' > content/hello.md
|
38
|
+
(Customizing...)
|
39
|
+
$ memorack server content
|
40
|
+
|
41
|
+
OS X (Pow + powder)
|
42
|
+
|
43
|
+
$ memorack create memo
|
44
|
+
$ cd memo
|
45
|
+
(Customizing...)
|
46
|
+
$ powder link
|
47
|
+
$ open http://memo.dev/
|
48
|
+
|
49
|
+
* [Pow: Zero-configuration Rack server for Mac OS X](http://pow.cx)
|
50
|
+
* `gem install powder`
|
51
|
+
|
52
|
+
## Directory
|
53
|
+
|
54
|
+
Template
|
55
|
+
|
56
|
+
.
|
57
|
+
├── .gitignore -- for git
|
58
|
+
├── .powenv -- for pow + rbenv
|
59
|
+
├── Gemfile -- `bundle install`
|
60
|
+
├── config.ru -- for rack application
|
61
|
+
├── content -- Content directory for memo
|
62
|
+
│ └── README.md -- Sample file(remove it)
|
63
|
+
└── themes
|
64
|
+
└── custom -- Default theme
|
65
|
+
├── config.json -- Configuration
|
66
|
+
└── index.md -- Description(Show by top page)
|
67
|
+
|
68
|
+
## Customizing
|
69
|
+
|
70
|
+
### Layout
|
71
|
+
|
72
|
+
`index.html` is mustache template
|
73
|
+
|
74
|
+
$ cd themes/custom
|
75
|
+
$ memorack theme -c basic/index.html
|
76
|
+
Created 'index.html'
|
77
|
+
(Edit 'index.html'...)
|
78
|
+
|
79
|
+
#### mustache variables
|
80
|
+
|
81
|
+
Basic variables -- `{{VAR}}`
|
82
|
+
|
83
|
+
* `title`
|
84
|
+
* `page.title`
|
85
|
+
* `app.name`
|
86
|
+
* `app.version`
|
87
|
+
* `app.url`
|
88
|
+
* other variable in config.json
|
89
|
+
|
90
|
+
Special variables -- `{{{VAR}}}`
|
91
|
+
|
92
|
+
* `__menu__`
|
93
|
+
* `__content__`
|
94
|
+
|
95
|
+
## TODO
|
96
|
+
|
97
|
+
* Template comments translate english
|
98
|
+
* Add customizing tips
|
99
|
+
* Server test program
|
100
|
+
* Abstraction of URI
|
101
|
+
|
102
|
+
## Contributing
|
103
|
+
|
104
|
+
1. Fork it
|
105
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
106
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
107
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
108
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path('../etc/tools/update_version', __FILE__)
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
# Spec
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new(:spec) do |spec|
|
7
|
+
spec.libs << "spec"
|
8
|
+
spec.test_files = Dir['spec/**/*_spec.rb']
|
9
|
+
spec.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/memorack
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
def update_version(path)
|
2
|
+
version = `git describe --tags --dirty`.chomp
|
3
|
+
version.gsub!(/-([a-z0-9]+(-dirty)?)$/) { |m| "(#{$1})" }
|
4
|
+
|
5
|
+
begin
|
6
|
+
return if version == open(path).read.chomp
|
7
|
+
return if version.empty?
|
8
|
+
rescue
|
9
|
+
end
|
10
|
+
|
11
|
+
open(path, 'w') { |f| f.puts version }
|
12
|
+
end
|
13
|
+
|
14
|
+
update_version 'VERSION'
|
data/lib/memorack/cli.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'i18n'
|
7
|
+
|
8
|
+
|
9
|
+
module MemoRack
|
10
|
+
class CLI
|
11
|
+
|
12
|
+
def self.run(argv = ARGV, options = {})
|
13
|
+
CLI.new.run(argv, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
i18n_init
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(argv = ARGV, options = {})
|
21
|
+
subcmd = nil
|
22
|
+
|
23
|
+
parser = OptionParser.new do |opts|
|
24
|
+
begin
|
25
|
+
opts.version = LONG_VERSION || VERSION
|
26
|
+
|
27
|
+
opts.banner = <<-BANNER.gsub(/^\t+/,'')
|
28
|
+
Usage: #{opts.program_name} create [options] PATH
|
29
|
+
#{opts.program_name} theme [options] [THEME]
|
30
|
+
#{opts.program_name} server [options] PATH
|
31
|
+
BANNER
|
32
|
+
|
33
|
+
opts.separator ""
|
34
|
+
opts.on("-h", "--help", t(:help)) { abort opts.help }
|
35
|
+
|
36
|
+
opts.order!(argv)
|
37
|
+
|
38
|
+
subcmd = argv.shift
|
39
|
+
abort opts.help unless subcmd
|
40
|
+
abort opts.help unless has_action?(subcmd)
|
41
|
+
rescue => e
|
42
|
+
abort e.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
action(subcmd, argv, options)
|
47
|
+
end
|
48
|
+
|
49
|
+
# I18n を初期化する
|
50
|
+
def i18n_init
|
51
|
+
I18n.load_path = Dir[File.expand_path('../locales/*.yml', __FILE__)]
|
52
|
+
I18n.backend.load_translations
|
53
|
+
|
54
|
+
locale = ENV['LANG'][0, 2].to_sym if ENV['LANG']
|
55
|
+
I18n.locale = locale if I18n.available_locales.include?(locale)
|
56
|
+
end
|
57
|
+
|
58
|
+
# I18n で翻訳する
|
59
|
+
def t(code, locals = {}, options = {})
|
60
|
+
options[:scope] ||= [:usage]
|
61
|
+
sprintf(I18n.t(code, options), locals)
|
62
|
+
end
|
63
|
+
|
64
|
+
# ディレクトリを繰返す
|
65
|
+
def dir_earch(dir, match = '**/*', flag = File::FNM_DOTMATCH)
|
66
|
+
Dir.chdir(dir) { |d|
|
67
|
+
Dir.glob(match, flag).each { |file|
|
68
|
+
next if File.basename(file) =~ /^[.]{1,2}$/
|
69
|
+
file = File.join(file, '') if File.directory?(file)
|
70
|
+
yield(file)
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
# テーマ一覧を表示する
|
76
|
+
def show_themes(domain, themes)
|
77
|
+
return unless File.directory?(themes)
|
78
|
+
|
79
|
+
puts "#{domain}:"
|
80
|
+
|
81
|
+
Dir.foreach(themes) { |file|
|
82
|
+
next if /^\./ =~ file
|
83
|
+
puts " #{file}"
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
# サブコマンドが定義されているか?
|
88
|
+
def has_action?(command)
|
89
|
+
respond_to? "memorack_#{command}"
|
90
|
+
end
|
91
|
+
|
92
|
+
# サブコマンドの実行
|
93
|
+
def action(command, argv, options = {})
|
94
|
+
command = command.gsub(/-/, '_')
|
95
|
+
|
96
|
+
# オプション解析
|
97
|
+
options_method = "options_#{command}"
|
98
|
+
options.merge!(send(options_method, argv)) if respond_to?(options_method)
|
99
|
+
|
100
|
+
send("memorack_#{command}", options, *argv)
|
101
|
+
end
|
102
|
+
|
103
|
+
# サブコマンド・オプションのバナー作成
|
104
|
+
def banner(opts, method, *args)
|
105
|
+
subcmd = method.to_s.gsub(/^.+_/, '')
|
106
|
+
["Usage: #{opts.program_name} #{subcmd}", *args].join(' ')
|
107
|
+
end
|
108
|
+
|
109
|
+
# オプション解析を定義する
|
110
|
+
def self.define_options(command, *banner, &block)
|
111
|
+
define_method "options_#{command}" do |argv|
|
112
|
+
options = {}
|
113
|
+
|
114
|
+
OptionParser.new { |opts|
|
115
|
+
begin
|
116
|
+
opts.banner = banner(opts, command, *banner)
|
117
|
+
instance_exec(opts, argv, options, &block)
|
118
|
+
rescue => e
|
119
|
+
abort e.to_s
|
120
|
+
end
|
121
|
+
}
|
122
|
+
|
123
|
+
options
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# オプション解析
|
128
|
+
|
129
|
+
# テンプレートの作成
|
130
|
+
define_options(:create, '[options] PATH') { |opts, argv, options|
|
131
|
+
opts.on("-h", "--help", t(:help)) { abort opts.help }
|
132
|
+
|
133
|
+
opts.parse!(argv)
|
134
|
+
abort opts.help if argv.empty?
|
135
|
+
}
|
136
|
+
|
137
|
+
# テーマ関連の操作
|
138
|
+
define_options(:theme, '[options] [THEME]') { |opts, argv, options|
|
139
|
+
default_options = {dir: 'themes'}
|
140
|
+
|
141
|
+
options.merge!(default_options)
|
142
|
+
|
143
|
+
opts.separator ""
|
144
|
+
opts.on("-c", "--copy", t(:copy)) { options[:copy] = true }
|
145
|
+
opts.on("-d", "--dir DIR", t(:dir, options)) { |arg| options[:dir] = arg }
|
146
|
+
opts.on("-h", "--help", t(:help)) { abort opts.help }
|
147
|
+
|
148
|
+
opts.parse!(argv)
|
149
|
+
}
|
150
|
+
|
151
|
+
# サーバーの実行
|
152
|
+
define_options(:server, '[options] PATH') { |opts, argv, options|
|
153
|
+
default_options = {
|
154
|
+
theme: 'oreilly',
|
155
|
+
|
156
|
+
server: {
|
157
|
+
environment: ENV['RACK_ENV'] || 'development',
|
158
|
+
Port: 9292,
|
159
|
+
Host: '0.0.0.0',
|
160
|
+
AccessLog: [],
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
options.merge!(default_options)
|
165
|
+
|
166
|
+
opts.separator ""
|
167
|
+
opts.on("-p", "--port PORT", String,
|
168
|
+
t(:port, options[:server])) { |arg| options[:server][:Port] = arg }
|
169
|
+
opts.on("-t", "--theme THEME", String,
|
170
|
+
t(:theme, options)) { |arg| options[:theme] = arg }
|
171
|
+
opts.on("-h", "--help", t(:help)) { abort opts.help }
|
172
|
+
|
173
|
+
opts.parse!(argv)
|
174
|
+
abort opts.help if argv.empty?
|
175
|
+
}
|
176
|
+
|
177
|
+
|
178
|
+
# サブコマンド
|
179
|
+
|
180
|
+
# テンプレートの作成
|
181
|
+
def memorack_create(options, *argv)
|
182
|
+
path = argv.shift
|
183
|
+
abort "File exists '#{path}'" if File.exists?(path)
|
184
|
+
|
185
|
+
FileUtils.copy_entry(File.expand_path('../template', __FILE__), path)
|
186
|
+
puts "Created '#{path}'"
|
187
|
+
end
|
188
|
+
|
189
|
+
# テーマ関連の操作
|
190
|
+
def memorack_theme(options, *argv)
|
191
|
+
theme_or_file = argv.shift
|
192
|
+
|
193
|
+
themes = File.expand_path("../themes", __FILE__)
|
194
|
+
dir = options[:dir]
|
195
|
+
|
196
|
+
if theme_or_file
|
197
|
+
theme = theme_or_file.gsub(%r(/.*), '')
|
198
|
+
|
199
|
+
if options[:copy]
|
200
|
+
# テーマをコピー
|
201
|
+
theme_dir = File.join(themes, theme)
|
202
|
+
abort "Theme not exists '#{theme}'" unless File.directory?(theme_dir)
|
203
|
+
|
204
|
+
from = File.join(themes, theme_or_file)
|
205
|
+
abort "File not exists '#{theme_or_file}'" unless File.exists?(from)
|
206
|
+
|
207
|
+
path = name = File.basename(from)
|
208
|
+
path = File.join(dir, name) if File.directory?(dir) && File.directory?(from)
|
209
|
+
|
210
|
+
FileUtils.copy_entry(from, path)
|
211
|
+
puts "Created '#{path}'"
|
212
|
+
else
|
213
|
+
# テーマの情報を表示
|
214
|
+
app = MemoRack::MemoApp.new(nil, theme: theme, root: dir)
|
215
|
+
theme_dir = app.themes.first
|
216
|
+
|
217
|
+
abort "Theme not exists '#{theme}'" unless theme_dir
|
218
|
+
|
219
|
+
# 継承関係の表示
|
220
|
+
theme_chain = app.themes.collect { |path|
|
221
|
+
name = File.basename(path)
|
222
|
+
File.dirname(path) == themes ? "[#{name}]" : name
|
223
|
+
}
|
224
|
+
|
225
|
+
puts theme_chain.join(' --> ')
|
226
|
+
|
227
|
+
# ファイル一覧の表示
|
228
|
+
dir_earch(theme_dir) { |file|
|
229
|
+
puts " #{file}"
|
230
|
+
}
|
231
|
+
end
|
232
|
+
else
|
233
|
+
# テーマ一覧を表示
|
234
|
+
show_themes('MemoRack', themes)
|
235
|
+
show_themes('User', dir)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# サーバーの実行
|
240
|
+
def memorack_server(options, *argv)
|
241
|
+
path = argv.shift
|
242
|
+
abort "Directory not exists '#{path}'" unless File.exists?(path)
|
243
|
+
abort "Not directory '#{path}'" unless File.directory?(path)
|
244
|
+
|
245
|
+
server_options = options[:server]
|
246
|
+
|
247
|
+
# サーバーの起動
|
248
|
+
require 'rack/builder'
|
249
|
+
require 'rack/handler/webrick'
|
250
|
+
app = Rack::Builder.new {
|
251
|
+
require 'memorack'
|
252
|
+
require 'tmpdir'
|
253
|
+
|
254
|
+
Dir.mktmpdir do |tmpdir|
|
255
|
+
run MemoRack::MemoApp.new(nil, theme: options[:theme], root: path, tmpdir: tmpdir)
|
256
|
+
end
|
257
|
+
}
|
258
|
+
|
259
|
+
server_options[:app] = app
|
260
|
+
Rack::Server.new(server_options).start
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: utf-8 -*-
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
|
8
|
+
module MemoRack
|
9
|
+
|
10
|
+
class MdMenu
|
11
|
+
URI_UNSAFE = /[^\-_.!~*'a-zA-Z\d;\/?:@&=+$,\[\]]/
|
12
|
+
|
13
|
+
DEFAULT_FORMATS = {
|
14
|
+
'markdown' => ['txt', 'md', 'markdown'],
|
15
|
+
'textile' => ['tt'],
|
16
|
+
'wiki' => ['wiki'],
|
17
|
+
'json' => ['json'],
|
18
|
+
'html' => ['html', 'htm']
|
19
|
+
}
|
20
|
+
|
21
|
+
def initialize(config)
|
22
|
+
@config = config
|
23
|
+
@file = config[:file]
|
24
|
+
@links = analyze(@file)
|
25
|
+
@files = []
|
26
|
+
@extentions = {}
|
27
|
+
|
28
|
+
regFormats(config[:formats] || DEFAULT_FORMATS)
|
29
|
+
end
|
30
|
+
|
31
|
+
# フォーマット・ハッシュからファイル拡張子を登録する
|
32
|
+
def regFormats(formats)
|
33
|
+
formats.each { |name, extentions|
|
34
|
+
regFormat(name, extentions)
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# フォーマットとファイル拡張子を登録する
|
39
|
+
def regFormat(name, extentions)
|
40
|
+
extentions.each { |value|
|
41
|
+
@extentions[value] = name
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
# Markdownファイルを解析してリンクを取出す
|
46
|
+
def analyze(path)
|
47
|
+
links = []
|
48
|
+
|
49
|
+
if path && File.exist?(path) then
|
50
|
+
open(path) { |f|
|
51
|
+
while line = f.gets()
|
52
|
+
case line
|
53
|
+
when /\[.+\]\s*\(([^()]+)\)/
|
54
|
+
# インライン・リンク
|
55
|
+
links << $1
|
56
|
+
when /\[.+\]:\s*([^\s]+)/
|
57
|
+
# 参照リンク
|
58
|
+
links << $1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
return links
|
65
|
+
end
|
66
|
+
|
67
|
+
# 追加されたファイルを集める
|
68
|
+
def collection(dir)
|
69
|
+
# 拡張子
|
70
|
+
exts = @extentions.keys.join(',')
|
71
|
+
# 検索パターン
|
72
|
+
pattern = File.join(dir, "**/*.{#{exts}}");
|
73
|
+
pattern.gsub!(/^\.\//, '')
|
74
|
+
|
75
|
+
Dir.glob(pattern) { |path|
|
76
|
+
link = @config[:prefix].to_s + (@config[:uri_escape] ? URI.escape(path, URI_UNSAFE) : path)
|
77
|
+
@files << {:link => link, :path => path} if ! @links.member?(link)
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
# 標準出力を切換える
|
82
|
+
def stdout(path, mode = 'r', perm = 0666)
|
83
|
+
curr = $stdout
|
84
|
+
f = nil
|
85
|
+
|
86
|
+
begin
|
87
|
+
if path.kind_of?(String)
|
88
|
+
$stdout = f = File.open(path, mode, perm)
|
89
|
+
elsif path
|
90
|
+
$stdout = path
|
91
|
+
end
|
92
|
+
|
93
|
+
yield
|
94
|
+
ensure
|
95
|
+
f.close if f
|
96
|
+
$stdout = curr
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# ディレクトリが違うときにサブディレクトリを引数に実行する
|
101
|
+
def each_subdir(d, dir = nil, dirs = [])
|
102
|
+
return [dir, dirs] if d == dir
|
103
|
+
|
104
|
+
prefix = @config[:prefix]
|
105
|
+
|
106
|
+
d.gsub!(/^#{prefix}/, '') if prefix
|
107
|
+
ds = d.scan(/[^\/]+/)
|
108
|
+
ds.delete('.')
|
109
|
+
|
110
|
+
ds.each_with_index { |name, i|
|
111
|
+
next if name == dirs[i]
|
112
|
+
|
113
|
+
name = URI.unescape(name) if @config[:uri_escape]
|
114
|
+
yield(name, i)
|
115
|
+
}
|
116
|
+
|
117
|
+
[d, ds]
|
118
|
+
end
|
119
|
+
|
120
|
+
# 新規リンクを追加する
|
121
|
+
def generate(outfile = @file)
|
122
|
+
len = @files.length
|
123
|
+
|
124
|
+
if 0 < len
|
125
|
+
outfile = nil if outfile && outfile.kind_of?(String) && File.exist?(outfile) && ! @config[:update]
|
126
|
+
|
127
|
+
stdout(outfile, 'a') {
|
128
|
+
prefix = @config[:prefix]
|
129
|
+
dir = nil
|
130
|
+
dirs = []
|
131
|
+
|
132
|
+
@files.each { |item|
|
133
|
+
title = File.basename(item[:path], '.*')
|
134
|
+
link = item[:link]
|
135
|
+
|
136
|
+
dir, dirs = each_subdir(File.dirname(link), dir, dirs) { |name, i|
|
137
|
+
print ' ' * i + "- #{name}\n"
|
138
|
+
}
|
139
|
+
|
140
|
+
print ' ' * dirs.size + "- [#{title}](#{link})\n"
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
if outfile then
|
145
|
+
# update
|
146
|
+
message = "append #{len} links"
|
147
|
+
else
|
148
|
+
# dry-run
|
149
|
+
message = "found #{len} links (can update with -u option)"
|
150
|
+
end
|
151
|
+
else
|
152
|
+
message = "not found"
|
153
|
+
end
|
154
|
+
|
155
|
+
$stderr.print message, "\n" if message && @config[:verbose]
|
156
|
+
|
157
|
+
outfile
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
if __FILE__ == $0
|
165
|
+
# コマンド引数の解析
|
166
|
+
config = {:verbose => true}
|
167
|
+
|
168
|
+
OptionParser.new { |opts|
|
169
|
+
opts.banner = "Usage: #{opts.program_name} [-f FILE] [-u] [-e] [--prefix PREFIX] DIRECTORY… "
|
170
|
+
|
171
|
+
opts.on('-f FILE', 'menu file') { |v| config[:file] = v }
|
172
|
+
opts.on('-u', 'file update(default is dry-run)') { config[:update] = true }
|
173
|
+
opts.on('-e', 'URI escape') { config[:uri_escape] = true }
|
174
|
+
opts.on('-p PREFIX', '--prefix PREFIX', 'link prefix') { |v| config[:prefix] = v }
|
175
|
+
opts.on('-h', '--help') { abort opts.help }
|
176
|
+
opts.parse!(ARGV)
|
177
|
+
}
|
178
|
+
|
179
|
+
ARGV.push '.' if ARGV.length == 0
|
180
|
+
|
181
|
+
mdmenu = MemoRack::MdMenu.new(config)
|
182
|
+
|
183
|
+
ARGV.each { |dir| mdmenu.collection(dir) }
|
184
|
+
mdmenu.generate
|
185
|
+
end
|