bwkfanboy 1.4.1 → 2.0.0
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 +4 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +51 -0
- data/Procfile +1 -0
- data/README.rdoc +40 -77
- data/Rakefile +13 -48
- data/bin/bwkfanboy +47 -166
- data/bin/bwkfanboy_generate +7 -19
- data/bin/bwkfanboy_parse +21 -17
- data/bwkfanboy.gemspec +40 -0
- data/config.ru +3 -0
- data/doc/NEWS.rdoc +21 -79
- data/doc/plugin.rdoc +63 -79
- data/etc/bwkfanboy.yaml +2 -0
- data/etc/sinatra.rb +34 -0
- data/lib/bwkfanboy/cliconfig.rb +141 -0
- data/lib/bwkfanboy/cliutils.rb +114 -0
- data/lib/bwkfanboy/fetch.rb +22 -24
- data/lib/bwkfanboy/generator.rb +78 -0
- data/lib/bwkfanboy/home.rb +53 -0
- data/lib/bwkfanboy/meta.rb +5 -2
- data/lib/bwkfanboy/plugin.rb +247 -0
- data/lib/bwkfanboy/plugin_skeleton.erb +19 -23
- data/lib/bwkfanboy/server.rb +73 -0
- data/lib/bwkfanboy/utils.rb +39 -129
- data/plugins/bwk.rb +25 -0
- data/plugins/econlib.rb +22 -0
- data/plugins/freebsd-ports-update.rb +73 -0
- data/plugins/inc.rb +29 -0
- data/plugins/test.rb +29 -0
- data/public/.gitattributes +1 -0
- data/public/favicon.ico +0 -0
- data/public/jquery-1.7.2.min.js +0 -0
- data/public/list.js +111 -0
- data/public/loading.gif +0 -0
- data/public/style.css +54 -0
- data/shotgun.rb +20 -0
- data/test/example/.gitattributes +1 -0
- data/test/example/.gitignore +1 -0
- data/test/example/02/plugins/bwk.html +0 -0
- data/test/{plugins → example/02/plugins}/empty.rb +0 -0
- data/test/example/02/plugins/garbage.rb +1 -0
- data/test/example/02/plugins/inc.html +0 -0
- data/test/helper.rb +30 -27
- data/test/helper_cliutils.rb +34 -0
- data/test/test_cli.rb +86 -0
- data/test/test_fetch.rb +49 -18
- data/test/test_generate.rb +43 -16
- data/test/test_home.rb +33 -0
- data/test/test_plugin.rb +141 -0
- data/test/test_server.rb +21 -32
- data/views/list.haml +38 -0
- metadata +223 -110
- data/bin/bwkfanboy_fetch +0 -13
- data/bin/bwkfanboy_server +0 -126
- data/doc/README.erb +0 -114
- data/doc/README.rdoc +0 -141
- data/doc/TODO +0 -7
- data/doc/bwkfanboy_fetch.rdoc +0 -4
- data/doc/bwkfanboy_generate.rdoc +0 -7
- data/doc/bwkfanboy_parse.rdoc +0 -7
- data/doc/bwkfanboy_server.rdoc +0 -35
- data/doc/rakefile.rb +0 -59
- data/lib/bwkfanboy/generate.rb +0 -63
- data/lib/bwkfanboy/parser.rb +0 -156
- data/lib/bwkfanboy/plugins/bwk.rb +0 -33
- data/lib/bwkfanboy/plugins/econlib.rb +0 -34
- data/lib/bwkfanboy/plugins/freebsd-ports-update.rb +0 -76
- data/lib/bwkfanboy/plugins/inc.rb +0 -37
- data/lib/bwkfanboy/schema.js +0 -39
- data/test/popen4.sh +0 -4
- data/test/rake_git.rb +0 -36
- data/test/semis/Rakefile +0 -35
- data/test/semis/bwk.html +0 -393
- data/test/semis/bwk.json +0 -82
- data/test/semis/econlib.html +0 -21
- data/test/semis/inc.html +0 -1067
- data/test/semis/links.txt +0 -4
- data/test/test_parse.rb +0 -27
- data/test/xml-clean.sh +0 -8
- data/web/bwkfanboy.cgi +0 -36
data/lib/bwkfanboy/meta.rb
CHANGED
@@ -0,0 +1,247 @@
|
|
1
|
+
require 'msgpack'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Bwkfanboy
|
5
|
+
|
6
|
+
# Helpers for plugin authors.
|
7
|
+
module BH
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# FIXME: clean unsafe html for 'html' content_type
|
11
|
+
def clean t
|
12
|
+
return '' unless t
|
13
|
+
t
|
14
|
+
end
|
15
|
+
|
16
|
+
# Tries to parse _s_ string as a date.
|
17
|
+
# Return the result in ISO 8601 format.
|
18
|
+
def date(t)
|
19
|
+
DateTime.parse(BH.clean(t)).iso8601
|
20
|
+
rescue
|
21
|
+
DateTime.now.iso8601
|
22
|
+
end
|
23
|
+
|
24
|
+
# See test_plugin.rb
|
25
|
+
def all_set? t
|
26
|
+
return false unless t
|
27
|
+
|
28
|
+
if t.is_a?(Array)
|
29
|
+
return false if t.size == 0
|
30
|
+
|
31
|
+
t.each {|i|
|
32
|
+
return false unless i
|
33
|
+
return false if i.to_s.strip.size == 0
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
return false if t.to_s.strip.size == 0
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class PluginException < StandardError
|
44
|
+
def initialize msg
|
45
|
+
super msg
|
46
|
+
end
|
47
|
+
|
48
|
+
alias :orig_to_s :to_s
|
49
|
+
# looks clumsy
|
50
|
+
def to_s
|
51
|
+
"plugin: #{orig_to_s}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class PluginInvalidName < PluginException
|
56
|
+
end
|
57
|
+
|
58
|
+
class PluginNotFound < PluginException
|
59
|
+
end
|
60
|
+
|
61
|
+
class PluginNoOptions < PluginException
|
62
|
+
end
|
63
|
+
|
64
|
+
# Requires defined 'parse(streams)' method in plugin.
|
65
|
+
#
|
66
|
+
# Raises only PluginException on purpose.
|
67
|
+
class Plugin
|
68
|
+
include Enumerable
|
69
|
+
|
70
|
+
MAX_ENTRIES = 128
|
71
|
+
NAME_RE = /^[a-zA-Z0-9-]+$/
|
72
|
+
|
73
|
+
# [path] an array
|
74
|
+
# [name] plugin's name (without .rb extension)
|
75
|
+
# [opt] an array
|
76
|
+
# [&block] you can examine the Plugin object there
|
77
|
+
def initialize path, name, opt, &block
|
78
|
+
@path = path
|
79
|
+
raise PluginInvalidName, "name doesn't match #{NAME_RE}" unless validName?(name)
|
80
|
+
@name = name
|
81
|
+
@origin = nil # a path where plugin was found
|
82
|
+
@syslib = File.dirname __FILE__
|
83
|
+
|
84
|
+
# Variables for plugin authours
|
85
|
+
@opt = (opt && opt.map(&:to_s)) || []
|
86
|
+
@uri = []
|
87
|
+
@enc = 'UTF-8'
|
88
|
+
@version = 1
|
89
|
+
@copyright = ''
|
90
|
+
@title = ''
|
91
|
+
@content_type = ''
|
92
|
+
|
93
|
+
@data = []
|
94
|
+
load &block
|
95
|
+
end
|
96
|
+
|
97
|
+
attr_accessor :origin
|
98
|
+
attr_accessor :uri, :enc, :version, :copyright, :title, :content_type
|
99
|
+
|
100
|
+
def validName? name
|
101
|
+
name =~ NAME_RE
|
102
|
+
end
|
103
|
+
|
104
|
+
def each &b
|
105
|
+
@data.each &b
|
106
|
+
end
|
107
|
+
|
108
|
+
def << obj
|
109
|
+
return @data if full?
|
110
|
+
|
111
|
+
['title', 'link', 'updated', 'author', 'content'].each {|idx|
|
112
|
+
obj[idx] &&= BH.clean obj[idx]
|
113
|
+
raise PluginException, "empty '#{idx}' in the entry #{obj.inspect}" if obj[idx].size == 0
|
114
|
+
}
|
115
|
+
|
116
|
+
@data << obj
|
117
|
+
end
|
118
|
+
|
119
|
+
def full?
|
120
|
+
@data.size >= MAX_ENTRIES
|
121
|
+
end
|
122
|
+
|
123
|
+
def [] index
|
124
|
+
@data[index]
|
125
|
+
end
|
126
|
+
|
127
|
+
def size
|
128
|
+
@data.size
|
129
|
+
end
|
130
|
+
|
131
|
+
def pack stream = ''
|
132
|
+
# hopefully, urf8 will survive
|
133
|
+
MessagePack.pack export, stream
|
134
|
+
end
|
135
|
+
|
136
|
+
def export
|
137
|
+
{
|
138
|
+
'channel' => {
|
139
|
+
'updated' => entryMostRecent,
|
140
|
+
'id' => @uri.to_s,
|
141
|
+
'author' => @copyright,
|
142
|
+
'title' => @title,
|
143
|
+
'link' => @uri.first,
|
144
|
+
'x_entries_content_type' => @content_type,
|
145
|
+
},
|
146
|
+
'x_entries' => @data
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
# We can do this while adding a new entry, not here
|
151
|
+
def entryMostRecent
|
152
|
+
return nil if @data.size == 0
|
153
|
+
|
154
|
+
max = DateTime.parse @data.sample['updated']
|
155
|
+
@data.each {|idx|
|
156
|
+
cur = DateTime.parse idx['updated']
|
157
|
+
max = cur if max < cur
|
158
|
+
}
|
159
|
+
|
160
|
+
return max.iso8601
|
161
|
+
end
|
162
|
+
|
163
|
+
def load
|
164
|
+
raise PluginException, 'invalid search path' unless @path && @path.respond_to?(:each)
|
165
|
+
|
166
|
+
p = nil
|
167
|
+
@path.each {|idx|
|
168
|
+
contents = Dir.glob "#{idx}/*.rb"
|
169
|
+
pos = contents.index "#{idx}/#{@name}.rb"
|
170
|
+
if pos && p = contents[pos]
|
171
|
+
@origin = idx
|
172
|
+
break
|
173
|
+
end
|
174
|
+
}
|
175
|
+
|
176
|
+
raise PluginNotFound, "'#{@name}' not found" unless p
|
177
|
+
|
178
|
+
begin
|
179
|
+
instance_eval File.read(p)
|
180
|
+
rescue Exception
|
181
|
+
raise PluginException, "'#{@name}' failed to parse: #{$!}"
|
182
|
+
end
|
183
|
+
|
184
|
+
unless BH.all_set?(uri)
|
185
|
+
raise PluginException, 'uri must be an array of strings' if @opt.size != 0
|
186
|
+
raise PluginNoOptions, 'don\'t we forget about additional options?'
|
187
|
+
end
|
188
|
+
raise PluginException, 'enc is unset' unless BH.all_set?(enc)
|
189
|
+
raise PluginException, 'version must be an integer' unless BH.all_set?(version)
|
190
|
+
raise PluginException, 'copyright is unset' unless BH.all_set?(copyright)
|
191
|
+
raise PluginException, 'title is unset' unless BH.all_set?(title)
|
192
|
+
raise PluginException, 'content_type is unset' unless BH.all_set?(content_type)
|
193
|
+
|
194
|
+
# use this, for example, to print a message to user that loading
|
195
|
+
# was fine
|
196
|
+
yield self if block_given?
|
197
|
+
end
|
198
|
+
|
199
|
+
# Runs loaded plugin's parser
|
200
|
+
def run_parser streams
|
201
|
+
ok = streams ? true : false
|
202
|
+
streams.each {|i| ok = false unless i.respond_to?(:eof) } if streams
|
203
|
+
raise PluginException, 'parser expects a valid array of IO objects' unless ok
|
204
|
+
|
205
|
+
begin
|
206
|
+
parse streams
|
207
|
+
rescue Exception
|
208
|
+
raise PluginException, "'#{@name}' failed to parse: #{$!}"
|
209
|
+
end
|
210
|
+
|
211
|
+
check
|
212
|
+
end
|
213
|
+
|
214
|
+
def check
|
215
|
+
raise PluginException, "it ain't grab anything" if @data.size == 0
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
module PluginInfo
|
221
|
+
extend self
|
222
|
+
|
223
|
+
def about path, name, opt
|
224
|
+
p = Plugin.new path, name, opt
|
225
|
+
r = {}
|
226
|
+
['title', 'version', 'copyright', 'uri'].each {|idx|
|
227
|
+
r[idx] = p.send(idx)
|
228
|
+
}
|
229
|
+
r
|
230
|
+
end
|
231
|
+
|
232
|
+
def getList path
|
233
|
+
r = []
|
234
|
+
path.each {|idx|
|
235
|
+
dir = idx.to_s
|
236
|
+
e = { dir => [] }
|
237
|
+
Dir.glob("#{dir}/*.rb").each {|file|
|
238
|
+
e[dir] << File.basename(file, '.rb')
|
239
|
+
}
|
240
|
+
r << e
|
241
|
+
}
|
242
|
+
|
243
|
+
r
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
end
|
@@ -1,30 +1,26 @@
|
|
1
|
-
# This is a skeleton for a <%=
|
2
|
-
# plugins work please read doc/plugins.rdoc file from <%=
|
1
|
+
# This is a skeleton for a <%= Meta::NAME %> <%= Meta::VERSION %> plugin. To understand how
|
2
|
+
# plugins work please read doc/plugins.rdoc file from <%= Meta::NAME %>'s
|
3
3
|
# distribution.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
VERSION = 1
|
12
|
-
COPYRIGHT = '(c) <%= DateTime.now.year %> <%= Etc.getpwuid(Process.euid)[:gecos] %>'
|
13
|
-
TITLE = "News from example.org"
|
14
|
-
CONTENT_TYPE = 'html'
|
15
|
-
end
|
5
|
+
@uri << 'http://example.org/news'
|
6
|
+
@enc = 'UTF-8'
|
7
|
+
@version = 1
|
8
|
+
@copyright = '(c) <%= DateTime.now.year %> <%= Etc.getpwuid(Process.euid)[:gecos] %>'
|
9
|
+
@title = "News from example.org"
|
10
|
+
@content_type = 'html'
|
16
11
|
|
17
|
-
|
18
|
-
|
19
|
-
doc = Nokogiri::HTML
|
20
|
-
doc.xpath("XPATH QUERY").each {|
|
21
|
-
t =
|
22
|
-
l =
|
23
|
-
u = date
|
24
|
-
a =
|
25
|
-
c =
|
12
|
+
def parse streams
|
13
|
+
streams.each do |io|
|
14
|
+
doc = Nokogiri::HTML io, nil, @enc
|
15
|
+
doc.xpath("XPATH QUERY").each {|idx|
|
16
|
+
t = idx.xpath("XPATH QUERY").text
|
17
|
+
l = idx.xpath("XPATH QUERY").text
|
18
|
+
u = BH.date idx.xpath("XPATH QUERY").text
|
19
|
+
a = idx.xpath("XPATH QUERY").text
|
20
|
+
c = idx.xpath("XPATH QUERY").text
|
26
21
|
|
27
|
-
self << { title
|
22
|
+
self << { 'title' => t, 'link' => l, 'updated' => u,
|
23
|
+
'author' => a, 'content' => c }
|
28
24
|
}
|
29
25
|
end
|
30
26
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'haml'
|
3
|
+
require 'sinatra/base'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
require_relative 'home'
|
7
|
+
require_relative 'utils'
|
8
|
+
require_relative '../../etc/sinatra'
|
9
|
+
|
10
|
+
module Bwkfanboy
|
11
|
+
class MyApp < Sinatra::Base
|
12
|
+
MySinatraConfig.read self
|
13
|
+
|
14
|
+
set :home, Home.new
|
15
|
+
set :public_folder, CliUtils::DIR_LIB_SRC.parent.parent + 'public'
|
16
|
+
set :views, CliUtils::DIR_LIB_SRC.parent.parent + 'views'
|
17
|
+
|
18
|
+
use Rack::Deflater
|
19
|
+
|
20
|
+
def getOpts opts
|
21
|
+
return [] unless opts
|
22
|
+
opts.gsub! /\s+/, ' '
|
23
|
+
opts.strip.split ' '
|
24
|
+
end
|
25
|
+
|
26
|
+
# List all plugins
|
27
|
+
get '/' do
|
28
|
+
list = PluginInfo.getList settings.home.conf[:plugins_path]
|
29
|
+
haml :list, locals: {
|
30
|
+
meta: Meta,
|
31
|
+
list: list
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
get %r{/info/([a-zA-Z0-9_-]+)} do |plugin|
|
36
|
+
cache_control :no_cache
|
37
|
+
opts = getOpts params['o']
|
38
|
+
begin
|
39
|
+
PluginInfo.about(settings.home.conf[:plugins_path], plugin, opts).to_json
|
40
|
+
rescue PluginInvalidName, PluginNoOptions
|
41
|
+
halt 400, $!.to_s
|
42
|
+
rescue PluginNotFound
|
43
|
+
halt 404, $!.to_s
|
44
|
+
rescue PluginException
|
45
|
+
halt 500, $!.to_s
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
get %r{/([a-zA-Z0-9_-]+)} do |plugin|
|
50
|
+
begin
|
51
|
+
opts = getOpts params['o']
|
52
|
+
r = Utils.atom(settings.home.conf[:plugins_path], plugin, opts).to_s
|
53
|
+
|
54
|
+
# Search for <updated> tag and set Last-Modified header
|
55
|
+
if (m = r.match('<updated>(.+?)</updated>'))
|
56
|
+
headers 'Last-Modified' => DateTime.parse(m.to_s).httpdate
|
57
|
+
end
|
58
|
+
content_type 'application/atom+xml; charset=UTF-8'
|
59
|
+
headers 'Content-Disposition' => "inline; filename=\"#{Meta::NAME}-#{plugin}.xml"
|
60
|
+
|
61
|
+
r
|
62
|
+
rescue PluginInvalidName, PluginNoOptions
|
63
|
+
halt 400, $!.to_s
|
64
|
+
rescue PluginNotFound
|
65
|
+
halt 404, $!.to_s
|
66
|
+
rescue FetchException, PluginException, GeneratorException
|
67
|
+
halt 500, $!.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
run! if app_file == $0
|
72
|
+
end
|
73
|
+
end
|
data/lib/bwkfanboy/utils.rb
CHANGED
@@ -1,148 +1,58 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'erb'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'etc'
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require_relative '
|
5
|
+
require_relative 'cliutils'
|
6
|
+
require_relative 'fetch'
|
7
|
+
require_relative 'plugin'
|
8
|
+
require_relative 'generator'
|
8
9
|
|
9
10
|
module Bwkfanboy
|
10
|
-
module Meta
|
11
|
-
USER_AGENT = "#{NAME}/#{VERSION} (#{RUBY_PLATFORM}; N; #{Encoding.default_external.name}; #{RUBY_ENGINE}; rv:#{RUBY_VERSION}.#{RUBY_PATCHLEVEL})"
|
12
|
-
PLUGIN_CLASS = 'Page'
|
13
|
-
DIR_TMP = "/tmp/#{Meta::NAME}/#{ENV['USER']}"
|
14
|
-
DIR_LOG = "#{DIR_TMP}/log"
|
15
|
-
LOG_MAXSIZE = 64*1024
|
16
|
-
PLUGIN_NAME = /^[ a-zA-Z0-9_-]+$/
|
17
|
-
PLUGIN_OPTS = /^[ a-zA-Z'"0-9_-]+$/
|
18
|
-
end
|
19
|
-
|
20
11
|
module Utils
|
21
|
-
|
22
|
-
|
23
|
-
self.cfg = Hash.new()
|
24
|
-
cfg[:verbose] = 0
|
25
|
-
cfg[:log] = "#{Meta::DIR_LOG}/general.log"
|
26
|
-
|
27
|
-
def self.warnx(t)
|
28
|
-
m = File.basename($0) +" warning: "+ t + "\n";
|
29
|
-
$stderr.print(m);
|
30
|
-
log.warn(m.chomp) if log
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.errx(ec, t)
|
34
|
-
m = File.basename($0) +" error: "+ t + "\n"
|
35
|
-
$stderr.print(m);
|
36
|
-
log.error(m.chomp) if log
|
37
|
-
exit(ec)
|
38
|
-
end
|
12
|
+
extend self
|
39
13
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
14
|
+
# [template] a full path to a .erb file
|
15
|
+
# [desiredName] a future skeleton name
|
16
|
+
def skeletonCreate template, desiredName
|
17
|
+
t = ERB.new File.read template
|
18
|
+
t.filename = template # to report errors relative to this file
|
19
|
+
begin
|
20
|
+
md5_system = Digest::MD5.hexdigest t.result(binding)
|
21
|
+
rescue Exception
|
22
|
+
CliUtils.errx EX_SOFTWARE, "cannot read the template: #{$!}"
|
45
23
|
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.vewarnx(level, t)
|
49
|
-
warnx(t) if cfg[:verbose] >= level
|
50
|
-
end
|
51
24
|
|
52
|
-
|
53
|
-
|
54
|
-
if ! File.writable?(Meta::DIR_TMP) then
|
25
|
+
if ! File.exists?(desiredName)
|
26
|
+
# create a new skeleton
|
55
27
|
begin
|
56
|
-
|
57
|
-
Meta::DIR_TMP.split('/')[1..-1].each {|i|
|
58
|
-
t += i + '/'
|
59
|
-
Dir.mkdir(t) if ! Dir.exists?(t)
|
60
|
-
}
|
28
|
+
File.open(desiredName, 'w+') { |fp| fp.puts t.result(binding) }
|
61
29
|
rescue
|
62
|
-
|
30
|
+
CliUtils.errx EX_IOERR, "cannot write the skeleton: #{$!}"
|
31
|
+
end
|
32
|
+
else
|
33
|
+
# warn a careless user
|
34
|
+
if md5_system != Digest::MD5.file(desiredName).hexdigest
|
35
|
+
CliUtils.warnx "#{desiredName} already exists"
|
36
|
+
return false
|
63
37
|
end
|
64
38
|
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.log_start()
|
68
|
-
dir_tmp_create()
|
69
|
-
begin
|
70
|
-
Dir.mkdir(Meta::DIR_LOG) if ! File.writable?(Meta::DIR_LOG)
|
71
|
-
log = Logger.new(cfg[:log], 2, Meta::LOG_MAXSIZE)
|
72
|
-
rescue
|
73
|
-
warnx("cannot open log #{cfg[:log]}");
|
74
|
-
return nil
|
75
|
-
end
|
76
|
-
log.level = Logger::DEBUG
|
77
|
-
log.datetime_format = "%H:%M:%S"
|
78
|
-
log.info("#{$0} starting")
|
79
|
-
log
|
80
|
-
end
|
81
|
-
self.log = log_start()
|
82
|
-
|
83
|
-
# Loads (via <tt>require()</tt>) a Ruby code from _path_ (the full path to
|
84
|
-
# the file). <em>class_name</em> is the name of the class to check
|
85
|
-
# for existence after successful plugin loading.
|
86
|
-
def self.plugin_load(path, class_name)
|
87
|
-
begin
|
88
|
-
require(path)
|
89
|
-
# TODO get rid of eval()
|
90
|
-
fail "class #{class_name} isn't defined" if (! eval("defined?#{class_name}") || ! eval(class_name).is_a?(Class) )
|
91
|
-
rescue LoadError
|
92
|
-
errx(1, "cannot load plugin '#{path}' #{$!}");
|
93
|
-
rescue Exception
|
94
|
-
errx(1, "plugin '#{path}' has errors: #{$!}\n\nBacktrace:\n\n#{$!.backtrace.join("\n")}")
|
95
|
-
end
|
96
|
-
end
|
97
39
|
|
98
|
-
|
99
|
-
def self.plugin_opts(a)
|
100
|
-
opt = a.size >= 2 ? a[1..-1] : ''
|
40
|
+
true
|
101
41
|
end
|
102
42
|
|
43
|
+
# Do all the work of reading the plugin, parsing and generating the
|
44
|
+
# atom feed.
|
45
|
+
# FIXME: ensure all streams are closed
|
46
|
+
def atom pluginsPath, name, opt
|
47
|
+
p = Plugin.new pluginsPath, name, opt
|
48
|
+
streams = Fetch.openStreams p.uri
|
49
|
+
p.run_parser(streams)
|
103
50
|
|
104
|
-
|
105
|
-
|
106
|
-
# program does.
|
107
|
-
#
|
108
|
-
# If _o_ is non nil function parses _arr_ immediately, otherwise it
|
109
|
-
# only creates +OptionParser+ object and return it (if _simple_ is
|
110
|
-
# false). See <tt>bwkfanboy</tt> script for examples.
|
111
|
-
def self.cl_parse(arr, banner, o = nil, simple = false)
|
112
|
-
if ! o then
|
113
|
-
o = OptionParser.new
|
114
|
-
o.banner = banner
|
115
|
-
o.on('-v', 'Be more verbose.') { |i| Bwkfanboy::Utils.cfg[:verbose] += 1 }
|
116
|
-
o.on('-V', 'Show version & exit.') { |i|
|
117
|
-
puts Bwkfanboy::Meta::VERSION
|
118
|
-
exit 0
|
119
|
-
}
|
120
|
-
return o if ! simple
|
121
|
-
end
|
51
|
+
r = Generator.atom p.export
|
52
|
+
Fetch.closeStreams streams
|
122
53
|
|
123
|
-
|
124
|
-
o.parse!(arr)
|
125
|
-
rescue
|
126
|
-
Bwkfanboy::Utils.errx(1, $!.to_s)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
# used in CGI and WEBrick examples
|
131
|
-
def self.cmd_run(cmd)
|
132
|
-
so = sr = ''
|
133
|
-
status = Open4::popen4(cmd) { |pid, stdin, stdout, stderr|
|
134
|
-
so = stdout.read
|
135
|
-
sr = stderr.read
|
136
|
-
}
|
137
|
-
[status.exitstatus, sr, so]
|
138
|
-
end
|
139
|
-
|
140
|
-
def self.gem_dir_system
|
141
|
-
t = ["#{File.dirname(File.expand_path($0))}/../lib/#{Meta::NAME}",
|
142
|
-
"#{Gem.dir}/gems/#{Meta::NAME}-#{Meta::VERSION}/lib/#{Meta::NAME}"]
|
143
|
-
t.each {|i| return i if File.readable?(i) }
|
144
|
-
raise "both paths are invalid: #{t}"
|
54
|
+
r
|
145
55
|
end
|
146
56
|
|
147
|
-
end
|
57
|
+
end
|
148
58
|
end
|