bcat 0.2.0 → 0.3.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.
@@ -0,0 +1,5 @@
1
+ Report bcat bugs and patches to the issue tracker:
2
+
3
+ <http://github.com/rtomayko/bcat/issues>
4
+
5
+ Unified diff patches and links to forks are appreciated.
data/COPYING CHANGED
@@ -0,0 +1,19 @@
1
+ bcat
2
+ Copyright (c) 2010 Ryan Tomayko <http://tomayko.com/about>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to
6
+ deal in the Software without restriction, including without limitation the
7
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8
+ sell copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,5 @@
1
+ bcat is usually installed with rubygems:
2
+
3
+ $ gem install bcat
4
+
5
+ bcat depends on the rack package.
data/README CHANGED
@@ -1,7 +1,17 @@
1
- bcat reads from standard input and displays output in a web browser:
1
+ bcat
2
+ http://github.com/rtomayko/bcat
3
+ git clone git://github.com/rtomayko/bcat.git
4
+ gem install bcat
5
+
6
+ bcat is a pipe to browser utility. It reads from standard input and displays
7
+ what it reads in a web browser:
2
8
 
3
9
  $ echo "hi mom" |bcat
4
- $ echo "hi mom" |bcat -t 'Page Title'
10
+ $ echo "hi mom" |bcat -t 'Important Message'
11
+
12
+ bcat assumes its input is plain text, but you can also pipe in HTML:
13
+
14
+ $ echo "<h1>hi mom</h1>" |bcat -h
5
15
  $ echo "*hi mom*" |markdown |bcat -h
6
16
 
7
17
  Browser output is displayed progressively as it's read from standard input,
@@ -11,3 +21,19 @@ generate output over longer periods of time:
11
21
  $ make all |bcat
12
22
  $ rake test |bcat
13
23
  $ tail -f /var/log/syslog |bcat
24
+ $ (while printf .; do sleep 1; done) |bcat
25
+
26
+ See the bcat(1) manual for detailed command usage.
27
+
28
+ bcat is known to work under MacOS X, Linux, and FreeBSD (other UNIX-like
29
+ environments with freedesktop.org integration should work fine too). Progressive
30
+ output has been tested under Safari, Firefox, Chrome, and GNOME Epiphany.
31
+
32
+ See the INSTALLING, COPYING, and CONTRIBUTING files for information on those
33
+ things.
34
+
35
+ bcat was inspired by the HTML output capabilities included in TextMate
36
+ and a desire to have those capabilities from the shell and within editors
37
+ like Vim.
38
+ <http://manual.macromates.com/en/commands#html_output>
39
+ <http://blog.macromates.com/2005/html-output-for-commands/>
data/Rakefile CHANGED
@@ -1,4 +1,26 @@
1
1
  require 'date'
2
+ task :default => :test
3
+
4
+ ROOTDIR = File.expand_path('..', __FILE__).sub(/#{Dir.pwd}(?=\/)/, '.')
5
+ LIBDIR = "#{ROOTDIR}/lib"
6
+ BINDIR = "#{ROOTDIR}/bin"
7
+
8
+ task :environment do
9
+ $:.unshift ROOTDIR if !$:.include?(ROOTDIR)
10
+ $:.unshift LIBDIR if !$:.include?(LIBDIR)
11
+ ENV['RUBYLIB'] = $LOAD_PATH.join(':')
12
+ ENV['PATH'] = "#{BINDIR}:#{ENV['PATH']}"
13
+ end
14
+
15
+ desc 'Run tests'
16
+ task :test => :environment do
17
+ $LOAD_PATH.unshift "#{ROOTDIR}/test"
18
+ Dir['test/test_*.rb'].each { |f| require(f) }
19
+ end
20
+
21
+ def source_version
22
+ @source_version ||= `ruby -Ilib -rbcat -e 'puts Bcat::VERSION'`.chomp
23
+ end
2
24
 
3
25
  require 'rubygems'
4
26
  $spec = eval(File.read('bcat.gemspec'))
@@ -15,10 +37,6 @@ task :man do
15
37
  sh "ronn -w -r5 man/*.ronn"
16
38
  end
17
39
 
18
- def source_version
19
- @source_version ||= `ruby -Ilib -rbcat -e 'puts Bcat::VERSION'`.chomp
20
- end
21
-
22
40
  file 'bcat.gemspec' => FileList['{lib,test,bin}/**','Rakefile'] do |f|
23
41
  # read spec file and split out manifest section
24
42
  spec = File.read(f.name)
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'bcat'
3
- s.version = '0.2.0'
4
- s.date = '2010-06-20'
3
+ s.version = '0.3.0'
4
+ s.date = '2010-06-21'
5
5
 
6
6
  s.summary = "browser cat"
7
7
  s.description =
@@ -13,6 +13,9 @@ Gem::Specification.new do |s|
13
13
 
14
14
  # = MANIFEST =
15
15
  s.files = %w[
16
+ CONTRIBUTING
17
+ COPYING
18
+ INSTALLING
16
19
  README
17
20
  Rakefile
18
21
  bcat.gemspec
@@ -21,10 +24,12 @@ Gem::Specification.new do |s|
21
24
  bin/btee
22
25
  lib/bcat.rb
23
26
  lib/bcat/browser.rb
27
+ lib/bcat/html.rb
24
28
  lib/bcat/kidgloves.rb
25
29
  lib/bcat/reader.rb
26
30
  man/bcat.1.ronn
27
31
  man/btee.1.ronn
32
+ test/test_bcat_head_parser.rb
28
33
  ]
29
34
  # = MANIFEST =
30
35
 
data/bin/bcat CHANGED
@@ -1,32 +1,41 @@
1
1
  #!/usr/bin/env ruby
2
- #/ Usage: bcat [-h] [-t <title>] [<file>]...
2
+ #/ Usage: bcat [-ht] [-m <module>] [-T <title>] [<file>]...
3
3
  #/ btee <options> [<file>]...
4
- #/ Read standard input, or one or more <file>s, and write to browser. When
5
- #/ invoked as btee, also write input to standard output.
4
+ #/ Pipe to browser utility. Read standard input, possibly one or more <file>s,
5
+ #/ and write concatenated / formatted output to browser. When invoked as btee,
6
+ #/ also write all input back to standard output.
6
7
  #/
7
- #/ Options
8
- #/ -h, --html input is html encoded
9
- #/ -t, --title=<text> use <text> as the page title
10
- #/ -d, --debug enable verbose debug logging on stderr
8
+ #/ Input format (auto detected by default):
9
+ #/ -h, --html input is already HTML encoded, perhaps a whole document
10
+ #/ -t, --text input is unencoded text
11
+ #/
12
+ #/ Tweak output with these options:
13
+ #/ -T, --title=<text> use <text> as the page title
14
+ #/ -m, --module=<m1>[,<m2>]...
15
+ #/ inject formatting module(s)
16
+ #/
17
+ #/ -d, --debug enable verbose debug logging on stderr
11
18
  require 'optparse'
12
19
 
13
20
  options = {
14
- :html => false,
15
- :title => Dir.pwd,
16
- :Host => '127.0.0.1',
17
- :Port => 8091,
18
- :debug => false
21
+ :format => nil,
22
+ :title => Dir.pwd,
23
+ :Host => '127.0.0.1',
24
+ :Port => 8091,
25
+ :debug => false,
26
+ :tee => !!($0 =~ /tee$/)
19
27
  }
20
28
 
21
29
  (class <<self;self;end).send(:define_method, :notice) { |message|
22
30
  warn "#{File.basename($0)}: #{message}" if options[:debug] }
23
31
 
24
32
  ARGV.options do |argv|
25
- argv.on('-h', '--html') { options[:html] = true }
33
+ argv.on('-h', '--html') { options[:format] = 'html' }
34
+ argv.on('-t', '--text') { options[:format] = 'text' }
26
35
  argv.on('-a', '--app=v') { |app| ENV['BCAT_APPLICATION'] = app }
27
- argv.on('-t', '--title=v') { |text| options[:title] = text }
36
+ argv.on('-T', '--title=v') { |text| options[:title] = text }
28
37
  argv.on('-d', '--debug') { options[:debug] = true }
29
- argv.on_tail('--help') { exec "grep ^#/ <#{__FILE__} | cut -c4-" }
38
+ argv.on_tail('--help') { exec "grep ^#/ <#{__FILE__} | cut -c4-" }
30
39
  argv.parse!
31
40
  end
32
41
  ARGV.push '-' if ARGV.empty?
@@ -38,17 +47,10 @@ include Bcat::Browser
38
47
  notice "env BCAT_APPLICATION=#{ENV['BCAT_APPLICATION'].inspect}"
39
48
  notice "env BCAT_COMMAND=#{browser_command.inspect}"
40
49
 
41
- reader =
42
- if File.basename($0) =~ /tee$/
43
- Bcat::TeeReader.new(ARGV, $stdout)
44
- else
45
- Bcat::Reader.new(ARGV)
46
- end
47
-
48
50
  notice "starting server"
49
51
  pid = nil
50
52
  begin
51
- bcat = Bcat.new(reader, options)
53
+ bcat = Bcat.new(ARGV, options)
52
54
  bcat.serve! do |sock|
53
55
  url = "http://#{bcat[:Host]}:#{bcat[:Port]}/#{File.basename(Dir.pwd)}"
54
56
  pid = browser(url)
@@ -59,5 +61,5 @@ end
59
61
 
60
62
  Process.wait(pid) if pid
61
63
  status = $?
62
- notice "open exited with #{status}"
64
+ notice "browser exited with #{status}"
63
65
  exit status
@@ -1,49 +1,84 @@
1
1
  require 'rack'
2
2
  require 'bcat/reader'
3
+ require 'bcat/html'
3
4
  require 'bcat/kidgloves'
4
5
  require 'bcat/browser'
5
6
 
6
7
  class Bcat
7
- VERSION = '0.2.0'
8
+ VERSION = '0.3.0'
8
9
  include Rack::Utils
9
10
 
10
- def initialize(reader, config={})
11
- @reader = reader
11
+ attr_reader :format
12
+
13
+ def initialize(files=[], config={})
12
14
  @config = {:Host => '127.0.0.1', :Port => 8091}.merge(config)
15
+ @reader = Bcat::Reader.new(files)
16
+ @format = @config[:format] || @reader.sniff
17
+
18
+ @filter = @reader
19
+ @filter = TeeFilter.new(@filter) if @config[:tee]
20
+ @filter = TextFilter.new(@filter) if @format == 'text'
13
21
  end
14
22
 
15
23
  def [](key)
16
24
  @config[key]
17
25
  end
18
26
 
27
+ def to_app
28
+ app = self
29
+ Rack::Builder.new do
30
+ use Rack::Chunked
31
+ run app
32
+ end
33
+ end
34
+
35
+ def serve!(&bk)
36
+ Rack::Handler::KidGloves.run to_app, @config, &bk
37
+ end
38
+
19
39
  def call(env)
20
40
  notice "#{env['REQUEST_METHOD']} #{env['PATH_INFO'].inspect}"
21
41
  [200, {"Content-Type" => "text/html;charset=utf-8"}, self]
22
42
  end
23
43
 
24
44
  def each
25
- yield "\n" * 1000
26
- yield "<!DOCTYPE html>\n"
27
- yield head
28
- yield "<pre>" if !self[:html]
29
-
30
- @reader.each do |buf|
31
- if !self[:html]
32
- buf = escape_html(buf)
33
- buf.gsub!(/\n/, "<br>")
34
- end
35
- buf = escape_js(buf)
36
- yield "<script>document.write('#{buf}');</script>"
45
+ head_parser = Bcat::HeadParser.new
46
+
47
+ @filter.each do |buf|
48
+ if head_parser.nil?
49
+ yield buf
50
+ elsif head_parser.feed(buf)
51
+ yield content_for_head(inject=head_parser.head)
52
+ yield head_parser.body
53
+ head_parser = nil
54
+ end
55
+ end
56
+
57
+ if head_parser
58
+ yield content_for_head(inject=head_parser.head) +
59
+ head_parser.body
37
60
  end
38
61
 
39
- yield "</pre>" if !self[:html]
40
62
  yield foot
63
+ rescue Errno::EINVAL
64
+ # socket was closed
65
+ notice "browser client went away"
66
+ rescue => boom
67
+ notice "boom: #{boom.class}: #{boom.to_s}"
68
+ raise
41
69
  end
42
70
 
43
- def head
44
- ["<html>",
45
- "<head><title>#{self[:title] || 'bcat'}</title></head>",
46
- "<body>"].join
71
+ def content_for_head(inject='')
72
+ [
73
+ "\n" * 1000,
74
+ "<!DOCTYPE html>",
75
+ "<html>",
76
+ "<head>",
77
+ "<!-- bcat was here -->",
78
+ "<title>#{self[:title] || 'bcat'}</title>",
79
+ inject.to_s,
80
+ "</head>"
81
+ ].join("\n")
47
82
  end
48
83
 
49
84
  def foot
@@ -61,18 +96,6 @@ class Bcat
61
96
  raise Interrupt
62
97
  end
63
98
 
64
- def to_app
65
- app = self
66
- Rack::Builder.new do
67
- use Rack::Chunked
68
- run app
69
- end
70
- end
71
-
72
- def serve!(&bk)
73
- Rack::Handler::KidGloves.run to_app, @config, &bk
74
- end
75
-
76
99
  def notice(message)
77
100
  return if !@config[:debug]
78
101
  warn "#{File.basename($0)}: #{message}"
@@ -0,0 +1,106 @@
1
+ class Bcat
2
+
3
+ # Parses HTML until the first displayable body character and provides methods
4
+ # for accessing head and body contents.
5
+ class HeadParser
6
+ attr_accessor :buf
7
+
8
+ def initialize
9
+ @buf = ''
10
+ @head = []
11
+ @body = nil
12
+ @html = nil
13
+ end
14
+
15
+ # Called to parse new data as it arrives.
16
+ def feed(data)
17
+ if complete?
18
+ @body << data
19
+ else
20
+ @buf << data
21
+ parse(@buf)
22
+ end
23
+ complete?
24
+ end
25
+
26
+ # Truthy once the first displayed character of the body has arrived.
27
+ def complete?
28
+ !@body.nil?
29
+ end
30
+
31
+ # Determine if the input is HTML. This nil before the first non-whitespace
32
+ # character is received, true if the first non-whitespace character is a
33
+ # '<', and false if the first non-whitespace character is something other
34
+ # than '<'.
35
+ def html?
36
+ @html
37
+ end
38
+
39
+ # The head contents without any DOCTYPE, <html>, or <head> tags. This should
40
+ # consist of only <style>, <script>, <link>, <meta>, and <title> tags.
41
+ def head
42
+ @head.join.gsub(/<\/?(?:html|head|!DOCTYPE).*?>/mi, '')
43
+ end
44
+
45
+ # The current body contents. The <body> tag is guaranteed to be present. If
46
+ # a <body> was included in the input, it's preserved with original
47
+ # attributes; otherwise, a <body> tag is inserted. The inject argument can
48
+ # be used to insert a string as the immediate descendant of the <body> tag.
49
+ def body(inject=nil)
50
+ if @body =~ /\A\s*(<body.*?>)(.*)/mi
51
+ [$1, inject, $2].compact.join("\n")
52
+ else
53
+ ["<body>", inject, @body].compact.join("\n")
54
+ end
55
+ end
56
+
57
+ HEAD_TOKS = [
58
+ /\A(<!DOCTYPE.*?>)/m,
59
+ /\A(<title.*?>.*?<\/title>)/mi,
60
+ /\A(<script.*?>.*?<\/script>)/mi,
61
+ /\A(<style.*?>.*?<\/style>)/mi,
62
+ /\A(<(?:html|head|meta|link).*?>)/mi,
63
+ /\A(<\/(?:html|head|meta|link|script|style|title)>)/mi,
64
+ /\A(<!--(.*?)-->)/m
65
+ ]
66
+
67
+ BODY_TOKS = [
68
+ /\A[^<]/,
69
+ /\A<(?!html|head|meta|link|script|style|title).*?>/
70
+ ]
71
+
72
+ # Parses buf into head and body parts. Basic approach is to eat anything
73
+ # possibly body related until we hit text or a body element.
74
+ def parse(buf=@buf)
75
+ if @html.nil?
76
+ if buf =~ /\A\s*[<]/m
77
+ @html = true
78
+ elsif buf =~ /\A\s*[^<]/m
79
+ @html = false
80
+ end
81
+ end
82
+
83
+ while !buf.empty?
84
+ buf.sub!(/\A(\s+)/m) { @head << $1 ; '' }
85
+ matched =
86
+ HEAD_TOKS.any? do |tok|
87
+ buf.sub!(tok) do
88
+ @head << $1
89
+ ''
90
+ end
91
+ end
92
+ break unless matched
93
+ end
94
+
95
+
96
+ if buf.empty?
97
+ buf
98
+ elsif BODY_TOKS.any? { |tok| buf =~ tok }
99
+ @body = buf
100
+ nil
101
+ else
102
+ buf
103
+ end
104
+ end
105
+ end
106
+ end
@@ -1,3 +1,5 @@
1
+ require 'rack/utils'
2
+
1
3
  class Bcat
2
4
  # ARGF style multi-file streaming interface. Input is read with IO#readpartial
3
5
  # to avoid buffering.
@@ -15,36 +17,73 @@ class Bcat
15
17
  File.open(f, 'rb')
16
18
  end
17
19
  end
20
+ @buf = []
18
21
  end
19
22
 
20
23
  def each
21
- fds.each do |fd|
24
+ yield @buf.shift while @buf.any?
25
+ while fd = fds.first
22
26
  fd.sync = true
23
27
  begin
24
28
  while buf = fd.readpartial(4096)
25
29
  yield buf
26
30
  end
27
31
  rescue EOFError
28
- ensure
29
32
  fd.close
30
33
  end
34
+ fds.shift
31
35
  end
32
36
  end
37
+
38
+ def sniff
39
+ @format ||=
40
+ catch :detect do
41
+ each do |chunk|
42
+ @buf << chunk
43
+ case chunk
44
+ when /\A\s*</m
45
+ throw :detect, 'html'
46
+ when /\A\s*[^<]/m
47
+ throw :detect, 'text'
48
+ end
49
+ end
50
+ throw :detect, 'text'
51
+ end
52
+ end
33
53
  end
34
54
 
35
55
  # Like Reader but writes all input to an output IO object in addition to
36
56
  # yielding to the block.
37
- class TeeReader < Reader
38
- def initialize(files=[], out=$stdout)
57
+ class TeeFilter
58
+ def initialize(source, out=$stdout)
59
+ @source = source
39
60
  @out = out
40
- super(files)
41
61
  end
42
62
 
43
63
  def each
44
- super() do |chunk|
64
+ @source.each do |chunk|
65
+ yield chunk
45
66
  @out.write chunk
67
+ end
68
+ end
69
+ end
70
+
71
+ class TextFilter
72
+ include Rack::Utils
73
+
74
+ def initialize(source, force=false)
75
+ @source = source
76
+ @force = force
77
+ end
78
+
79
+ def each
80
+ yield "<pre>"
81
+ @source.each do |chunk|
82
+ chunk = escape_html(chunk)
83
+ chunk = "<span>#{chunk}</span>" if !chunk.gsub!(/\n/, "<br>")
46
84
  yield chunk
47
85
  end
86
+ yield "</pre>"
48
87
  end
49
88
  end
50
89
  end
@@ -17,16 +17,26 @@ addition to being piped into the browser.
17
17
 
18
18
  ## OPTIONS
19
19
 
20
- * `-t`, `--title`=<text>:
21
- Use <text> as the page `<title>`. By default, the path to the current working
22
- directory is used as the title.
20
+ By default, `bcat` attempts to detect whether input is HTML or plain text using
21
+ a simple heuristic, but you can force input to be treated as one or the other
22
+ with these options:
23
+
24
+ * `-t`, `--text`:
25
+ The input is non-HTML encoded text. All bare `<` and `&` characters are
26
+ entity encoded, end-of-line characters are converted to `<br>`, and the
27
+ entire output is wrapped in a `<pre>`.
23
28
 
24
29
  * `-h`, `--html`:
25
- Do not HTML encode input. By default, `bcat` assumes input is plain text
26
- and entity encodes any `<` or `&` characters, converts raw end-of-line
27
- characters (`\n`) to `<br>` tags, and wraps output in a `<pre>` block. The
28
- `--html` option disables all of these conversions, causing the input text
29
- to be written directly as HTML.
30
+ The input is already HTML encoded. Under this mode, bcat passes input
31
+ through to the browser mostly unmodified. The input may be a full HTML
32
+ document, or it may be an HTML fragment. `bcat` outputs `<html>`, `<head>`,
33
+ and `<body>` elements even if they are not included in the input.
34
+
35
+ Customization
36
+
37
+ * `-T`, `--title`=<text>:
38
+ Use <text> as the page `<title>`. By default, the path to the current working
39
+ directory is used as the title.
30
40
 
31
41
  * `-a`, `--app`=`Safari`|`Google Chrome`|`Firefox`:
32
42
  MacOS only. The name of the browser application. This can also be set using
@@ -17,16 +17,26 @@ addition to being piped into the browser.
17
17
 
18
18
  ## OPTIONS
19
19
 
20
- * `-t`, `--title`=<text>:
21
- Use <text> as the page `<title>`. By default, the path to the current working
22
- directory is used as the title.
20
+ By default, `bcat` attempts to detect whether input is HTML or plain text using
21
+ a simple heuristic, but you can force input to be treated as one or the other
22
+ with these options:
23
+
24
+ * `-t`, `--text`:
25
+ The input is non-HTML encoded text. All bare `<` and `&` characters are
26
+ entity encoded, end-of-line characters are converted to `<br>`, and the
27
+ entire output is wrapped in a `<pre>`.
23
28
 
24
29
  * `-h`, `--html`:
25
- Do not HTML encode input. By default, `bcat` assumes input is plain text
26
- and entity encodes any `<` or `&` characters, converts raw end-of-line
27
- characters (`\n`) to `<br>` tags, and wraps output in a `<pre>` block. The
28
- `--html` option disables all of these conversions, causing the input text
29
- to be written directly as HTML.
30
+ The input is already HTML encoded. Under this mode, bcat passes input
31
+ through to the browser mostly unmodified. The input may be a full HTML
32
+ document, or it may be an HTML fragment. `bcat` outputs `<html>`, `<head>`,
33
+ and `<body>` elements even if they are not included in the input.
34
+
35
+ Customization
36
+
37
+ * `-T`, `--title`=<text>:
38
+ Use <text> as the page `<title>`. By default, the path to the current working
39
+ directory is used as the title.
30
40
 
31
41
  * `-a`, `--app`=`Safari`|`Google Chrome`|`Firefox`:
32
42
  MacOS only. The name of the browser application. This can also be set using
@@ -0,0 +1,56 @@
1
+ require 'contest'
2
+ require 'bcat/html'
3
+
4
+ class HeadParserTest < Test::Unit::TestCase
5
+
6
+ setup { @parser = Bcat::HeadParser.new }
7
+
8
+ test 'starts in an unknown state' do
9
+ assert @parser.html?.nil?
10
+ assert @parser.buf.empty?
11
+ end
12
+
13
+ test 'detects non-HTML input' do
14
+ @parser.feed("HOWDY <h1>")
15
+ assert_equal false, @parser.html?
16
+ assert_equal '', @parser.head
17
+ end
18
+
19
+ test 'separates head elements from body' do
20
+ @parser.feed("<style>h1{ font-size:500% }</style>")
21
+ @parser.feed("<h1>HOLLA</h1>")
22
+ assert_equal "<style>h1{ font-size:500% }</style>", @parser.head.strip
23
+ assert_equal "<body>\n<h1>HOLLA</h1>", @parser.body
24
+ end
25
+
26
+ test 'handles multiple head elements' do
27
+ stuff = [
28
+ "<style>h1{ font-size:500% }</style>",
29
+ "<link rel=alternate>",
30
+ "<script type='text/javascript'>{};</script>"
31
+ ]
32
+ stuff.each { |html| @parser.feed(html) }
33
+ @parser.feed("\n \n\n\n<h1>HOLLA</h1>")
34
+
35
+ assert_equal stuff.join, @parser.head.strip
36
+ end
37
+
38
+ test 'handles full documents' do
39
+ @parser.feed("<!DOCTYPE html>\n")
40
+ @parser.feed("<html><head><title>YO</title></head>")
41
+ @parser.feed("<body id=oyy><h1>OY</h1></body></html>")
42
+ assert_equal "<title>YO</title>", @parser.head.strip
43
+ assert_equal "<body id=oyy>\n<h1>OY</h1></body></html>", @parser.body
44
+ end
45
+
46
+ test 'knows when the head is fully parsed' do
47
+ @parser.feed("<!DOCTYPE html>\n")
48
+ assert !@parser.complete?
49
+
50
+ @parser.feed("<html><head><title>YO</title></head>")
51
+ assert !@parser.complete?
52
+
53
+ @parser.feed("<body id=oyy><h1>OY</h1></body></html>")
54
+ assert @parser.complete?
55
+ end
56
+ end
metadata CHANGED
@@ -1,13 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bcat
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 2
9
- - 0
10
- version: 0.2.0
4
+ version: 0.3.0
11
5
  platform: ruby
12
6
  authors:
13
7
  - Ryan Tomayko
@@ -15,23 +9,19 @@ autorequire:
15
9
  bindir: bin
16
10
  cert_chain: []
17
11
 
18
- date: 2010-06-20 00:00:00 -07:00
12
+ date: 2010-06-21 00:00:00 -07:00
19
13
  default_executable: bcat
20
14
  dependencies:
21
15
  - !ruby/object:Gem::Dependency
22
16
  name: rack
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
25
- none: false
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
26
20
  requirements:
27
21
  - - ">="
28
22
  - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
23
  version: "0"
33
- type: :runtime
34
- version_requirements: *id001
24
+ version:
35
25
  description: Concatenate input from standard input, or one or more files, and write progressive output to a browser.
36
26
  email: rtomayko@gmail.com
37
27
  executables:
@@ -41,6 +31,9 @@ extensions: []
41
31
  extra_rdoc_files:
42
32
  - COPYING
43
33
  files:
34
+ - CONTRIBUTING
35
+ - COPYING
36
+ - INSTALLING
44
37
  - README
45
38
  - Rakefile
46
39
  - bcat.gemspec
@@ -49,11 +42,12 @@ files:
49
42
  - bin/btee
50
43
  - lib/bcat.rb
51
44
  - lib/bcat/browser.rb
45
+ - lib/bcat/html.rb
52
46
  - lib/bcat/kidgloves.rb
53
47
  - lib/bcat/reader.rb
54
48
  - man/bcat.1.ronn
55
49
  - man/btee.1.ronn
56
- - COPYING
50
+ - test/test_bcat_head_parser.rb
57
51
  has_rdoc: true
58
52
  homepage: http://github.com/rtomayko/bcat/
59
53
  licenses: []
@@ -65,27 +59,21 @@ rdoc_options:
65
59
  require_paths:
66
60
  - lib
67
61
  required_ruby_version: !ruby/object:Gem::Requirement
68
- none: false
69
62
  requirements:
70
63
  - - ">="
71
64
  - !ruby/object:Gem::Version
72
- hash: 3
73
- segments:
74
- - 0
75
65
  version: "0"
66
+ version:
76
67
  required_rubygems_version: !ruby/object:Gem::Requirement
77
- none: false
78
68
  requirements:
79
69
  - - ">="
80
70
  - !ruby/object:Gem::Version
81
- hash: 3
82
- segments:
83
- - 0
84
71
  version: "0"
72
+ version:
85
73
  requirements: []
86
74
 
87
75
  rubyforge_project:
88
- rubygems_version: 1.3.7
76
+ rubygems_version: 1.3.5
89
77
  signing_key:
90
78
  specification_version: 3
91
79
  summary: browser cat