memorack 0.0.1
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/.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
|