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.
Files changed (81) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +7 -0
  3. data/Gemfile.lock +51 -0
  4. data/Procfile +1 -0
  5. data/README.rdoc +40 -77
  6. data/Rakefile +13 -48
  7. data/bin/bwkfanboy +47 -166
  8. data/bin/bwkfanboy_generate +7 -19
  9. data/bin/bwkfanboy_parse +21 -17
  10. data/bwkfanboy.gemspec +40 -0
  11. data/config.ru +3 -0
  12. data/doc/NEWS.rdoc +21 -79
  13. data/doc/plugin.rdoc +63 -79
  14. data/etc/bwkfanboy.yaml +2 -0
  15. data/etc/sinatra.rb +34 -0
  16. data/lib/bwkfanboy/cliconfig.rb +141 -0
  17. data/lib/bwkfanboy/cliutils.rb +114 -0
  18. data/lib/bwkfanboy/fetch.rb +22 -24
  19. data/lib/bwkfanboy/generator.rb +78 -0
  20. data/lib/bwkfanboy/home.rb +53 -0
  21. data/lib/bwkfanboy/meta.rb +5 -2
  22. data/lib/bwkfanboy/plugin.rb +247 -0
  23. data/lib/bwkfanboy/plugin_skeleton.erb +19 -23
  24. data/lib/bwkfanboy/server.rb +73 -0
  25. data/lib/bwkfanboy/utils.rb +39 -129
  26. data/plugins/bwk.rb +25 -0
  27. data/plugins/econlib.rb +22 -0
  28. data/plugins/freebsd-ports-update.rb +73 -0
  29. data/plugins/inc.rb +29 -0
  30. data/plugins/test.rb +29 -0
  31. data/public/.gitattributes +1 -0
  32. data/public/favicon.ico +0 -0
  33. data/public/jquery-1.7.2.min.js +0 -0
  34. data/public/list.js +111 -0
  35. data/public/loading.gif +0 -0
  36. data/public/style.css +54 -0
  37. data/shotgun.rb +20 -0
  38. data/test/example/.gitattributes +1 -0
  39. data/test/example/.gitignore +1 -0
  40. data/test/example/02/plugins/bwk.html +0 -0
  41. data/test/{plugins → example/02/plugins}/empty.rb +0 -0
  42. data/test/example/02/plugins/garbage.rb +1 -0
  43. data/test/example/02/plugins/inc.html +0 -0
  44. data/test/helper.rb +30 -27
  45. data/test/helper_cliutils.rb +34 -0
  46. data/test/test_cli.rb +86 -0
  47. data/test/test_fetch.rb +49 -18
  48. data/test/test_generate.rb +43 -16
  49. data/test/test_home.rb +33 -0
  50. data/test/test_plugin.rb +141 -0
  51. data/test/test_server.rb +21 -32
  52. data/views/list.haml +38 -0
  53. metadata +223 -110
  54. data/bin/bwkfanboy_fetch +0 -13
  55. data/bin/bwkfanboy_server +0 -126
  56. data/doc/README.erb +0 -114
  57. data/doc/README.rdoc +0 -141
  58. data/doc/TODO +0 -7
  59. data/doc/bwkfanboy_fetch.rdoc +0 -4
  60. data/doc/bwkfanboy_generate.rdoc +0 -7
  61. data/doc/bwkfanboy_parse.rdoc +0 -7
  62. data/doc/bwkfanboy_server.rdoc +0 -35
  63. data/doc/rakefile.rb +0 -59
  64. data/lib/bwkfanboy/generate.rb +0 -63
  65. data/lib/bwkfanboy/parser.rb +0 -156
  66. data/lib/bwkfanboy/plugins/bwk.rb +0 -33
  67. data/lib/bwkfanboy/plugins/econlib.rb +0 -34
  68. data/lib/bwkfanboy/plugins/freebsd-ports-update.rb +0 -76
  69. data/lib/bwkfanboy/plugins/inc.rb +0 -37
  70. data/lib/bwkfanboy/schema.js +0 -39
  71. data/test/popen4.sh +0 -4
  72. data/test/rake_git.rb +0 -36
  73. data/test/semis/Rakefile +0 -35
  74. data/test/semis/bwk.html +0 -393
  75. data/test/semis/bwk.json +0 -82
  76. data/test/semis/econlib.html +0 -21
  77. data/test/semis/inc.html +0 -1067
  78. data/test/semis/links.txt +0 -4
  79. data/test/test_parse.rb +0 -27
  80. data/test/xml-clean.sh +0 -8
  81. data/web/bwkfanboy.cgi +0 -36
@@ -0,0 +1,2 @@
1
+ ---
2
+ :foobar: foobar
@@ -0,0 +1,34 @@
1
+ # This file is loaded by the application at start-up.
2
+
3
+ # Disable useless rack logger completely! Yay, yay!
4
+ module Rack
5
+ class CommonLogger
6
+ def call(env)
7
+ # do nothing
8
+ @app.call(env)
9
+ end
10
+ end
11
+ end
12
+
13
+ module Bwkfanboy
14
+ module MySinatraConfig
15
+
16
+ def self.read o
17
+ o.configure do
18
+ # heroku logs
19
+ $stdout.sync = true
20
+ end
21
+
22
+ o.configure :production, :development do
23
+ end
24
+
25
+ o.configure :development do
26
+ end
27
+
28
+ o.configure :production do
29
+ o.set :haml, ugly: true
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,141 @@
1
+ # :erb: ruby-cli
2
+
3
+ require 'pathname'
4
+ require 'yaml'
5
+ require 'optparse'
6
+ require 'shellwords'
7
+
8
+ require_relative 'cliutils'
9
+
10
+ module Bwkfanboy
11
+
12
+ # Load configuration from 3 places (starting from least significant):
13
+ # config file, env variable, command line.
14
+ class CliConfig
15
+ # Possible config file locations.
16
+ DIR_CONFIG = [Pathname.new(Dir.home) + ".#{Meta::NAME}",
17
+ Pathname.new('/etc'),
18
+ Pathname.new('/usr/etc'),
19
+ Pathname.new('/usr/local/etc'),
20
+ CliUtils::DIR_LIB_SRC.parent.parent + 'etc']
21
+
22
+ # Example:
23
+ #
24
+ # conf = CliConfig.new
25
+ # conf[:my_option] = 123
26
+ # conf.load
27
+ def initialize
28
+ @conf = Hash.new
29
+ @conf[:verbose] = 0
30
+ @conf[:banner] = "Usage: #{File.basename($0)} [options]"
31
+ @conf[:config_name] = Meta::NAME + '.yaml'
32
+ @conf[:config_env] = Meta::NAME.upcase + '_CONF'
33
+ @conf[:config_dirs] = DIR_CONFIG
34
+ end
35
+
36
+ # Setter for @conf
37
+ def []=(key, val)
38
+ CliUtils.verbose = val if key == :verbose # sync verbosity levels
39
+ @conf[key] = val
40
+ end
41
+
42
+ # Getter for @conf
43
+ def [](key)
44
+ @conf[key]
45
+ end
46
+
47
+ # Return a full path to a config file or nil if no config file found.
48
+ def getConfigPath
49
+ if @conf[:config_name].index('/')
50
+ return @conf[:config_name] if File.file?(@conf[:config_name])
51
+ else
52
+ @conf[:config_dirs].each {|i|
53
+ r = Pathname.new(i) + @conf[:config_name]
54
+ return r if File.file?(r)
55
+ }
56
+ end
57
+
58
+ CliUtils.warnx "no config file '#{@conf[:config_name]}' found" if @conf[:verbose] >= 2
59
+ return nil
60
+ end
61
+
62
+ # Load a config from file. Return true on success or false otherwise.
63
+ def loadFile
64
+ file = getConfigPath
65
+ return false unless file
66
+
67
+ CliUtils::veputs(2, "Loading #{File.basename(file)}... " + CliUtils::NNL_MARK)
68
+ myconf = YAML.load_file(file) rescue CliUtils.errx(EX_CONFIG, "cannot parse config #{file}: #{$!}")
69
+ # preserve existing values
70
+ @conf.merge!(myconf) {|key, oldval, newval| oldval }
71
+ CliUtils::veputs 2, "OK"
72
+ return true
73
+ end
74
+
75
+ # Check if options in array opts are in @conf.
76
+ def requiredOptions?(opts)
77
+ opts.each {|idx|
78
+ if !@conf.key?(idx.to_sym) || !@conf[idx.to_sym]
79
+ CliUtils.errx EX_CONFIG, "option #{idx} is either nil or missing"
80
+ end
81
+ }
82
+ end
83
+
84
+ # Parse CLO and env variable. If block is given it is passed with
85
+ # OptionParser object as a parameter.
86
+ def optParse
87
+ OptionParser.new do |o|
88
+ o.on('-v', 'Be more verbose.') { |i|
89
+ self[:verbose] += 1
90
+ }
91
+ o.on('-V', '--version', 'Show version & exit.') { |i|
92
+ puts Meta::VERSION
93
+ exit EX_OK
94
+ }
95
+ o.on('--config NAME',
96
+ "Set a config name or file",
97
+ "(default is #{@conf[:config_name]}).") {|arg|
98
+ @conf[:config_name] = arg
99
+ }
100
+ o.on('--config-dirs', 'Show possible config locations.') {
101
+ mark = false
102
+ @conf[:config_dirs].each { |idx|
103
+ f = Pathname(idx) + @conf[:config_name]
104
+ if File.file?(f) && !mark
105
+ puts "* #{f}"
106
+ mark = true
107
+ else
108
+ puts " #{f}"
109
+ end
110
+ }
111
+ exit EX_OK
112
+ }
113
+
114
+ yield o if block_given?
115
+ o.banner = @conf[:banner]
116
+
117
+ env = nil
118
+ env = ENV[@conf[:config_env]].shellsplit if ENV.key?(@conf[:config_env])
119
+
120
+ begin
121
+ [env, ARGV].each { |i| o.parse!(i) if i }
122
+ rescue
123
+ CliUtils.errx EX_USAGE, $!.to_s
124
+ end
125
+ end
126
+ end
127
+
128
+ # Parse CLO, env variables and load config file.
129
+ #
130
+ # [reqOpts] an array of requied options
131
+ # [&block] a optional block for OptionParser
132
+ def load(reqOpts = [], &block)
133
+ optParse(&block)
134
+ loadFile
135
+ requiredOptions?(reqOpts)
136
+ end
137
+
138
+ end
139
+ end
140
+
141
+ # Don't remove this: falsework/2.0.0/ruby-cli/2012-03-05T05:04:11+02:00
@@ -0,0 +1,114 @@
1
+ # :erb: ruby-cli
2
+
3
+ require 'pp'
4
+ require 'open4'
5
+ require 'pathname'
6
+
7
+ require_relative 'meta'
8
+
9
+ module Bwkfanboy
10
+
11
+ # Preferable exit codes. See sysexits(3) in FreeBSD.
12
+ EX_OK = 0
13
+ EX_USAGE = 64
14
+ EX_DATAERR = 65
15
+ EX_NOINPUT = 66
16
+ EX_NOUSER = 67
17
+ EX_NOHOST = 68
18
+ EX_UNAVAILABLE = 69
19
+ EX_SOFTWARE = 70
20
+ EX_OSERR = 71
21
+ EX_OSFILE = 72
22
+ EX_CANTCREAT = 73
23
+ EX_IOERR = 74
24
+ EX_TEMPFAIL = 75
25
+ EX_PROTOCOL = 76
26
+ EX_NOPERM = 77
27
+ EX_CONFIG = 78
28
+
29
+ # Common routines useful in any CLI program.
30
+ class CliUtils
31
+ # Physical location of program libraries.
32
+ DIR_LIB_SRC = Pathname.new File.dirname(__FILE__)
33
+ # veputs uses this to decide to put a newline or not to put.
34
+ NNL_MARK = '__NNL__'
35
+
36
+ # Class-wide verbosity level.
37
+ @@verbose = 0
38
+
39
+ # Setter.
40
+ def self.verbose=(val)
41
+ @@verbose = val
42
+ end
43
+
44
+ # Getter.
45
+ def self.getVerbose
46
+ @@verbose
47
+ end
48
+
49
+ # A handy check. Use it like:
50
+ #
51
+ # puts (CliUtils.debug ? "DEBUG mode" : "")
52
+ def self.debug
53
+ @@verbose >= 2
54
+ end
55
+
56
+ # A handy method that return a nicely formatted current global
57
+ # backtrace.
58
+ def self.getBacktrace
59
+ "#{$!}\n\nBacktrace:\n\n#{$!.backtrace.join("\n")}"
60
+ end
61
+
62
+ # Print an error msg & exit if exit_code > 0.
63
+ def self.errx(exit_code = 0, msg)
64
+ $stderr.puts File.basename($0) + ' error: ' + msg.to_s
65
+ exit exit_code if exit_code > 0
66
+ end
67
+
68
+ # Print a warning.
69
+ def self.warnx(msg)
70
+ $stderr.puts File.basename($0) + ' warning: ' + msg.to_s
71
+ end
72
+
73
+ # [level] Verbosity level.
74
+ # [msg] A message to print.
75
+ #
76
+ # Don't print msg with a newline if it contains NNL_MARK at the end.
77
+ def self.veputs(level, msg)
78
+ t = msg.dup
79
+
80
+ nnl = false
81
+ if t.match(/#{NNL_MARK}$/)
82
+ t.sub!(/#{$&}/, '')
83
+ nnl = true
84
+ end
85
+
86
+ if @@verbose >= level
87
+ nnl ? print(t) : print("#{t}\n")
88
+ $stdout.flush
89
+ end
90
+ end
91
+
92
+ # Analogue to a shell command +which+.
93
+ def self.which(file)
94
+ return true if file =~ %r%\A/% and File.exist? file
95
+
96
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |path|
97
+ File.exist? File.join(path, file)
98
+ end
99
+ end
100
+
101
+ # Execute cmd and return an array [exit_status, stderr, stdout].
102
+ def self.exec(cmd)
103
+ so = sr = ''
104
+ status = Open4::popen4(cmd) { |pid, stdin, stdout, stderr|
105
+ so = stdout.read
106
+ sr = stderr.read
107
+ }
108
+ [status.exitstatus, sr, so]
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ # Don't remove this: falsework/2.0.0/ruby-cli/2012-03-05T05:04:11+02:00
@@ -1,36 +1,34 @@
1
1
  require 'open-uri'
2
2
 
3
- require_relative 'utils'
4
-
5
3
  module Bwkfanboy
6
- class Fetch
7
-
8
- # If no block given, return contents of fetch'ed URI. Otherwise,
9
- # execute the block with 1 parameter--stream.
10
- def self.cat(uri)
11
- uri.chomp!
12
4
 
13
- Bwkfanboy::Utils.veputs(1, "fetching #{uri}\n")
5
+ class FetchException < StandardError
6
+ end
7
+
8
+ module Fetch
9
+ extend self
10
+
11
+ # Return an array of opened streams.
12
+ #
13
+ # [uri] an array of URIs
14
+ def openStreams uri
15
+ return nil unless uri
14
16
 
17
+ streams = []
15
18
  begin
16
- open(uri, "User-Agent" => Bwkfanboy::Meta::USER_AGENT) {|f|
17
- if defined?(f.meta) && f.status[0] != '200' then
18
- Bwkfanboy::Utils.errx(1, "cannot fetch #{uri} : HTTP responce: #{f.status[0]}")
19
- end
20
- Bwkfanboy::Utils.veputs(1, "charset = #{f.content_type_parse[1][1]}\n") if defined?(f.meta)
21
- if block_given?
22
- yield f
23
- else
24
- return f.read
25
- end
26
- }
19
+ uri.each {|i| streams << open(i) }
27
20
  rescue
28
- # typically Errno::ENOENT
29
- Bwkfanboy::Utils.errx(1, "cannot fetch: #{$!}");
21
+ raise FetchException, "streams: #{$!}"
30
22
  end
31
-
32
- return ""
23
+
24
+ raise FetchException, 'streams: failed to open at least 1' if streams.size == 0
25
+ streams
33
26
  end
34
27
 
28
+ def closeStreams streams
29
+ streams.each {|i| i.close }
30
+ rescue
31
+ # do nothing
32
+ end
35
33
  end
36
34
  end
@@ -0,0 +1,78 @@
1
+ require 'rss/maker'
2
+ require 'msgpack'
3
+
4
+ module Bwkfanboy
5
+
6
+ # TODO: generate this class automatically via something like
7
+ # Utils.makeExceptClass 'GeneratorException', prefix: 'generator'
8
+ class GeneratorException < StandardError
9
+ def initialize msg
10
+ super msg
11
+ end
12
+
13
+ alias :orig_to_s :to_s
14
+ def to_s
15
+ "generator: #{orig_to_s}"
16
+ end
17
+ end
18
+
19
+ module Generator
20
+ extend self
21
+
22
+ def unpack stream
23
+ p = MessagePack::Unpacker.new stream
24
+ p.each {|i| return i } # p.first or p[0] Messagepack doesn't implement
25
+ return nil
26
+ rescue EOFError
27
+ return nil
28
+ end
29
+
30
+ # [data] a hash; see Plugin#pack for the exact format.
31
+ def atom data
32
+ raise GeneratorException, 'unpacked input is nil' unless data
33
+
34
+ feed = RSS::Maker.make("atom") { |maker|
35
+ maker.channel.id = data['channel']['id']
36
+ maker.channel.updated = data['channel']['updated']
37
+ maker.channel.author = data['channel']['author']
38
+ maker.channel.title = data['channel']['title']
39
+
40
+ maker.channel.links.new_link {|i|
41
+ i.href = data['channel']['link']
42
+ i.rel = 'alternate'
43
+ i.type = 'text/html' # eh
44
+ }
45
+
46
+ maker.items.do_sort = true
47
+
48
+ data['x_entries'].each { |i|
49
+ maker.items.new_item do |item|
50
+ item.links.new_link {|k|
51
+ k.href = i['link']
52
+ k.rel = 'alternate'
53
+ k.type = 'text/html' # only to make happy crappy pr2nntp gateway
54
+ }
55
+ item.title = i['title']
56
+ item.author = i['author']
57
+ item.updated = i['updated']
58
+ item.content.type = data['channel']['x_entries_content_type']
59
+
60
+ case item.content.type
61
+ when 'text'
62
+ item.content.content = i['content']
63
+ when 'html'
64
+ item.content.content = i['content']
65
+ else
66
+ item.content.xhtml = i['content']
67
+ end
68
+ end
69
+ }
70
+ }
71
+
72
+ feed
73
+ rescue
74
+ raise GeneratorException, $!.to_s
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,53 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+
4
+ require_relative 'cliconfig'
5
+
6
+ module Bwkfanboy
7
+ class Home
8
+ # Home root directory
9
+ ROOT = Pathname.new "#{Dir.home}/.#{Meta::NAME.downcase}"
10
+ PLUGINS = 'plugins'
11
+ LOGS = 'log'
12
+
13
+ SYSTEM_PLUGINS = CliUtils::DIR_LIB_SRC.parent.parent + 'plugins'
14
+
15
+ attr_reader :root, :logs
16
+ attr_accessor :conf
17
+
18
+ # Load config & create all required directories.
19
+ def initialize dir = nil # yield loader, o
20
+ @root = (dir && Pathname(dir)) || ROOT
21
+ @logs = @root + LOGS
22
+
23
+ CliConfig::DIR_CONFIG.unshift @root
24
+ @conf = CliConfig.new
25
+ @conf[:plugins_path] = [@root + PLUGINS, SYSTEM_PLUGINS]
26
+ @conf.load do |o|
27
+ yield self, o if block_given?
28
+ o.on('-I DIR', 'Include DIR in search for plugins.') {|i|
29
+ @conf[:plugins_path] << Pathname.new(i) if File.directory?(i)
30
+ }
31
+ o.on('--plugins-path', 'Print all searchable directories.') {
32
+ print_plugins_path
33
+ exit EX_OK
34
+ }
35
+ end
36
+
37
+ print_env
38
+ FileUtils.mkdir_p [@conf[:plugins_path], @logs]
39
+ end
40
+
41
+ def print_plugins_path
42
+ @conf[:plugins_path].each {|i| puts i }
43
+ end
44
+
45
+ def print_env
46
+ if @conf[:verbose] >= 2
47
+ puts "Libs dir: #{CliUtils::DIR_LIB_SRC}"
48
+ pp @conf
49
+ end
50
+ end
51
+
52
+ end
53
+ end