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,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