chat_stew 0.0.1

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