gnn-rblibs 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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +32 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/gnn-rblibs.gemspec +67 -0
- data/lib/dir/each_leaf.rb +17 -0
- data/lib/file/kind.rb +8 -0
- data/lib/file/observer.rb +138 -0
- data/lib/fileutils/sync.rb +61 -0
- data/lib/getopt.rb +185 -0
- data/lib/loadconf.rb +26 -0
- data/lib/plugin.rb +28 -0
- data/lib/process/invoke.rb +15 -0
- data/lib/rbconfig/command.rb +22 -0
- data/lib/string/random.rb +15 -0
- data/lib/term/countdown.rb +50 -0
- data/lib/webrick/single_thread_server.rb +80 -0
- data/lib/www/mechanize/get.rb +125 -0
- data/spec/gnn-rblibs_spec.rb +7 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +8 -0
- metadata +88 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 tarao
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
= gnn-rblibs
|
2
|
+
|
3
|
+
Some Ruby tweaks.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
First, make your gem source gemcutter by
|
8
|
+
|
9
|
+
gem install gemcutter
|
10
|
+
gem tumble
|
11
|
+
|
12
|
+
or
|
13
|
+
|
14
|
+
gem sources -a http://gemcutter.org
|
15
|
+
|
16
|
+
then
|
17
|
+
|
18
|
+
gem install gnn-rblibs
|
19
|
+
|
20
|
+
== Note on Patches/Pull Requests
|
21
|
+
|
22
|
+
* Fork the project.
|
23
|
+
* Make your feature addition or bug fix.
|
24
|
+
* Add tests for it. This is important so I don't break it in a
|
25
|
+
future version unintentionally.
|
26
|
+
* Commit, do not mess with rakefile, version, or history.
|
27
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
28
|
+
* Send me a pull request. Bonus points for topic branches.
|
29
|
+
|
30
|
+
== Copyright
|
31
|
+
|
32
|
+
Copyright (c) 2009 tarao. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "gnn-rblibs"
|
8
|
+
gem.summary = %Q{Some Ruby tweaks}
|
9
|
+
gem.description = %Q{Some Ruby tweaks for file operations, commandline parsers, etc.}
|
10
|
+
gem.email = "tarao.gnn@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/tarao/gnn-rblibs"
|
12
|
+
gem.authors = ["tarao"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "gnn-rblibs #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/gnn-rblibs.gemspec
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{gnn-rblibs}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["tarao"]
|
12
|
+
s.date = %q{2009-11-28}
|
13
|
+
s.description = %q{Some Ruby tweaks for file operations, commandline parsers, etc.}
|
14
|
+
s.email = %q{tarao.gnn@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"gnn-rblibs.gemspec",
|
27
|
+
"lib/dir/each_leaf.rb",
|
28
|
+
"lib/file/kind.rb",
|
29
|
+
"lib/file/observer.rb",
|
30
|
+
"lib/fileutils/sync.rb",
|
31
|
+
"lib/getopt.rb",
|
32
|
+
"lib/loadconf.rb",
|
33
|
+
"lib/plugin.rb",
|
34
|
+
"lib/process/invoke.rb",
|
35
|
+
"lib/rbconfig/command.rb",
|
36
|
+
"lib/string/random.rb",
|
37
|
+
"lib/term/countdown.rb",
|
38
|
+
"lib/webrick/single_thread_server.rb",
|
39
|
+
"lib/www/mechanize/get.rb",
|
40
|
+
"spec/gnn-rblibs_spec.rb",
|
41
|
+
"spec/spec.opts",
|
42
|
+
"spec/spec_helper.rb"
|
43
|
+
]
|
44
|
+
s.homepage = %q{http://github.com/tarao/gnn-rblibs}
|
45
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
46
|
+
s.require_paths = ["lib"]
|
47
|
+
s.rubygems_version = %q{1.3.5}
|
48
|
+
s.summary = %q{Some Ruby tweaks}
|
49
|
+
s.test_files = [
|
50
|
+
"spec/spec_helper.rb",
|
51
|
+
"spec/gnn-rblibs_spec.rb"
|
52
|
+
]
|
53
|
+
|
54
|
+
if s.respond_to? :specification_version then
|
55
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
56
|
+
s.specification_version = 3
|
57
|
+
|
58
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
59
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
62
|
+
end
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Dir
|
2
|
+
def self.children(path, flag=0)
|
3
|
+
c = self.glob(File.join(path, '*'), flag).reject{|f| f=~/\/\.+$/}
|
4
|
+
return block_given? ? c.each(&Proc.new) : c
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.each_leaf(path, flag=0, nofollow=false, block=nil)
|
8
|
+
block = Proc.new if block_given?
|
9
|
+
if (!nofollow || !File.symlink?(path)) && File.directory?(path)
|
10
|
+
self.children(path, flag) do |child|
|
11
|
+
self.each_leaf(child, flag, nofollow, block) # recursive
|
12
|
+
end
|
13
|
+
else
|
14
|
+
block && block.call(path)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/file/kind.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
class File
|
2
|
+
class Observer
|
3
|
+
ACCESS = 0x0001
|
4
|
+
MODIFY = 0x0002
|
5
|
+
ATTRIB = 0x0004
|
6
|
+
# CLOSE_WRITE = 0x0008
|
7
|
+
# CLOSE_NOWRITE = 0x0010
|
8
|
+
# OPEN = 0x0020
|
9
|
+
MOVED_FROM = 0x0040
|
10
|
+
MOVED_TO = 0x0080
|
11
|
+
CREATE = 0x0100
|
12
|
+
DELETE = 0x0200
|
13
|
+
# DELETE_SELF = 0x0400
|
14
|
+
# MOVE_SELF = 0x0800
|
15
|
+
# CLOSE = CLOSE_WRITE | CLOSE_NOWRITE
|
16
|
+
MOVE = MOVED_FROM | MOVED_TO
|
17
|
+
CHANGE = MODIFY | ATTRIB | MOVE | CREATE | DELETE
|
18
|
+
|
19
|
+
class Win32
|
20
|
+
def self.installed?
|
21
|
+
begin
|
22
|
+
require 'rubygems'
|
23
|
+
require 'win32/changenotify'
|
24
|
+
rescue LoadError
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
return self
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.watch(files, events, dir = false)
|
31
|
+
mask = 0
|
32
|
+
# FIXME: more events
|
33
|
+
# mask |= ::Win32::ChangeNotify::ATTRIBUTES if (events & ATTRIB) != 0
|
34
|
+
mask |= ::Win32::ChangeNotify::DIR_NAME if (events & MOVE) != 0
|
35
|
+
mask |= ::Win32::ChangeNotify::FILE_NAME if (events & MOVE) != 0
|
36
|
+
mask |= ::Win32::ChangeNotify::LAST_WRITE if (events & CHANGE) != 0
|
37
|
+
mask |= ::Win32::ChangeNotify::SIZE if (events & CHANGE) != 0
|
38
|
+
directory = dir ? files : File.dirname(files.first)
|
39
|
+
cn = ::Win32::ChangeNotify.new(directory, false, mask)
|
40
|
+
loop do
|
41
|
+
cn.wait do |arr|
|
42
|
+
arr.each do |st|
|
43
|
+
if dir || files.find{|f| f == st.file_name}
|
44
|
+
# FIXME: convert st.action to event
|
45
|
+
yield({ :name => st.file_name,
|
46
|
+
:event => events,
|
47
|
+
:class => self })
|
48
|
+
return true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.watch_dir(dir, events, &block)
|
57
|
+
return self.watch(dir, events, true, &block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Inotify
|
62
|
+
def self.installed?
|
63
|
+
begin
|
64
|
+
require 'inotify'
|
65
|
+
rescue LoadError
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
return self
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.watch(files, events)
|
72
|
+
notifier = ::Inotify.new
|
73
|
+
begin
|
74
|
+
files.each{|f| notifier.add_watch(f, events)}
|
75
|
+
notifier.each_event do |e|
|
76
|
+
yield({ :event => e.mask,
|
77
|
+
:class => self })
|
78
|
+
return true
|
79
|
+
end
|
80
|
+
ensure
|
81
|
+
notifier.close
|
82
|
+
end
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.watch_dir(dir, events, &block)
|
87
|
+
return self.watch(dir, events, &block)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Naive
|
92
|
+
def self.installed?
|
93
|
+
return Naive
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.watch(files, events, s=0.1)
|
97
|
+
mtime = {}
|
98
|
+
files.each{|f| mtime[f] = File.mtime(f)}
|
99
|
+
|
100
|
+
loop do
|
101
|
+
# FIXME: other events
|
102
|
+
fname = files.find{|f| File.mtime(f) != mtime[f]}
|
103
|
+
if fname
|
104
|
+
yield({ :name => fname,
|
105
|
+
:event => events,
|
106
|
+
:class => self })
|
107
|
+
return true
|
108
|
+
end
|
109
|
+
sleep(s)
|
110
|
+
end
|
111
|
+
return false
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.watch_dir(dir, events, s=0.1, &block)
|
115
|
+
return self.watch(dir, events, s, &block)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.watch(files, events, &block)
|
120
|
+
klass = Win32.installed? || Inotify.installed? || Naive.installed?
|
121
|
+
raise 'No file observer' unless klass
|
122
|
+
files = [files] unless files.is_a?(Array)
|
123
|
+
mtime = {}
|
124
|
+
files.each{|f| mtime[f] = File.mtime(f)}
|
125
|
+
return klass.watch(files, events) do |x|
|
126
|
+
# FIXME: other events
|
127
|
+
x[:name] = x[:name] || files.find{|f| File.mtime(f) != mtime[f]}
|
128
|
+
block.call(x)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.watch_dir(dir, events, &block)
|
133
|
+
klass = Win32.installed? || Inotify.installed? || Naive.installed?
|
134
|
+
raise 'No file observer' unless klass
|
135
|
+
return klass.watch_dir(dir, events, &block)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'file/kind'
|
2
|
+
require 'dir/each_leaf'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module FileUtils
|
6
|
+
class Sync
|
7
|
+
def self.each_leaf(src, dst, flag=0, nf=false, block=nil)
|
8
|
+
block = Proc.new if block_given?
|
9
|
+
if [File.kind(src, nf), File.kind(dst, nf)].all?{|k| k == :directory}
|
10
|
+
Dir.children(src, flag) do |file|
|
11
|
+
target = File.join(dst, File.basename(file))
|
12
|
+
self.each_leaf(file, target, flag, nf, block) # recursive
|
13
|
+
end
|
14
|
+
else
|
15
|
+
block && block.call(src, dst)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.conflict?(src, dst, nofollow=false)
|
20
|
+
return false unless File.exist?(dst)
|
21
|
+
return File.kind(src, nofollow) != File.kind(dst, nofollow)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.uptodate?(s, d)
|
25
|
+
cond = false
|
26
|
+
cond ||= !File.exist?(d)
|
27
|
+
cond ||= File.file?(s) && File.file?(d) && FileUtils.uptodate?(s, d)
|
28
|
+
return cond
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(opt={})
|
32
|
+
@cond = opt[:cond] || {}
|
33
|
+
@cond[:conflict] ||= proc{|s,d| self.class.conflict?(s,d)}
|
34
|
+
@cond[:uptodate] ||= proc{|s,d| self.class.uptodate?(s,d)}
|
35
|
+
|
36
|
+
@method = opt[:method] || {}
|
37
|
+
@method[:conflict] ||= proc{}
|
38
|
+
@method[:uptodate] ||= proc do |s, d|
|
39
|
+
FileUtils.cp_r(s, d, {:preserve => true})
|
40
|
+
end
|
41
|
+
@method[:nothing] ||= proc{}
|
42
|
+
|
43
|
+
@flag = opt[:flag] || 0
|
44
|
+
@nofollow = opt[:nofollow] || false
|
45
|
+
end
|
46
|
+
|
47
|
+
def sync(src, dst)
|
48
|
+
self.class.each_leaf(src, dst, @flag, @nofollow) do |s,d|
|
49
|
+
none = proc{ :nothing }
|
50
|
+
what = [ :conflict, :uptodate ].find(none){|w| @cond[w].call(s, d)}
|
51
|
+
@method[what].call(s, d)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def sync(src, dst, opt={})
|
57
|
+
Sync.new(opt).sync(src, dst)
|
58
|
+
end
|
59
|
+
|
60
|
+
module_function :sync
|
61
|
+
end
|
data/lib/getopt.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
class GetOpt
|
2
|
+
def self.escape(str, quote=true)
|
3
|
+
str = str.to_s
|
4
|
+
return str unless str.match(/[\\\"\'\s]/)
|
5
|
+
str = str.gsub(/\\/, '\\\\').gsub(/\"/, '\\\"')
|
6
|
+
return quote ? '"'+str+'"' : str
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :args, :parsed, :rest, :parser
|
10
|
+
|
11
|
+
def initialize(args, *opts)
|
12
|
+
@parsed = {}
|
13
|
+
@args = []
|
14
|
+
@rest = []
|
15
|
+
|
16
|
+
# argument syntax definitions
|
17
|
+
syntax(opts[0] || [], opts[1] || {})
|
18
|
+
|
19
|
+
# custom parser
|
20
|
+
@parser = {
|
21
|
+
:custom => (opts[1].is_a?(Proc) && opts[1]) || proc{|v| eat(v)}
|
22
|
+
}
|
23
|
+
|
24
|
+
pparse_rest = proc{|n,v,a,r| parse_rest(n,v,a,r)}
|
25
|
+
pparse_val = proc{|n,v,a,r| parse_val(n,v,a,r)}
|
26
|
+
pparse_bool_short = proc{|n,v,a,r| parse_bool_short(n,v,a,r)}
|
27
|
+
pparse_bool_long = proc{|n,v,a,r| parse_bool_long(n,v,a,r)}
|
28
|
+
|
29
|
+
# parser for short arguments
|
30
|
+
@parser[:short] = {
|
31
|
+
'b' => pparse_bool_short,
|
32
|
+
'i' => pparse_val,
|
33
|
+
'f' => pparse_val,
|
34
|
+
's' => pparse_val,
|
35
|
+
}
|
36
|
+
|
37
|
+
# parser for long arguments
|
38
|
+
@parser[:long] = {
|
39
|
+
'b' => pparse_bool_long,
|
40
|
+
'i' => pparse_val,
|
41
|
+
'f' => pparse_val,
|
42
|
+
's' => pparse_val,
|
43
|
+
'' => pparse_rest, # '... -- ...' is a separator
|
44
|
+
}
|
45
|
+
|
46
|
+
return parse(args || [])
|
47
|
+
end
|
48
|
+
|
49
|
+
def syntax(opts, default={})
|
50
|
+
@full = {}
|
51
|
+
@type = {}
|
52
|
+
@default = {}
|
53
|
+
default.each{|k,v| @default[k.to_s] = v}
|
54
|
+
opts.each do |opt|
|
55
|
+
default_value = nil
|
56
|
+
if opt.is_a?(Array)
|
57
|
+
default_value = opt[1]
|
58
|
+
opt = opt[0]
|
59
|
+
end
|
60
|
+
if opt =~ /^(?:([^-=])\|)?([^-=]+)(?:=(.+))?$/
|
61
|
+
@type[$2] = $3 || 'b'
|
62
|
+
@full[$1] = $2 if $1
|
63
|
+
@default[$2.to_s] = default_value if default_value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
@type[''] = ''
|
67
|
+
return self
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse(args)
|
71
|
+
args = @parser[:custom].call(args) while args.length != 0
|
72
|
+
return self
|
73
|
+
end
|
74
|
+
|
75
|
+
def [](opt)
|
76
|
+
opt = opt.to_s
|
77
|
+
val = @parsed[opt]
|
78
|
+
return val == nil ? @default[opt] : val
|
79
|
+
end
|
80
|
+
|
81
|
+
def []=(opt,val)
|
82
|
+
return @parsed[opt.to_s] = val
|
83
|
+
end
|
84
|
+
|
85
|
+
def push(name, val)
|
86
|
+
if @type[name] == 'b'
|
87
|
+
if !val
|
88
|
+
@parsed[name] = false
|
89
|
+
elsif !@parsed[name]
|
90
|
+
@parsed[name] = true
|
91
|
+
elsif @parsed[name].is_a?(Integer)
|
92
|
+
@parsed[name] = @parsed[name].succ
|
93
|
+
else # @parsed[name] == true
|
94
|
+
@parsed[name] = 2 # promote to be an Integer
|
95
|
+
end
|
96
|
+
else
|
97
|
+
if @parsed[name].instance_of?(Array)
|
98
|
+
@parsed[name] << val
|
99
|
+
elsif @parsed[name]
|
100
|
+
@parsed[name] = [ @parsed[name], val ] # promote to be an Array
|
101
|
+
else
|
102
|
+
@parsed[name] = val
|
103
|
+
end
|
104
|
+
end
|
105
|
+
return self
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_s
|
109
|
+
arr1 = []
|
110
|
+
@parsed.each{|name,value|
|
111
|
+
if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
112
|
+
arr1 << ('--' + (value ? '' : 'no-') + name)
|
113
|
+
elsif @type[name] == 'b' && value.is_a?(Integer)
|
114
|
+
arr1 += (1..value).map{|v| '--' + name}
|
115
|
+
elsif value.is_a?(Array)
|
116
|
+
value.each{|v| arr1 << ('--' + name + '=' + self.class.escape(v))}
|
117
|
+
else
|
118
|
+
arr1 << ('--' + name + '=' + self.class.escape(value))
|
119
|
+
end
|
120
|
+
}
|
121
|
+
arr2 = @args
|
122
|
+
arr2 += ['--'] + @rest if @rest.length > 0
|
123
|
+
return (arr1 + arr2.map{|val| self.class.escape(val)}).join(' ')
|
124
|
+
end
|
125
|
+
|
126
|
+
# predefined parsers
|
127
|
+
|
128
|
+
def parse_bool_short(name, val, arg, rest)
|
129
|
+
flags = arg[1..-1] # skip '-'
|
130
|
+
flags.each_byte do |f| # for each flag
|
131
|
+
name = @full[f.chr]
|
132
|
+
if name && @type[name] == 'b'
|
133
|
+
push(name, true)
|
134
|
+
else
|
135
|
+
parse_unknown(name, nil, '-'+f.chr, rest)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
return rest
|
139
|
+
end
|
140
|
+
|
141
|
+
def parse_bool_long(name, val, arg, rest)
|
142
|
+
push(name, !( (val == 'false') ^ (arg =~ /^--no/) ))
|
143
|
+
return rest
|
144
|
+
end
|
145
|
+
|
146
|
+
def parse_val(name, val, arg, rest)
|
147
|
+
val = (val || rest.shift).send('to_' + @type[name]) # to_i, to_f, etc.
|
148
|
+
push(name, val)
|
149
|
+
return rest
|
150
|
+
end
|
151
|
+
|
152
|
+
def parse_rest(name, val, arg, rest)
|
153
|
+
@rest = rest
|
154
|
+
return []
|
155
|
+
end
|
156
|
+
|
157
|
+
def parse_unknown(name, val, arg, rest)
|
158
|
+
@args << arg
|
159
|
+
return rest
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def get_parser(name, long)
|
165
|
+
parser = @parser[ long ? :long : :short ]
|
166
|
+
return parser[@type[name]] || proc{|n,v,a,r| parse_unknown(n,v,a,r)}
|
167
|
+
end
|
168
|
+
|
169
|
+
def eat(args)
|
170
|
+
arg = args.shift
|
171
|
+
|
172
|
+
long = false
|
173
|
+
name = nil
|
174
|
+
if arg =~ /^--(no-?)?([^=]*)(?:=(.*))?$/
|
175
|
+
long = true
|
176
|
+
name = (@type[$2] || !$1) ? $2 : $1+$2
|
177
|
+
val = $3
|
178
|
+
elsif arg =~ /^-([^-])(.+)?$/
|
179
|
+
name = @full[$1]
|
180
|
+
val = $2
|
181
|
+
end
|
182
|
+
|
183
|
+
return get_parser(name, long).call(name, val, arg, args)
|
184
|
+
end
|
185
|
+
end
|
data/lib/loadconf.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module LoadConf
|
2
|
+
def report(method, e)
|
3
|
+
if method.instance_of?(String)
|
4
|
+
warn(method)
|
5
|
+
elsif method.instance_of?(Proc)
|
6
|
+
method.call(e)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def load(fname, *opt)
|
11
|
+
er = opt[0] || {}
|
12
|
+
begin
|
13
|
+
open(fname) do |src|
|
14
|
+
begin
|
15
|
+
return instance_eval(src.read.untaint, fname, 1)
|
16
|
+
rescue => e
|
17
|
+
report(er[:script], e)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
rescue SystemCallError => e
|
21
|
+
report(er[:open], e)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module_function :load, :report
|
26
|
+
end
|
data/lib/plugin.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class Plugin
|
2
|
+
@@list = []
|
3
|
+
def self.load(dir)
|
4
|
+
Dir::glob("#{dir}/*.rb").each do |fname|
|
5
|
+
open(fname) do |src|
|
6
|
+
begin
|
7
|
+
val = instance_eval(src.read.untaint, fname, 1)
|
8
|
+
@@list << val if val
|
9
|
+
warn("loaded plugin: #{fname}")
|
10
|
+
rescue
|
11
|
+
warn($!)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :api
|
18
|
+
def initialize(args)
|
19
|
+
@api = []
|
20
|
+
@@list.each do |klass|
|
21
|
+
begin
|
22
|
+
@api += klass.new(*args).api
|
23
|
+
rescue
|
24
|
+
warn($!)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module RbConfig
|
4
|
+
cmd = File.join(CONFIG['bindir'], CONFIG['ruby_install_name'])
|
5
|
+
CONFIG['ruby_command'] = cmd.sub(/.*\s.*/m, '"\&"')
|
6
|
+
|
7
|
+
def program_name
|
8
|
+
return (defined?(ExerbRuntime) && ExerbRuntime.filepath) || $0
|
9
|
+
end
|
10
|
+
|
11
|
+
def self_invoke_command
|
12
|
+
cmd = File.expand_path(program_name)
|
13
|
+
cmd.sub(/.*\s.*/m, '"\&"')
|
14
|
+
ext = Config::CONFIG['EXEEXT']
|
15
|
+
unless ext.length != 0 && program_name =~ /#{ext}$/
|
16
|
+
cmd = CONFIG['ruby_command'] + ' ' + cmd
|
17
|
+
end
|
18
|
+
return cmd
|
19
|
+
end
|
20
|
+
|
21
|
+
module_function :program_name, :self_invoke_command
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class String
|
2
|
+
def randomize(len=128)
|
3
|
+
return Array.new(len).map{ self[rand(self.length)].chr }.join
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.random(len=128, src=nil)
|
7
|
+
src ||=
|
8
|
+
[
|
9
|
+
'a'..'z',
|
10
|
+
'A'..'Z',
|
11
|
+
0..9,
|
12
|
+
].map{|v| v.to_a}.join + '!@#$%^&*()_-=,./'
|
13
|
+
return src.randomize(len)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'termios'
|
2
|
+
|
3
|
+
module Term
|
4
|
+
def countdown(time, *args, &block)
|
5
|
+
wait, br = args
|
6
|
+
if args[0].instance_of?(Hash)
|
7
|
+
wait = args[0][:wait]
|
8
|
+
br = args[0][:br]
|
9
|
+
end
|
10
|
+
wait ||= 0.05
|
11
|
+
br ||= '[\r\n]'
|
12
|
+
default = proc do |elapse|
|
13
|
+
t = time - elapse
|
14
|
+
t = 0 if t < 0
|
15
|
+
t *= 1000
|
16
|
+
$stdout.flush
|
17
|
+
printf("\r%6d", t.to_i)
|
18
|
+
end
|
19
|
+
block = default unless block
|
20
|
+
|
21
|
+
old = Termios.tcgetattr($stdin)
|
22
|
+
tio = Termios.tcgetattr($stdin)
|
23
|
+
tio.cc[Termios::VMIN] = 1
|
24
|
+
tio.cc[Termios::VTIME] = 0
|
25
|
+
tio.iflag &= ~Termios::ICRNL # see libcurses/screen/cbreak.c
|
26
|
+
tio.lflag &= ~Termios::ICANON
|
27
|
+
tio.lflag &= ~Termios::ECHO
|
28
|
+
Termios.tcsetattr($stdin, Termios::TCSANOW, tio)
|
29
|
+
|
30
|
+
begin
|
31
|
+
s = Time.now.to_f
|
32
|
+
e = s + time.to_f
|
33
|
+
while true
|
34
|
+
now = Time.now.to_f
|
35
|
+
block.call(now - s)
|
36
|
+
break if now >= e
|
37
|
+
sleep(wait)
|
38
|
+
s = $stdin.read_nonblock(1) rescue next
|
39
|
+
break if s =~ /#{br}/
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
ensure
|
43
|
+
Termios.tcsetattr($stdin, Termios::TCSANOW, old)
|
44
|
+
end
|
45
|
+
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
|
49
|
+
module_function :countdown
|
50
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'socket'
|
3
|
+
require 'timeout'
|
4
|
+
require 'webrick/config'
|
5
|
+
require 'webrick/log'
|
6
|
+
|
7
|
+
module WEBrick
|
8
|
+
class GenericServer
|
9
|
+
def start(&block)
|
10
|
+
raise ServerError, "already started." if @status != :Stop
|
11
|
+
server_type = @config[:ServerType] || SimpleServer
|
12
|
+
|
13
|
+
server_type.start{
|
14
|
+
@logger.info \
|
15
|
+
"#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
|
16
|
+
call_callback(:StartCallback)
|
17
|
+
|
18
|
+
@status = :Running
|
19
|
+
while @status == :Running
|
20
|
+
begin
|
21
|
+
if svrs = IO.select(@listeners, nil, nil, 2.0)
|
22
|
+
svrs[0].each{|svr|
|
23
|
+
@tokens.pop # blocks while no token is there.
|
24
|
+
if sock = accept_client(svr)
|
25
|
+
th = start_thread(sock, &block)
|
26
|
+
else
|
27
|
+
@tokens.push(nil)
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
rescue Errno::EBADF, IOError => ex
|
32
|
+
# if the listening socket was closed in GenericServer#shutdown,
|
33
|
+
# IO::select raise it.
|
34
|
+
rescue Exception => ex
|
35
|
+
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
|
36
|
+
@logger.error msg
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
@logger.info "going to shutdown ..."
|
41
|
+
call_callback(:StopCallback)
|
42
|
+
@logger.info "#{self.class}#start done."
|
43
|
+
@status = :Stop
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def start_thread(sock, &block)
|
50
|
+
begin
|
51
|
+
Thread.current[:WEBrickSocket] = sock
|
52
|
+
begin
|
53
|
+
addr = sock.peeraddr
|
54
|
+
@logger.debug "accept: #{addr[3]}:#{addr[1]}"
|
55
|
+
rescue SocketError
|
56
|
+
@logger.debug "accept: <address unknown>"
|
57
|
+
raise
|
58
|
+
end
|
59
|
+
call_callback(:AcceptCallback, sock)
|
60
|
+
block ? block.call(sock) : run(sock)
|
61
|
+
rescue Errno::ENOTCONN
|
62
|
+
@logger.debug "Errno::ENOTCONN raised"
|
63
|
+
rescue ServerError => ex
|
64
|
+
msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
|
65
|
+
@logger.error msg
|
66
|
+
rescue Exception => ex
|
67
|
+
@logger.error ex
|
68
|
+
ensure
|
69
|
+
@tokens.push(nil)
|
70
|
+
Thread.current[:WEBrickSocket] = nil
|
71
|
+
if addr
|
72
|
+
@logger.debug "close: #{addr[3]}:#{addr[1]}"
|
73
|
+
else
|
74
|
+
@logger.debug "close: <address unknown>"
|
75
|
+
end
|
76
|
+
sock.close
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end # end of GenericServer
|
80
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mechanize'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module WWW
|
6
|
+
class Mechanize
|
7
|
+
class Method
|
8
|
+
attr_accessor :method, :domain, :verbose, :agent, :req
|
9
|
+
|
10
|
+
def initialize(method, conf={}, verbose=nil)
|
11
|
+
@method = method
|
12
|
+
@domain = @method[:type] + '://' + @method[:domain]
|
13
|
+
@verbose = verbose
|
14
|
+
|
15
|
+
# user agent
|
16
|
+
logger = conf[:logger] || (conf[:log] && Logger.new(conf[:log]))
|
17
|
+
@agent = WWW::Mechanize.new{|a| a.log = logger if logger}
|
18
|
+
@agent.ca_file = conf[:ssl_cert] || '/usr/share/ssl/cert.pem'
|
19
|
+
@agent.user_agent_alias = conf[:ua_alias] if conf[:ua_alias]
|
20
|
+
@agent.user_agent = conf[:ua] if conf[:ua]
|
21
|
+
auth = @method[:auth]
|
22
|
+
@agent.auth(auth[:user], auth[:pass]) if auth
|
23
|
+
|
24
|
+
# request methods
|
25
|
+
psend = proc{|rq,r| do_send(rq,r)}
|
26
|
+
@req = {
|
27
|
+
:get => psend,
|
28
|
+
:post => psend,
|
29
|
+
:form => proc{|rq,r| do_form(rq,r)},
|
30
|
+
:regexp => proc{|rq,r| do_parse(rq,r)},
|
31
|
+
}
|
32
|
+
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def do
|
37
|
+
warn("#{@method[:name]}: retrieve by #{@method[:type]}") if @verbose
|
38
|
+
result = { :obj => {} }
|
39
|
+
|
40
|
+
@method[:requests].each do |req|
|
41
|
+
begin
|
42
|
+
if req.is_a?(String)
|
43
|
+
result = @req[:get].call(req, result)
|
44
|
+
elsif req.is_a?(Proc)
|
45
|
+
result = req.call(self, req, result)
|
46
|
+
elsif req.is_a?(Hash)
|
47
|
+
key = @req.keys.find{|k| req.key?(k)}
|
48
|
+
raise 'invalid method' unless key
|
49
|
+
result = @req[key].call(req, result)
|
50
|
+
else
|
51
|
+
raise 'unknown method'
|
52
|
+
end
|
53
|
+
rescue => e
|
54
|
+
warn("#{e}: #{req.to_s}") if @verbose
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
return result
|
59
|
+
end
|
60
|
+
|
61
|
+
def do_send(req, result) # POST / GET
|
62
|
+
m = (req.is_a?(Hash) && req[:post]) ? :post : :get
|
63
|
+
uri = @domain + (req.is_a?(Hash) ? req[m] : req)
|
64
|
+
params = req.is_a?(Hash) ? req[:params] : {}
|
65
|
+
warn("#{m} #{uri}") if @verbose
|
66
|
+
result[:page] = @agent.send(m.to_s, uri, params || {}) # reflective
|
67
|
+
return result
|
68
|
+
end
|
69
|
+
|
70
|
+
def do_form(req, result) # form.submit
|
71
|
+
raise 'no page at WWW.Mechanize.Method.do_form' unless result[:page]
|
72
|
+
|
73
|
+
name = req[:form]
|
74
|
+
f = result[:page].forms.find{|v| v.name == name} if name.is_a?(String)
|
75
|
+
f = result[:page].forms[name] if name.is_a?(Integer)
|
76
|
+
raise 'failed at WWW.Mechanize.Method.do_form' unless f
|
77
|
+
|
78
|
+
f.action = req[:action] if req[:action]
|
79
|
+
f.set_fields(req[:fields])
|
80
|
+
warn("submit #{f.action}") if @verbose
|
81
|
+
result[:page] = f.submit
|
82
|
+
return result
|
83
|
+
end
|
84
|
+
|
85
|
+
def do_parse(req, result) # parse
|
86
|
+
raise 'no page at WWW.Mechanize.Method.do_parse' unless result[:page]
|
87
|
+
|
88
|
+
if result[:page].body =~ /#{req[:regexp]}/im
|
89
|
+
warn("parse #{req[:regexp]}") if @verbose
|
90
|
+
if req[:result].is_a?(Hash)
|
91
|
+
req[:result].each do |key, val|
|
92
|
+
result[:obj][key] = $~[val]
|
93
|
+
end
|
94
|
+
elsif req[:result].is_a?(String)
|
95
|
+
$~.shift if $~.size > 1
|
96
|
+
result[:obj][req[:result].to_sym] = $~
|
97
|
+
else
|
98
|
+
$~.shift if $~.size > 1
|
99
|
+
result[:obj] = $~
|
100
|
+
end
|
101
|
+
else
|
102
|
+
raise 'parse failed at WWW.Mechanize.Method.do_parse'
|
103
|
+
end
|
104
|
+
|
105
|
+
return result
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.get(methods, opt)
|
110
|
+
v = opt[:verbose]
|
111
|
+
r = opt[:require]
|
112
|
+
c = opt[:config]
|
113
|
+
|
114
|
+
result = { :obj => {} }
|
115
|
+
methods = [ methods ] unless methods.is_a?(Array)
|
116
|
+
methods.each do |val|
|
117
|
+
m = Method.new(val, c, v)
|
118
|
+
result = m.do
|
119
|
+
break if r.all?{|k| result[:obj][k]}
|
120
|
+
end
|
121
|
+
|
122
|
+
return result
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gnn-rblibs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- tarao
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-28 00:00:00 +00:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.9
|
24
|
+
version:
|
25
|
+
description: Some Ruby tweaks for file operations, commandline parsers, etc.
|
26
|
+
email: tarao.gnn@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- .document
|
36
|
+
- .gitignore
|
37
|
+
- LICENSE
|
38
|
+
- README.rdoc
|
39
|
+
- Rakefile
|
40
|
+
- VERSION
|
41
|
+
- gnn-rblibs.gemspec
|
42
|
+
- lib/dir/each_leaf.rb
|
43
|
+
- lib/file/kind.rb
|
44
|
+
- lib/file/observer.rb
|
45
|
+
- lib/fileutils/sync.rb
|
46
|
+
- lib/getopt.rb
|
47
|
+
- lib/loadconf.rb
|
48
|
+
- lib/plugin.rb
|
49
|
+
- lib/process/invoke.rb
|
50
|
+
- lib/rbconfig/command.rb
|
51
|
+
- lib/string/random.rb
|
52
|
+
- lib/term/countdown.rb
|
53
|
+
- lib/webrick/single_thread_server.rb
|
54
|
+
- lib/www/mechanize/get.rb
|
55
|
+
- spec/gnn-rblibs_spec.rb
|
56
|
+
- spec/spec.opts
|
57
|
+
- spec/spec_helper.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: http://github.com/tarao/gnn-rblibs
|
60
|
+
licenses: []
|
61
|
+
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options:
|
64
|
+
- --charset=UTF-8
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
requirements: []
|
80
|
+
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 1.3.5
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: Some Ruby tweaks
|
86
|
+
test_files:
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
- spec/gnn-rblibs_spec.rb
|