pipio 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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +2 -0
  4. data/.simplecov +5 -0
  5. data/.travis.yml +12 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +20 -0
  8. data/NEWS.md +10 -0
  9. data/README.md +88 -0
  10. data/Rakefile +13 -0
  11. data/lib/pipio.rb +34 -0
  12. data/lib/pipio/alias_registry.rb +26 -0
  13. data/lib/pipio/chat.rb +39 -0
  14. data/lib/pipio/cleaners/html_cleaner.rb +95 -0
  15. data/lib/pipio/cleaners/text_cleaner.rb +15 -0
  16. data/lib/pipio/file_reader.rb +29 -0
  17. data/lib/pipio/message_creators/auto_or_xml_message_creator.rb +25 -0
  18. data/lib/pipio/message_creators/event_message_creator.rb +47 -0
  19. data/lib/pipio/message_creators/status_message_creator.rb +19 -0
  20. data/lib/pipio/messages/auto_reply_message.rb +7 -0
  21. data/lib/pipio/messages/event.rb +67 -0
  22. data/lib/pipio/messages/message.rb +23 -0
  23. data/lib/pipio/messages/status_message.rb +26 -0
  24. data/lib/pipio/messages/xml_message.rb +43 -0
  25. data/lib/pipio/metadata.rb +34 -0
  26. data/lib/pipio/metadata_parser.rb +55 -0
  27. data/lib/pipio/parser_factory.rb +32 -0
  28. data/lib/pipio/parsers/basic_parser.rb +83 -0
  29. data/lib/pipio/parsers/html_log_parser.rb +22 -0
  30. data/lib/pipio/parsers/null_parser.rb +9 -0
  31. data/lib/pipio/parsers/text_log_parser.rb +21 -0
  32. data/lib/pipio/tag_balancer.rb +163 -0
  33. data/lib/pipio/time_parser.rb +36 -0
  34. data/lib/pipio/version.rb +3 -0
  35. data/pipio.gemspec +27 -0
  36. data/spec/pipio/alias_registry_spec.rb +37 -0
  37. data/spec/pipio/chat_spec.rb +66 -0
  38. data/spec/pipio/cleaners/html_cleaner_spec.rb +102 -0
  39. data/spec/pipio/cleaners/text_cleaner_spec.rb +29 -0
  40. data/spec/pipio/file_reader_spec.rb +130 -0
  41. data/spec/pipio/messages/auto_reply_message_spec.rb +40 -0
  42. data/spec/pipio/messages/event_spec.rb +41 -0
  43. data/spec/pipio/messages/status_message_spec.rb +43 -0
  44. data/spec/pipio/messages/xml_message_spec.rb +55 -0
  45. data/spec/pipio/metadata_parser_spec.rb +81 -0
  46. data/spec/pipio/metadata_spec.rb +72 -0
  47. data/spec/pipio/parser_factory_spec.rb +31 -0
  48. data/spec/pipio/parsers/html_log_parser_spec.rb +160 -0
  49. data/spec/pipio/parsers/null_parser_spec.rb +13 -0
  50. data/spec/pipio/parsers/text_log_parser_spec.rb +37 -0
  51. data/spec/pipio/tag_balancer_spec.rb +16 -0
  52. data/spec/pipio/time_parser_spec.rb +66 -0
  53. data/spec/pipio_spec.rb +63 -0
  54. data/spec/spec_helper.rb +18 -0
  55. data/spec/support/chat_builder.rb +29 -0
  56. data/spec/support/chat_builder_helpers.rb +41 -0
  57. data/spec/support/file_builder.rb +22 -0
  58. data/spec/support/html_chat_builder.rb +67 -0
  59. data/spec/support/logfiles/2006-12-21.223606.txt +3 -0
  60. data/spec/support/logfiles/2008-01-15.071445-0500PST.htm +5 -0
  61. data/spec/support/logfiles/2008-01-15.071445-0500PST.html +5 -0
  62. data/spec/support/text_chat_builder.rb +21 -0
  63. data/spec/test-output/README.md +1 -0
  64. data/spec/test-output/html_log_output.xml +6 -0
  65. data/spec/test-output/text_log_output.xml +4 -0
  66. metadata +193 -0
@@ -0,0 +1,29 @@
1
+ describe Pipio::Cleaners::TextCleaner, '.clean' do
2
+ it 'removes \r' do
3
+ expect(clean("\r")).to eq('')
4
+ end
5
+
6
+ it 'converts & to &' do
7
+ expect(clean('&')).to eq('&')
8
+ end
9
+
10
+ it 'converts < to &lt;' do
11
+ expect(clean('<')).to eq('&lt;')
12
+ end
13
+
14
+ it 'converts > to &gt;' do
15
+ expect(clean('>')).to eq('&gt;')
16
+ end
17
+
18
+ it 'converts " to &quot;' do
19
+ expect(clean('"')).to eq('&quot;')
20
+ end
21
+
22
+ it "converts ' to &apos;" do
23
+ expect(clean("'")).to eq('&apos;')
24
+ end
25
+
26
+ def clean(line)
27
+ Pipio::Cleaners::TextCleaner.clean(line)
28
+ end
29
+ end
@@ -0,0 +1,130 @@
1
+ require 'tempfile'
2
+
3
+ module FileReaderHelpers
4
+ def no_op_cleaner
5
+ Class.new do
6
+ def self.clean(line)
7
+ line
8
+ end
9
+ end
10
+ end
11
+
12
+ def dirty_cleaner
13
+ Class.new do
14
+ def self.clean(line)
15
+ if line == "dirty"
16
+ "clean"
17
+ else
18
+ line
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def temporary_file
25
+ Tempfile.new('whatever').tap do |file|
26
+ yield file
27
+ file.flush
28
+ end
29
+ end
30
+ end
31
+
32
+ describe Pipio::FileReader, 'with a nonexistent file' do
33
+ include FileReaderHelpers
34
+
35
+ it 'does not crash' do
36
+ file_reader = Pipio::FileReader.new('nonexistent', no_op_cleaner)
37
+ expect { file_reader.read }.not_to raise_error
38
+ end
39
+
40
+ it 'has empty first_line' do
41
+ file_reader = Pipio::FileReader.new('nonexistent', no_op_cleaner)
42
+ file_reader.read
43
+ expect(file_reader.first_line).to eq('')
44
+ end
45
+
46
+ it 'has empty other_lines' do
47
+ file_reader = Pipio::FileReader.new('nonexistent', no_op_cleaner)
48
+ file_reader.read
49
+ expect(file_reader.other_lines).to eq([])
50
+ end
51
+ end
52
+
53
+ describe Pipio::FileReader, '#first_line' do
54
+ include FileReaderHelpers
55
+
56
+ it 'returns the first line' do
57
+ file = temporary_file do |f|
58
+ f.puts 'first'
59
+ f.puts 'second'
60
+ end
61
+
62
+ file_reader = Pipio::FileReader.new(file.path, no_op_cleaner)
63
+ file_reader.read
64
+ expect(file_reader.first_line).to eq('first')
65
+ end
66
+
67
+ it 'does not clean the first line' do
68
+ file = temporary_file do |f|
69
+ f.puts "dirty"
70
+ end
71
+
72
+ file_reader = Pipio::FileReader.new(file.path, dirty_cleaner)
73
+ file_reader.read
74
+ expect(file_reader.first_line).to eq("dirty")
75
+ end
76
+ end
77
+
78
+ describe Pipio::FileReader, '#other_lines' do
79
+ include FileReaderHelpers
80
+
81
+ it 'grabs the other lines' do
82
+ file = temporary_file do |f|
83
+ f.puts 'first'
84
+ f.puts 'second'
85
+ f.puts 'third'
86
+ end
87
+
88
+ file_reader = Pipio::FileReader.new(file.path, no_op_cleaner)
89
+ file_reader.read
90
+ expect(file_reader.other_lines).to eq(%w(second third))
91
+ end
92
+
93
+ it 'cleans the other lines' do
94
+ file = temporary_file do |f|
95
+ f.puts "first"
96
+ f.puts "dirty"
97
+ end
98
+
99
+ file_reader = Pipio::FileReader.new(file.path, dirty_cleaner)
100
+ file_reader.read
101
+ expect(file_reader.other_lines).to eq(%w(clean))
102
+ end
103
+
104
+
105
+ it 'removes empty lines from the other lines' do
106
+ file = temporary_file do |f|
107
+ f.puts "first"
108
+ f.puts "before"
109
+ f.puts
110
+ f.puts "after"
111
+ end
112
+
113
+ file_reader = Pipio::FileReader.new(file.path, no_op_cleaner)
114
+ file_reader.read
115
+ expect(file_reader.other_lines).to eq(%w(before after))
116
+ end
117
+
118
+ it 'removes all-whitespace lines from the other lines' do
119
+ file = temporary_file do |f|
120
+ f.puts "first"
121
+ f.puts "before"
122
+ f.puts " "
123
+ f.puts "after"
124
+ end
125
+
126
+ file_reader = Pipio::FileReader.new(file.path, no_op_cleaner)
127
+ file_reader.read
128
+ expect(file_reader.other_lines).to eq(%w(before after))
129
+ end
130
+ end
@@ -0,0 +1,40 @@
1
+ describe Pipio::AutoReplyMessage, '#to_s' do
2
+ it 'has the correct sender_screen_name' do
3
+ expect(auto_reply_message(sender_screen_name: "hello").to_s).to include 'sender="hello"'
4
+ end
5
+
6
+ it 'has the correct alias' do
7
+ expect(auto_reply_message(sender_alias: "garner").to_s).to include 'alias="garner"'
8
+ end
9
+
10
+ it 'has the correct time' do
11
+ time = Time.now
12
+ formatted_time = time.xmlschema
13
+ expect(auto_reply_message(time: time).to_s).to include %(time="#{formatted_time}")
14
+ end
15
+
16
+ it 'has the correct body' do
17
+ body = "hello"
18
+ styled_body = %(<div><span style="font-family: Helvetica; font-size: 12pt;">#{body}</span></div>)
19
+ expect(auto_reply_message(body: body).to_s).to include styled_body
20
+ end
21
+
22
+ it 'has the auto attribute set to true' do
23
+ expect(auto_reply_message.to_s).to include 'auto="true"'
24
+ end
25
+
26
+ def auto_reply_message(options = {})
27
+ options = default_options.merge(options)
28
+ Pipio::AutoReplyMessage.new(options[:sender_screen_name],
29
+ options[:time], options[:sender_alias], options[:body])
30
+ end
31
+
32
+ def default_options
33
+ {
34
+ sender_screen_name: 'jim_sender',
35
+ time: Time.now,
36
+ sender_alias: 'jim alias',
37
+ body: 'body'
38
+ }
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ describe Pipio::Event, '#to_s' do
2
+ it 'has the correct sender screen name' do
3
+ sender_screen_name = 'bob'
4
+ result = create_event(sender_screen_name: sender_screen_name).to_s
5
+ expect(result).to include %(sender="#{sender_screen_name}")
6
+ end
7
+
8
+ it 'has the correct time' do
9
+ time = Time.now
10
+ formatted_time = time.xmlschema
11
+ result = create_event(time: time).to_s
12
+ expect(result).to include %(time="#{formatted_time}")
13
+ end
14
+
15
+ it 'has the correct alias' do
16
+ sender_alias = 'jane_alias'
17
+ result = create_event(sender_alias: sender_alias).to_s
18
+ expect(result).to include %(alias="#{sender_alias}")
19
+ end
20
+
21
+ it 'has the correct body' do
22
+ body = 'body'
23
+ styled_body = %(<div><span style="font-family: Helvetica; font-size: 12pt;">#{body}</span></div>)
24
+ result = create_event(body: body).to_s
25
+ expect(result).to include styled_body
26
+ end
27
+
28
+ it 'is an event tag' do
29
+ expect(create_event.to_s).to match(/^<event/)
30
+ end
31
+
32
+ def create_event(opts = {})
33
+ opts[:sender_screen_name] ||= 'jim_sender'
34
+ opts[:time] ||= Time.now
35
+ opts[:sender_alias] ||= 'jane_alias'
36
+ opts[:body] ||= 'body'
37
+ opts[:event_type] ||= 'libPurpleEvent'
38
+
39
+ Pipio::Event.new(opts[:sender_screen_name], opts[:time], opts[:sender_alias], opts[:body], opts[:event_type])
40
+ end
41
+ end
@@ -0,0 +1,43 @@
1
+ describe Pipio::StatusMessage, '#to_s' do
2
+ it 'has the correct sender' do
3
+ sender_screen_name = 'bob'
4
+ result = create_status_message(sender_screen_name: sender_screen_name).to_s
5
+ expect(result).to include %(sender="#{sender_screen_name}")
6
+ end
7
+
8
+ it 'has the correct time' do
9
+ time = Time.now
10
+ formatted_time = time.xmlschema
11
+ result = create_status_message(time: time).to_s
12
+ expect(result).to include %(time="#{formatted_time}")
13
+ end
14
+
15
+ it 'has the correct alias' do
16
+ sender_alias = 'jane_alias'
17
+ result = create_status_message(sender_alias: sender_alias).to_s
18
+ expect(result).to include %(alias="#{sender_alias}")
19
+ end
20
+
21
+ it 'has the correct status' do
22
+ status = 'status'
23
+ result = create_status_message(status: status).to_s
24
+ expect(result).to include %(type="#{status}")
25
+ end
26
+
27
+ it 'is a status tag' do
28
+ expect(create_status_message.to_s).to match(/^<status/)
29
+ end
30
+
31
+ it 'ends in a newline' do
32
+ expect(create_status_message.to_s).to match(/\n$/)
33
+ end
34
+
35
+ def create_status_message(opts = {})
36
+ opts[:sender_screen_name] ||= 'jim_sender'
37
+ opts[:time] ||= Time.now
38
+ opts[:sender_alias] ||= 'jane_alias'
39
+ opts[:status] ||= 'status'
40
+
41
+ Pipio::StatusMessage.new(opts[:sender_screen_name], opts[:time], opts[:sender_alias], opts[:status])
42
+ end
43
+ end
@@ -0,0 +1,55 @@
1
+ describe Pipio::XMLMessage, '#to_s' do
2
+ it 'has the correct sender_screen_name' do
3
+ expect(create_xml_message(sender_screen_name: 'jim').to_s).to include %(sender="jim")
4
+ end
5
+
6
+ it 'has the correct alias' do
7
+ expect(create_xml_message(sender_alias: 'Jim Alias').to_s).to include %(alias="Jim Alias")
8
+ end
9
+
10
+ it 'has the correct time' do
11
+ time = Time.now
12
+ formatted_time = time.xmlschema
13
+ expect(create_xml_message(time: time).to_s).to include %(time="#{formatted_time}")
14
+ end
15
+
16
+ it 'has the correct body' do
17
+ unstyled_body = 'unstyled'
18
+ styled_body = %(<div><span style="font-family: Helvetica; font-size: 12pt;">#{unstyled_body}</span></div>)
19
+ expect(create_xml_message(body: unstyled_body).to_s).to include styled_body
20
+ end
21
+
22
+ context "normalization" do
23
+ it 'balances the tags in the body' do
24
+ unbalanced_body = '<div>unbalanced'
25
+ expect(create_xml_message(body: unbalanced_body).body).to eq('<div>unbalanced</div>')
26
+ end
27
+
28
+ it 'removes *** from the beginning of the sender alias' do
29
+ alias_with_stars = '***Jim'
30
+ alias_without_stars = 'Jim'
31
+ expect(create_xml_message(sender_alias: alias_with_stars).to_s).to include %(alias="#{alias_without_stars}")
32
+ end
33
+
34
+ it 'escapes & to &amp;' do
35
+ expect(create_xml_message(body: '&').body).to eq('&amp;')
36
+ end
37
+
38
+ %w(lt gt amp quot apos).each do |entity|
39
+ it "does not escape &#{entity};" do
40
+ entity_body = "&#{entity};"
41
+ expect(create_xml_message(body: entity_body).body).to eq(entity_body)
42
+ end
43
+ end
44
+ end
45
+
46
+ def create_xml_message(opts = {})
47
+ opts[:sender_screen_name] ||= 'jim_sender'
48
+ opts[:time] ||= Time.now
49
+ opts[:sender_alias] ||= 'jane_alias'
50
+ opts[:body] ||= 'body'
51
+ opts[:event_type] ||= 'libPurpleEvent'
52
+
53
+ Pipio::XMLMessage.new(opts[:sender_screen_name], opts[:time], opts[:sender_alias], opts[:body])
54
+ end
55
+ end
@@ -0,0 +1,81 @@
1
+ describe Pipio::MetadataParser do
2
+ context '#parse' do
3
+ it "finds my screen name" do
4
+ file = create_chat_file('log.html') do |b|
5
+ b.first_line from: 'JIM'
6
+ end
7
+
8
+ metadata = Pipio::MetadataParser.new(first_line_of(file)).parse
9
+ expect(metadata[:my_screen_name]).to eq('JIM')
10
+ end
11
+
12
+ it "finds their screen name" do
13
+ file = create_chat_file('log.html') do |b|
14
+ b.first_line to: 'lady anne'
15
+ end
16
+
17
+ metadata = Pipio::MetadataParser.new(first_line_of(file)).parse
18
+ expect(metadata[:their_screen_name]).to eq('lady anne')
19
+ end
20
+
21
+ it 'finds the start time' do
22
+ time_string = '2008-04-01 22:36:06'
23
+ file = create_chat_file('log.html') do |b|
24
+ b.first_line time: time_string
25
+ end
26
+
27
+ metadata = Pipio::MetadataParser.new(first_line_of(file)).parse
28
+ expect(metadata[:start_time]).to eq(Time.parse(time_string))
29
+ end
30
+
31
+ it 'finds the correct service' do
32
+ path = create_chat_file('log.html') do |b|
33
+ b.first_line service: 'aim'
34
+ end
35
+
36
+ metadata = Pipio::MetadataParser.new(first_line_of(path)).parse
37
+
38
+ expect(metadata[:service]).to eq 'aim'
39
+ end
40
+
41
+ it 'can detect peculiar times' do
42
+ time_string = "1/15/2008 7:14:45 AM"
43
+ expected_time = Time.parse('2008-01-15 07:14:45')
44
+ file = create_chat_file('log.html') do |b|
45
+ b.first_line time: time_string
46
+ end
47
+
48
+ metadata = Pipio::MetadataParser.new(first_line_of(file)).parse
49
+ expect(metadata[:start_time]).to eq(expected_time)
50
+ end
51
+
52
+ it 'sets all attributes to nil when initialized with an empty string' do
53
+ metadata = Pipio::MetadataParser.new('').parse
54
+ assert_all_attributes_nil(metadata)
55
+ end
56
+
57
+ it 'sets all attributes to nil when initialized with nil' do
58
+ metadata = Pipio::MetadataParser.new(nil).parse
59
+ assert_all_attributes_nil(metadata)
60
+ end
61
+
62
+ it 'resets all attributes to nil when given a non-standard file to parse' do
63
+ file = FileBuilder.create_file('nonstandard.html')
64
+ file.write '<HTML><BODY BGCOLOR="#ffffff"><B><FONT COLOR="#ff0000" LANG="0">jiggerific bug<!-- (3:22:29 PM)--></B></FONT><FONT COLOR="#ff0000" BACK="#ffffff">:</FONT><FONT COLOR="#000000"> try direct IM now</FONT><BR>'
65
+ file.close
66
+
67
+ metadata = Pipio::MetadataParser.new(first_line_of(file)).parse
68
+ assert_all_attributes_nil(metadata)
69
+ end
70
+ end
71
+
72
+ def first_line_of(file)
73
+ File.readlines(file).first
74
+ end
75
+
76
+ def assert_all_attributes_nil(metadata)
77
+ expect(metadata[:start_time]).to be_nil
78
+ expect(metadata[:their_screen_name]).to be_nil
79
+ expect(metadata[:my_screen_name]).to be_nil
80
+ end
81
+ end
@@ -0,0 +1,72 @@
1
+ describe Pipio::Metadata do
2
+ context '#service' do
3
+ it "returns the service" do
4
+ metadata = Pipio::Metadata.new(service: 'aim')
5
+ expect(metadata.service).to eq('aim')
6
+ end
7
+ end
8
+
9
+ context '#my_screen_name' do
10
+ it "returns my normalized screen name" do
11
+ metadata = Pipio::Metadata.new(my_screen_name: 'JIM BOB')
12
+ expect(metadata.my_screen_name).to eq('jimbob')
13
+ end
14
+ end
15
+
16
+ context '#their_screen_name' do
17
+ it "returns their screen name" do
18
+ metadata = Pipio::Metadata.new(their_screen_name: 'lady anne')
19
+ expect(metadata.their_screen_name).to eq('lady anne')
20
+ end
21
+ end
22
+
23
+ context '#start_time' do
24
+ it 'returns the start time' do
25
+ time = Time.now
26
+ metadata = Pipio::Metadata.new(start_time: time)
27
+
28
+ expect(metadata.start_time).to eq(time)
29
+ end
30
+ end
31
+
32
+ context '#start_month' do
33
+ it 'returns the start month' do
34
+ start_time = Time.now
35
+ metadata = Pipio::Metadata.new(start_time: start_time)
36
+ expect(metadata.start_month).to eq(start_time.mon)
37
+ end
38
+ end
39
+
40
+ context '#start_year' do
41
+ it 'returns the start year' do
42
+ start_time = Time.now
43
+ metadata = Pipio::Metadata.new(start_time: start_time)
44
+ expect(metadata.start_year).to eq(start_time.year)
45
+ end
46
+ end
47
+
48
+ context '#start_mday' do
49
+ it 'returns the start mday' do
50
+ start_time = Time.now
51
+ metadata = Pipio::Metadata.new(start_time: start_time)
52
+ expect(metadata.start_mday).to eq(start_time.mday)
53
+ end
54
+ end
55
+
56
+ context '#valid?' do
57
+ it 'is true when all attributes are provided' do
58
+ metadata = Pipio::Metadata.new(my_screen_name: '',
59
+ their_screen_name: '',
60
+ start_time: '',
61
+ service: 'aim'
62
+ )
63
+ expect(metadata).to be_valid
64
+ end
65
+
66
+ [:my_screen_name, :their_screen_name, :start_time, :service].each do |attribute|
67
+ it "is false when #{attribute} cannot be detected" do
68
+ expect(Pipio::Metadata.new(attribute => nil)).not_to be_valid
69
+ end
70
+ end
71
+ end
72
+ end