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,25 @@
1
+ @uri << 'http://www.dailyprincetonian.com/advanced_search/?author=Brian+Kernighan'
2
+ @copyright = "See bwkfanboy's LICENSE file"
3
+ @title = "Brian Kernighan's articles from Daily Princetonian"
4
+ @content_type = 'html'
5
+ @version = 2
6
+
7
+ # [streams] an array of IO streamss
8
+ def parse streams
9
+ streams.each do |io|
10
+ baseurl = "http://www.dailyprincetonian.com"
11
+
12
+ doc = Nokogiri::HTML io, nil, enc
13
+ doc.xpath("//div[@class='article_item']").each do |idx|
14
+ t = idx.xpath("h2/a").children.text
15
+ link = idx.xpath("h2/a")[0].attributes['href'].value
16
+ l = baseurl + link + "print"
17
+ u = BH.date idx.xpath("h2").children[1].text
18
+ a = idx.xpath("div/span/a[1]").children.text
19
+ c = idx.xpath("div[@class='summary']").text
20
+
21
+ self << { 'title' => t, 'link' => l, 'updated' => u,
22
+ 'author' => a, 'content' => c }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ @uri << 'http://www.econlib.org/cgi-bin/searcharticles.pl?sortby=DD&query=ha*'
2
+ @version = 1
3
+ @copyright = "See bwkfanboy's LICENSE file"
4
+ @title = "Latest articles from econlib.org"
5
+ @content_type = 'html'
6
+
7
+ def parse streams
8
+ baseurl = 'http://www.econlib.org'
9
+
10
+ doc = Nokogiri::HTML streams.first, nil, @enc
11
+ doc.xpath("//*[@id='divResults']//tr").each {|idx|
12
+ t = idx.xpath("td[3]//a").text
13
+ next if t == ""
14
+ l = baseurl + idx.xpath("td[3]//a")[0].attributes['href'].value
15
+ u = BH.date idx.xpath("td[4]").children.text
16
+ a = idx.xpath("td[3]/div").children[2].text
17
+ c = idx.xpath("td[4]").children[2].text
18
+
19
+ self << { 'title' => t, 'link' => l, 'updated' => u,
20
+ 'author' => a, 'content' => c }
21
+ }
22
+ end
@@ -0,0 +1,73 @@
1
+ require 'digest/md5'
2
+
3
+ @uri << '/usr/ports/UPDATING'
4
+ @enc = 'ASCII'
5
+ @version = 3
6
+ @copyright = "See bwkfanboy's LICENSE file"
7
+ @title = "News from FreeBSD ports"
8
+ @content_type = 'text'
9
+
10
+ def my_add ready, t, l, u, a, c
11
+ return true if ! ready
12
+ return false if full?
13
+
14
+ self << { 'title' => t, 'link' => l, 'updated' => u,
15
+ 'author' => a, 'content' => c.rstrip } if ready
16
+ true
17
+ end
18
+
19
+ def my_clean t
20
+ t = t[2..-1] if t[0] != "\t"
21
+ return '' if t == nil
22
+ t
23
+ end
24
+
25
+ def parse streams
26
+ re_u = /^(\d{8}):$/
27
+ re_t1 = /^ {2}AFFECTS:\s+(.+)$/
28
+ re_t2 = /^\s+(.+)$/
29
+ re_a = /^ {2}AUTHORS?:\s+(.+)$/
30
+
31
+ ready = false
32
+ mode = nil
33
+ t = l = u = a = c = nil
34
+ while line = streams.first.gets
35
+ line.rstrip!
36
+
37
+ if line =~ re_u then
38
+ # add a new entry
39
+ break if ! my_add(ready, t, l, u, a, c)
40
+ ready = true
41
+ u = BH.date($1)
42
+ l = $1 # partial, see below
43
+ t = a = c = nil
44
+ next
45
+ end
46
+
47
+ if ready then
48
+ if line =~ re_t1 then
49
+ mode = 'title'
50
+ t = $1
51
+ c = my_clean($&) + "\n"
52
+ # link should be unique
53
+ l = "file://#{@uri.first}\##{l}-#{Digest::MD5.hexdigest($1)}"
54
+ elsif line =~ re_a
55
+ mode = 'author'
56
+ a = $1
57
+ c += my_clean($&) + "\n"
58
+ elsif line =~ re_t2 && mode == 'title'
59
+ t += ' ' + $1
60
+ c += my_clean($&) + "\n"
61
+ else
62
+ # content
63
+ c += my_clean(line) + "\n"
64
+ mode = nil
65
+ end
66
+ end
67
+
68
+ # skipping the preamble
69
+ end
70
+
71
+ # add last entry
72
+ my_add(ready, t, l, u, a, c)
73
+ end
@@ -0,0 +1,29 @@
1
+ @opt.each {|i| @uri << 'http://www.inc.com/author/' + i }
2
+ @version = 1
3
+ @copyright = 'See bwkfanboy\'s LICENSE file'
4
+ @title = "Articles (per-user) from inc.com"
5
+ @content_type = 'html'
6
+
7
+ def parse streams
8
+ streams.each_with_index do |io, index|
9
+ profile = @opt[index]
10
+
11
+ doc = Nokogiri::HTML(io, nil, @enc)
12
+ doc.xpath("//div[@id='articleriver']/div/div").each do |idx|
13
+ t = idx.xpath("h3").text
14
+ l = idx.xpath("h3/a")[0].attributes['href'].value
15
+
16
+ next if (u = idx.xpath("div[@class='byline']/span")).size == 0
17
+ u = BH.date u.text
18
+
19
+ a = idx.xpath("div[@class='byline']/a").text
20
+
21
+ c = idx.xpath("p[@class='summary']")
22
+ c.xpath("a").remove
23
+ c = c.inner_html encoding: @enc
24
+
25
+ self << { 'title' => t, 'link' => l, 'updated' => u,
26
+ 'author' => a, 'content' => c }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ @opt.each { @uri << "#{@syslib}/../../test/example/02/plugins/inc.html" }
2
+ @version = 0
3
+ @copyright = 'takoe'
4
+ @title = "Test plugin that requires additional options"
5
+ @content_type = 'html'
6
+
7
+ def parse streams
8
+ streams.each_with_index do |io, index|
9
+ profile = @opt[index]
10
+
11
+ doc = Nokogiri::HTML(io, nil, @enc)
12
+ doc.xpath("//div[@id='articleriver']/div/div").each do |idx|
13
+ t = idx.xpath("h3").text
14
+ l = idx.xpath("h3/a")[0].attributes['href'].value
15
+
16
+ next if (u = idx.xpath("div[@class='byline']/span")).size == 0
17
+ u = BH.date u.text
18
+
19
+ a = idx.xpath("div[@class='byline']/a").text
20
+
21
+ c = idx.xpath("p[@class='summary']")
22
+ c.xpath("a").remove
23
+ c = c.inner_html encoding: @enc
24
+
25
+ self << { 'title' => t, 'link' => l, 'updated' => u,
26
+ 'author' => a, 'content' => c }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1 @@
1
+ jquery-* binary
Binary file
@@ -0,0 +1,111 @@
1
+ /*
2
+ Display plugin info on click.
3
+ */
4
+
5
+ function List() {}
6
+
7
+ List.INFO = '#info'
8
+ List.FORM = 'form'
9
+ List.OPTS = 'form input[name="opts"]'
10
+
11
+ List.prototype.mybind = function() {
12
+ var o = this
13
+ $('li > span').click(function() {
14
+ o.getInfo(this)
15
+ })
16
+ $(List.FORM).submit(function() {
17
+ o.getInfo($('li > span[class="pluginSelected"]'))
18
+ return false;
19
+ })
20
+ $(List.INFO).ajaxStart(function() {
21
+ o.progressAnimation(true)
22
+ })
23
+ $(List.INFO).ajaxStop(function() {
24
+ $(List.OPTS).focus()
25
+ })
26
+
27
+ $(List.OPTS).focus()
28
+ }
29
+
30
+ // Select current plugin, sent GET request & fill List.INFO.
31
+ List.prototype.getInfo = function(plugin) {
32
+ if (!plugin || plugin.length == 0) return
33
+
34
+ this.selectCurrent(plugin)
35
+ var name = $(plugin).text()
36
+ var url = '/info' + this.atom(name)
37
+
38
+ o = this
39
+ r = $.getJSON(url, function(json) {
40
+ o.drawPluginInfo(name, json)
41
+ })
42
+ .error(function() {
43
+ $(List.INFO).text('Error: ' + r.responseText)
44
+ })
45
+
46
+ }
47
+
48
+ List.prototype.selectCurrent = function(e) {
49
+ $('li > span').each(function(idx) {
50
+ if ($(e).text() == $(this).text()) {
51
+ $(this).addClass('pluginSelected')
52
+ $(this).removeClass('pluginUnselected')
53
+ } else {
54
+ $(this).removeClass('pluginSelected')
55
+ $(this).addClass('pluginUnselected')
56
+ }
57
+ })
58
+ }
59
+
60
+ List.prototype.progressAnimation = function(enable) {
61
+ if (enable) {
62
+ t = '<img src="/loading.gif" alt="Loading..." />'
63
+ $(List.INFO).html(t)
64
+ } else {
65
+ $(List.INFO + ' img').remove()
66
+ }
67
+ }
68
+
69
+ List.prototype.drawPluginInfo = function(plugin, json) {
70
+ $(List.INFO).html('')
71
+
72
+ var opts = this.getOpts()
73
+ var atom = '/' + plugin + (opts ? '?o='+opts : '')
74
+
75
+ var t = '<table border="1" cellpadding="3">'
76
+ t += '<tr><td>Atom</td><td>' + '<a href="'+atom+'">RSS reader link</a>' + '</td></tr>'
77
+ t += '<tr><td>Title</td><td>' + json["title"] + '</td></tr>'
78
+ t += '<tr><td>Version</td><td>' + json["version"] + '</td></tr>'
79
+ t += '<tr><td>Copyright</td><td>' + json["copyright"] + '</td></tr>'
80
+
81
+ // list of URI's
82
+ t += '<tr><td>URI (' + json['uri'].length + ')</td><td><ul>'
83
+ for (i in json['uri']) {
84
+ t += '<li> <a href="' + json['uri'][i] + '">'+ json['uri'][i] + '</a></li>'
85
+ }
86
+ t += '</ul></td>'
87
+
88
+ t += '</table>'
89
+
90
+ $(List.INFO).html(t)
91
+ }
92
+
93
+ List.prototype.getOpts = function() {
94
+ var opts = $(List.OPTS).val()
95
+ if (!opts) return ''
96
+ return opts.replace(/\s+/g, ' ').trim()
97
+ }
98
+
99
+ // Return a proper URL to a atom feed of [plugin]
100
+ List.prototype.atom = function(plugin) {
101
+ var opts = this.getOpts()
102
+ return '/' + plugin + (opts ? '?o='+opts : '')
103
+ }
104
+
105
+
106
+ // main
107
+
108
+ $(function() {
109
+ var list = new List()
110
+ list.mybind()
111
+ })
Binary file
@@ -0,0 +1,54 @@
1
+ #header {
2
+ /* background: green;*/
3
+ }
4
+
5
+ #list {
6
+ float: left;
7
+ width: 42%;
8
+ padding: 0 0 0 1%;
9
+ /* background: #b0c4de;*/
10
+ }
11
+
12
+ #plugin {
13
+ float: right;
14
+ width: 56%;
15
+ padding: 0 0 1% 1%;
16
+ /* background: #ffe81a;*/
17
+ }
18
+
19
+ #footer {
20
+ clear: both;
21
+ /* background: orange;*/
22
+ }
23
+
24
+ .footer {
25
+ float: right;
26
+ /* color: gray;*/
27
+ }
28
+
29
+ hr {
30
+ height: 1px;
31
+ border-width: 0;
32
+ background-color: black;
33
+ }
34
+
35
+ span:hover {
36
+ background: black;
37
+ color: white;
38
+ cursor: pointer;
39
+ }
40
+
41
+ .pluginSelected {
42
+ background: white;
43
+ color: blue;
44
+ }
45
+
46
+ .pluginUnselected {
47
+ background: white;
48
+ color: black;
49
+ }
50
+
51
+ table ul {
52
+ padding: 0 0 0 1em;
53
+ margin: 0;
54
+ }
@@ -0,0 +1,20 @@
1
+ require 'digest/md5'
2
+ require 'erb'
3
+ require 'etc'
4
+ require 'fakefs/safe'
5
+ require 'fileutils'
6
+ require 'haml'
7
+ require 'json'
8
+ require 'logger'
9
+ require 'msgpack'
10
+ require 'nokogiri'
11
+ require 'open-uri'
12
+ require 'open4'
13
+ require 'optparse'
14
+ require 'pathname'
15
+ require 'pp'
16
+ require 'rss/maker'
17
+ require 'shellwords'
18
+ require 'sinatra/base'
19
+ require 'stringio'
20
+ require 'yaml'
@@ -0,0 +1 @@
1
+ *.html binary
@@ -0,0 +1 @@
1
+ 01
@@ -0,0 +1 @@
1
+ garbage
@@ -1,33 +1,36 @@
1
- require 'digest/md5'
2
- require 'fileutils'
3
- include FileUtils
1
+ # This is supposed to be your helper for all your test. Feel free to
2
+ # add staff here.
4
3
 
5
- require_relative '../lib/bwkfanboy/utils'
6
- include Bwkfanboy
4
+ require_relative 'helper_cliutils'
7
5
 
8
- # don't run tests automatically if they were invoked as 'gem check -t ...'
9
- if $0 =~ /gem/
10
- require 'minitest/unit'
11
- else
12
- require 'minitest/autorun'
13
- end
6
+ class MyTestRunner
7
+ class Unit < MiniTest::Unit
14
8
 
15
- # Return the right directory for (probably executable) _c_.
16
- def cmd(c)
17
- case File.basename(Dir.pwd)
18
- when Meta::NAME.downcase
19
- # test probably is executed from the Rakefile
20
- Dir.chdir('test')
21
- when 'test'
22
- # we are in the test directory, there is nothing special to do
23
- else
24
- # tests were invoked by 'gem check -t bwkfanboy'
25
- begin
26
- Dir.chdir(Utils.gem_dir_system + '/../../test')
27
- rescue
28
- raise "running tests from '#{Dir.pwd}' isn't supported: #{$!}"
9
+ def before_suites
10
+ # code to run before the first test
29
11
  end
30
- end
31
12
 
32
- '../bin/' + c
13
+ def after_suites
14
+ # code to run after the last test
15
+ end
16
+
17
+ def _run_suites(suites, type)
18
+ begin
19
+ before_suites
20
+ super(suites, type)
21
+ ensure
22
+ after_suites
23
+ end
24
+ end
25
+
26
+ def _run_suite(suite, type)
27
+ begin
28
+ suite.before_suite if suite.respond_to?(:before_suite)
29
+ super(suite, type)
30
+ ensure
31
+ suite.after_suite if suite.respond_to?(:after_suite)
32
+ end
33
+ end
34
+
35
+ end
33
36
  end