bwkfanboy 1.4.1 → 2.0.0

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