chat_stew 0.0.1

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,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in chat_stew.gemspec
4
+ gemspec
@@ -0,0 +1,47 @@
1
+ # Chat Stew
2
+ A pluggable parsing library for chat logs. It handles Adium out of the box.
3
+
4
+ ## Usage
5
+
6
+ require 'chat_stew'
7
+ # my_file can be anything that responds to #read. If it's a String, it's assumed
8
+ # that it's a filename.
9
+ chat = ChatStew.parse(my_file)
10
+ chat.each do |message|
11
+ message.sender
12
+ message.receiver
13
+ message.message
14
+ message.time # a DateTime
15
+ end
16
+
17
+ ## Writing your own parser
18
+ A parser is anything that conforms to this specification:
19
+
20
+ * responds to #can_parse?(file) and returns truish or falsish
21
+ * responds to #parse(file) and returns an Enumerable containing
22
+ ChatStew::AdiumMessage instances
23
+
24
+ If two parsers are registered that can both parse a given file, the
25
+ most-recently-registered parser will win. I.E. if you register FooParser and
26
+ then register BarParser, and they can both parse a file called "foo.bar", then
27
+ BarParser will be used to parse it since it was registered after FooParser.
28
+
29
+ class FooParser
30
+ def can_parse?(file)
31
+ file.path =~ /foo$/
32
+ end
33
+
34
+ def parse(file)
35
+ # Just return an empty log
36
+ []
37
+ end
38
+ end
39
+
40
+ # Register it
41
+ foo_parser = FooParser.new
42
+ ChatStew.register(foo_parser)
43
+ # Will use foo_parser, so parse_result will be an empty array.
44
+ parse_result = ChatStew.parse("whatever.foo")
45
+
46
+ ## Author
47
+ Gabe Berke-Williams, 2011
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => [:spec]
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "chat_stew/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "chat_stew"
7
+ s.version = ChatStew::VERSION
8
+ s.authors = ["Gabe Berke-Williams"]
9
+ s.email = ["gabe@thoughtbot.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{An Adium log parser with an easy interface. Supports custom parsers too.}
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = "chat_stew"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency("nokogiri", "~> 1.5.0")
22
+
23
+ s.add_development_dependency("rspec", "~> 2.6.0")
24
+ s.add_development_dependency("bourne", "~> 1.0")
25
+ end
@@ -0,0 +1,30 @@
1
+ require "chat_stew/version"
2
+ require "chat_stew/adium_message"
3
+ require "chat_stew/parsers/adium"
4
+
5
+ module ChatStew
6
+ def self.register(parser)
7
+ parsers.unshift(parser)
8
+ end
9
+
10
+ def self.parse(input)
11
+ input = StringIO.new(input) unless input.respond_to?(:read)
12
+
13
+ valid_parser = parsers.detect {|parser| parser.can_parse?(input) }
14
+ if valid_parser.nil?
15
+ nil
16
+ else
17
+ valid_parser.parse(input)
18
+ end
19
+ end
20
+
21
+ def self.clear_parsers!
22
+ @parsers = []
23
+ end
24
+
25
+ private
26
+
27
+ def self.parsers
28
+ @parsers ||= []
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module ChatStew
2
+ AdiumMessage = Struct.new(:sender, :receiver, :alias, :message, :time)
3
+ end
@@ -0,0 +1,31 @@
1
+ require 'nokogiri'
2
+ require 'date'
3
+ require 'time'
4
+
5
+ module ChatStew
6
+ module Parsers
7
+ class Adium
8
+ def can_parse?(file)
9
+ contents = file.read
10
+ file.rewind
11
+ return contents.include?('<?xml version="1.0" encoding="UTF-8" ?>') &&
12
+ contents.include?('<chat xmlns="http://purl.org/net/ulf/ns/0.4-02"')
13
+ end
14
+
15
+ def parse(file)
16
+ doc = Nokogiri.XML(file)
17
+ chat_messages = doc.search('chat message')
18
+ account = doc.at('chat')[:account]
19
+ other_account = chat_messages.detect{|m| m[:sender] != account }[:sender]
20
+
21
+ chat_messages.map do |message|
22
+ AdiumMessage.new(message[:sender],
23
+ message[:sender] == account ? other_account : account,
24
+ message[:alias],
25
+ message.text,
26
+ DateTime.parse(message[:time]))
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module ChatStew
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe ChatStew::Parsers::Adium, "#can_parse?" do
4
+ let(:adium_log) { File.join('spec', 'support', 'logs', 'adium_log.xml') }
5
+ let(:random_log) { File.join('spec', 'support', 'logs', 'random_log.html') }
6
+
7
+ it "returns true for an Adium log" do
8
+ subject.can_parse?(adium_log).should be_true
9
+ end
10
+
11
+ it "returns false for a non-Adium log" do
12
+ subject.can_parse?(random_log).should be_false
13
+ end
14
+ end
15
+
16
+ describe ChatStew::Parsers::Adium, "#parse" do
17
+ let(:adium_log) { File.new(File.join('spec', 'support', 'logs', 'adium_log.xml')) }
18
+ let(:parsed) { subject.parse(adium_log) }
19
+
20
+ it "correctly parses the sender" do
21
+ parsed[0].sender.should == "this_is_me"
22
+ parsed[1].sender.should == "this_is_them"
23
+ end
24
+
25
+ it "correctly parses the receiver" do
26
+ parsed[0].receiver.should == "this_is_them"
27
+ parsed[1].receiver.should == "this_is_me"
28
+ end
29
+
30
+ it "correctly parses the alias" do
31
+ parsed[0].alias.should == "my_alias"
32
+ parsed[1].alias.should == "their_alias"
33
+ end
34
+
35
+ it "correctly parses the message" do
36
+ parsed[0].message.should == "first message > second message"
37
+ parsed[1].message.should == "untrue! & I can prove it"
38
+ end
39
+
40
+ it "correctly parses the time" do
41
+ parsed[0].time.should == DateTime.parse("2009-01-10T10:14:28-05:00")
42
+ parsed[1].time.should == DateTime.parse("2009-01-10T20:15:30-05:00")
43
+ end
44
+ end
@@ -0,0 +1,75 @@
1
+ require "spec_helper"
2
+
3
+ describe ChatStew, ".register" do
4
+ let(:parser) { stub("Parser", :can_parse? => true, :parse => []) }
5
+ let(:log) { stub("Chat Log", :read => "asdf") }
6
+
7
+ before { ChatStew.clear_parsers! }
8
+
9
+ it "registers the given parser" do
10
+ ChatStew.register(parser)
11
+ ChatStew.parse(log)
12
+
13
+ parser.should have_received(:parse).with(log)
14
+ end
15
+ end
16
+
17
+ describe ChatStew, ".parse" do
18
+ let(:parser_one) { stub("Parser", :can_parse? => true, :parse => ['one']) }
19
+ let(:parser_two) { stub("Parser", :can_parse? => true, :parse => ['two']) }
20
+ let(:invalid_parser) { stub("Parser", :can_parse? => false, :parse => ['two']) }
21
+ let(:log) { stub("Adium Log", :read => "asdf") }
22
+
23
+ before { ChatStew.clear_parsers! }
24
+
25
+ it "only uses parsers that can parse the file" do
26
+ ChatStew.register(parser_one)
27
+ ChatStew.register(invalid_parser)
28
+ ChatStew.parse(log)
29
+
30
+ invalid_parser.should have_received(:parse).never
31
+ parser_one.should have_received(:parse).with(log)
32
+ end
33
+
34
+ it "parses with the most-recently-registered parser" do
35
+ ChatStew.register(parser_one)
36
+ ChatStew.register(parser_two)
37
+ ChatStew.parse(log)
38
+
39
+ parser_one.should have_received(:parse).never
40
+ parser_two.should have_received(:parse).with(log)
41
+ end
42
+
43
+ it "returns nil if no parser is available" do
44
+ ChatStew.parse(log).should be_nil
45
+ end
46
+
47
+ context "input" do
48
+ let(:adium_log_path) { File.join('spec', 'support', 'logs', 'adium_log.xml') }
49
+ let(:mock_parser) { MockParser.new }
50
+ before { ChatStew.register(mock_parser) }
51
+
52
+ it "accepts a String" do
53
+ ChatStew.parse(adium_log_path)
54
+ mock_parser.should have_parsed
55
+ mock_parser.input.should respond_to(:read)
56
+ end
57
+
58
+ it "accepts a File" do
59
+ ChatStew.parse(File.new(adium_log_path))
60
+ mock_parser.input.should respond_to(:read)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe ChatStew, ".clear_parsers!" do
66
+ let(:parser) { stub("Parser", :can_parse? => true, :parse => ['one']) }
67
+ let(:log) { stub("Adium Log", :read => "asdf") }
68
+
69
+ it "clears all parsers" do
70
+ ChatStew.register(parser)
71
+ ChatStew.clear_parsers!
72
+ ChatStew.parse(log)
73
+ parser.should have_received(:parse).never
74
+ end
75
+ end
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__))
2
+ $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require "rspec"
5
+ require "bourne"
6
+
7
+ require "chat_stew"
8
+
9
+ require "./spec/support/mock_parser"
10
+
11
+ RSpec.configure do |c|
12
+ c.mock_with :mocha
13
+ end
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <chat xmlns="http://purl.org/net/ulf/ns/0.4-02" account="this_is_me" service="AIM">
3
+ <message sender="this_is_me" time="2009-01-10T10:14:28-05:00" alias="my_alias"><div><span style="font-family: Helvetica; font-size: 12pt;">first message &gt; second message</span></div></message>
4
+ <message sender="this_is_them" time="2009-01-10T20:15:30-05:00" alias="their_alias"><div><span style="font-family: Helvetica; font-size: 12pt;">untrue! &amp; I can prove it</span></div></message>
5
+ </chat>
@@ -0,0 +1,8 @@
1
+ <head><meta http-equiv="content-type" content="text/html; charset=UTF-8"><title>Conversation with buddy_screen_name at 2007-06-16 13:51:36 on my screen name (aim)</title></head><h3>Conversation with buddy_screen_name at 2007-06-16 13:51:36 on my screen name (aim)</h3>
2
+ <font color="#A82F2F"><font size="2">(13:51:35)</font> <b>buddy_alias:</b></font> <font sml="AIM/ICQ">received</font><br/>
3
+ <font color="#16569E"><font size="2">(14:37:20)</font> <b>my_alias:</b></font> <font sml="AIM/ICQ"><span style='font-size: small; '>sent</span></font><br/>
4
+ <font color="#16569E"><font size="2">(14:39:05)</font> <b>my_alias:</b></font> <font sml="AIM/ICQ"><span style='font-size: small; '><a HREF="http://www.crazymonkeygames.com/Boxhead-2Play-Rooms.html">http://www.crazymonkeygames.com/Boxhead-2Play-Rooms.html</a></span></font><br/>
5
+ <font color="#6C2585"><font size="2">(14:39:20)</font> <b>***my_alias:</b></font> <font sml="AIM/ICQ"><span style='font-size: small; '>meified</span></font><br/>
6
+ <font color="#6C2585"><font size="2">(14:39:21)</font> <b>my_alias:</b></font> <font sml="AIM/ICQ"><span style='font-size: small; '>extra link<a HREF="http://www.nytimes.com/aponline/us/AP-Suspicious-Devices.html?_r=1&amp;ref=us&amp;oref=slogin"></a></span></font><br/>
7
+ <font size="2">(14:53:22)</font><b> buddy_screen_name logged out.</b><br/>
8
+ <font color="#FF0000"><font size="2">(14:54:00)</font><b> Unable to send message: Refused by client</b></font><br/>
@@ -0,0 +1,24 @@
1
+ class MockParser
2
+ attr_reader :input
3
+
4
+ def initialize
5
+ @did_parse = false
6
+ @input = nil
7
+ end
8
+
9
+ def can_parse?(anything)
10
+ true
11
+ end
12
+
13
+ def parse(input)
14
+ @did_parse = true
15
+ @input = input
16
+
17
+ []
18
+ end
19
+
20
+ def has_parsed?
21
+ @did_parse == true
22
+ end
23
+ end
24
+
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chat_stew
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Gabe Berke-Williams
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-10-18 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 1
31
+ - 5
32
+ - 0
33
+ version: 1.5.0
34
+ type: :runtime
35
+ name: nokogiri
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 23
45
+ segments:
46
+ - 2
47
+ - 6
48
+ - 0
49
+ version: 2.6.0
50
+ type: :development
51
+ name: rspec
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 15
61
+ segments:
62
+ - 1
63
+ - 0
64
+ version: "1.0"
65
+ type: :development
66
+ name: bourne
67
+ version_requirements: *id003
68
+ description: An Adium log parser with an easy interface. Supports custom parsers too.
69
+ email:
70
+ - gabe@thoughtbot.com
71
+ executables: []
72
+
73
+ extensions: []
74
+
75
+ extra_rdoc_files: []
76
+
77
+ files:
78
+ - .gitignore
79
+ - .rspec
80
+ - Gemfile
81
+ - README.md
82
+ - Rakefile
83
+ - chat_stew.gemspec
84
+ - lib/chat_stew.rb
85
+ - lib/chat_stew/adium_message.rb
86
+ - lib/chat_stew/parsers/adium.rb
87
+ - lib/chat_stew/version.rb
88
+ - spec/chat_stew/adium_parser.rb
89
+ - spec/chat_stew_spec.rb
90
+ - spec/spec_helper.rb
91
+ - spec/support/logs/adium_log.xml
92
+ - spec/support/logs/random_log.html
93
+ - spec/support/mock_parser.rb
94
+ has_rdoc: true
95
+ homepage: ""
96
+ licenses: []
97
+
98
+ post_install_message:
99
+ rdoc_options: []
100
+
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ hash: 3
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project: chat_stew
124
+ rubygems_version: 1.6.2
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: An Adium log parser with an easy interface. Supports custom parsers too.
128
+ test_files:
129
+ - spec/chat_stew/adium_parser.rb
130
+ - spec/chat_stew_spec.rb
131
+ - spec/spec_helper.rb
132
+ - spec/support/logs/adium_log.xml
133
+ - spec/support/logs/random_log.html
134
+ - spec/support/mock_parser.rb