gnn-rblibs 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|