ppl 1.18.0 → 1.19.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a9d02380f3d44f146c22dd0e466d98ae56ac97d
4
- data.tar.gz: e85bf61f41a01927c72450f761563ad9669c5fdd
3
+ metadata.gz: 1e392faaea1882705856f5dbe483e493ca0c427a
4
+ data.tar.gz: 2334459a46cc72d92aa8c3c2f0a500a15de3fb5c
5
5
  SHA512:
6
- metadata.gz: 0f325d2c01e00c070edac9f3c6066185bf13bb20c7c25f1c659534485a833e28b7b5b6c7a25b42fe016f33b417800df2567a7ae591b8736a49027cc1cdada069
7
- data.tar.gz: a388cc2f1f2af9c50afa01da54ac54994c9d2b25f290a75e07bc041b8f9c4051c27697fc997bdaec46d2739c920d7d610eeb814261610c90525ac1e30b1c9cca
6
+ metadata.gz: 6227509847307b130c84049c0ce92063ea9079320e76e9530021a0fe7b3cd75425e1276abc2fbc35dcb2e6c0bb85f0c44181cd5235362e9fca4764f167cab584
7
+ data.tar.gz: 0e737fbd7ec0817918693c406587efab38d7f8f3dc8eb1f11577669665cf2a0750e5fdf70c6c445716848e94e6d45aeb7d47f6d316eb2625937c120e1cff60bc
data/completions/bash CHANGED
@@ -6,7 +6,7 @@ _ppl()
6
6
  prev="${COMP_WORDS[COMP_CWORD-1]}"
7
7
 
8
8
  # Complete options
9
- opts="add age bday email init ls mutt mv name nick org phone post pull push remote rm shell show url version"
9
+ opts="add age bday completion email init ls mutt mv name nick org phone post pull push remote rm scrape shell show url version"
10
10
  if [[ ${prev} == "ppl" ]]; then
11
11
  COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
12
12
  return 0
data/completions/zsh CHANGED
@@ -10,6 +10,7 @@ _1st_arguments=(
10
10
  "add:Add a new contact"
11
11
  "age:List or show contacts's ages"
12
12
  "bday:List, show or change birthdays"
13
+ "completion:Output shell completion function"
13
14
  "email:Show or change a contact's email address"
14
15
  "init:Create an empty address book"
15
16
  "ls:List all contacts"
@@ -24,6 +25,7 @@ _1st_arguments=(
24
25
  "push:Execute 'git push' in the address book directory"
25
26
  "remote:Execute 'git remote' in the address book directory"
26
27
  "rm:Delete a contact"
28
+ "scrape:Scrape contact details from stdin"
27
29
  "shell:Interactive mode"
28
30
  "show:Display the full details of a contact"
29
31
  "url:List, show or change URLs"
data/lib/ppl.rb CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  module Ppl
3
3
 
4
- Version = "1.18.0"
4
+ Version = "1.19.0"
5
5
 
6
6
  module Adapter
7
7
  end
@@ -26,6 +26,8 @@ end
26
26
 
27
27
  require "ppl/adapter/color"
28
28
  require "ppl/adapter/color/colored"
29
+ require "ppl/adapter/email_scraper"
30
+ require "ppl/adapter/email_scraper/mail"
29
31
  require "ppl/adapter/storage"
30
32
  require "ppl/adapter/storage/disk"
31
33
  require "ppl/adapter/storage/factory"
@@ -64,6 +66,7 @@ require "ppl/command/url"
64
66
  require "ppl/command/version"
65
67
  require "ppl/command/external"
66
68
  require "ppl/command/completion"
69
+ require "ppl/command/scrape"
67
70
 
68
71
  require "ppl/entity/address_book"
69
72
  require "ppl/entity/contact"
@@ -0,0 +1,9 @@
1
+
2
+ class Ppl::Adapter::EmailScraper
3
+
4
+ def scrape_contacts(email)
5
+ raise NotImplementedError
6
+ end
7
+
8
+ end
9
+
@@ -0,0 +1,40 @@
1
+
2
+ require "mail"
3
+
4
+ class Ppl::Adapter::EmailScraper::Mail
5
+
6
+ def scrape_contacts(email)
7
+ email = Mail.new(email)
8
+ contacts = []
9
+
10
+ sender = scrape_sender(email)
11
+ unless sender.nil?
12
+ contacts << sender
13
+ end
14
+
15
+ contacts
16
+ end
17
+
18
+ private
19
+
20
+ def scrape_sender(email)
21
+ from = email[:from]
22
+ unless from.nil?
23
+ sender = Ppl::Entity::Contact.new
24
+ sender.name = from.tree.addresses.first.display_name
25
+ sender.email_addresses << from.tree.addresses.first.address
26
+ sender.id = generate_contact_id(sender)
27
+ sender
28
+ end
29
+ end
30
+
31
+ def generate_contact_id(contact)
32
+ if !contact.name.nil?
33
+ contact.name.downcase.tr(" ", "_")
34
+ elsif !contact.email_addresses.empty?
35
+ contact.email_addresses.first
36
+ end
37
+ end
38
+
39
+ end
40
+
@@ -139,6 +139,13 @@ class Ppl::Application::Bootstrap
139
139
  rm
140
140
  end
141
141
 
142
+ register :command_scrape do
143
+ scrape = Ppl::Command::Scrape.new
144
+ scrape.storage = storage_adapter
145
+ scrape.email_scraper = email_scraper
146
+ scrape
147
+ end
148
+
142
149
  register :command_shell do
143
150
  shell = Ppl::Command::Shell.new
144
151
  shell.storage = storage_adapter
@@ -186,6 +193,7 @@ class Ppl::Application::Bootstrap
186
193
  suite.add_command command_push
187
194
  suite.add_command command_remote
188
195
  suite.add_command command_rm
196
+ suite.add_command command_scrape
189
197
  suite.add_command command_shell
190
198
  suite.add_command command_show
191
199
  suite.add_command command_url
@@ -337,5 +345,9 @@ class Ppl::Application::Bootstrap
337
345
  Ppl::Adapter::Vcard::GreenCard.new
338
346
  end
339
347
 
348
+ register :email_scraper do
349
+ Ppl::Adapter::EmailScraper::Mail.new
350
+ end
351
+
340
352
  end
341
353
 
@@ -4,11 +4,13 @@ class Ppl::Application::Input
4
4
  attr_accessor :arguments
5
5
  attr_accessor :options
6
6
  attr_accessor :stdin
7
+ attr_accessor :argf
7
8
 
8
9
  def initialize(arguments=[])
9
10
  @arguments = arguments
10
11
  @options = {}
11
12
  @stdin = $stdin
13
+ @argf = ARGF
12
14
  end
13
15
 
14
16
  end
@@ -0,0 +1,65 @@
1
+
2
+ require "readline"
3
+
4
+ class Ppl::Command::Scrape < Ppl::Application::Command
5
+
6
+ name "scrape"
7
+ description "Scrape contact details from stdin"
8
+
9
+ attr_writer :email_scraper
10
+
11
+ def options(parser, options)
12
+ parser.banner = "usage: ppl scrape [<options>]"
13
+ parser.on("-q", "--quiet", "Add contacts to the address book without prompting") do |i|
14
+ options[:quiet] = i
15
+ end
16
+ parser.on("-s", "--sender", "Scrape the sender's contact details") do |i|
17
+ options[:sender] = i
18
+ end
19
+ end
20
+
21
+ def execute(input, output)
22
+ contacts = scrape_email(input)
23
+ contacts.each do |contact|
24
+ if store_contact?(contact, input)
25
+ @storage.save_contact(contact)
26
+ end
27
+ end
28
+ return true
29
+ end
30
+
31
+ private
32
+
33
+ def scrape_email(input)
34
+ ARGV.shift
35
+ email = input.stdin.read
36
+ contacts = []
37
+ if input.options[:sender]
38
+ contacts |= scrape_sender(email)
39
+ end
40
+ contacts
41
+ end
42
+
43
+ def scrape_sender(email)
44
+ @email_scraper.scrape_contacts(email)
45
+ end
46
+
47
+ def store_contact?(contact, input)
48
+ if input.options[:quiet]
49
+ true
50
+ else
51
+ input.stdin.reopen("/dev/tty", "r") if input.stdin.eof?
52
+ message = generate_prompt_string(contact)
53
+ Readline.readline(message).downcase != "n"
54
+ end
55
+ end
56
+
57
+ def generate_prompt_string(contact)
58
+ sprintf('Add "%s <%s>" to your address book [Y/n]? ',
59
+ contact.name,
60
+ contact.email_addresses.first
61
+ )
62
+ end
63
+
64
+ end
65
+
data/ppl.gemspec CHANGED
@@ -2,8 +2,8 @@
2
2
  Gem::Specification.new do |spec|
3
3
 
4
4
  spec.name = "ppl"
5
- spec.version = "1.18.0"
6
- spec.date = "2013-04-16"
5
+ spec.version = "1.19.0"
6
+ spec.date = "2013-04-19"
7
7
 
8
8
  spec.required_ruby_version = ">= 1.9.3"
9
9
 
@@ -13,6 +13,7 @@ Gem::Specification.new do |spec|
13
13
 
14
14
  spec.add_dependency("colored", "1.2")
15
15
  spec.add_dependency("inifile", "2.0.2")
16
+ spec.add_dependency("mail", "2.5.3")
16
17
  spec.add_dependency("morphine", "0.1.1")
17
18
  spec.add_dependency("rugged", "0.17.0.b6")
18
19
  spec.add_dependency("greencard", "0.0.5")
@@ -0,0 +1,81 @@
1
+
2
+ describe Ppl::Adapter::EmailScraper::Mail do
3
+
4
+ before(:each) do
5
+ @adapter = Ppl::Adapter::EmailScraper::Mail.new
6
+ end
7
+
8
+ describe "#scrape_contacts" do
9
+
10
+ it "should return an array" do
11
+ @adapter.scrape_contacts("").should eq []
12
+ end
13
+
14
+ it "should scrape the sender's name" do
15
+ email = [
16
+ "Date: Fri, 30 Nov 2012 17:09:33 +0000",
17
+ "From: Test User <test@example.org>",
18
+ "Message-ID: <qwertyuioasdfghjk@mail.example.org>",
19
+ "Subject: Test Email",
20
+ "To: henry@henrysmith.org",
21
+ "",
22
+ "Hey,",
23
+ "This is a test email.",
24
+ "Bye!",
25
+ ].join("\n")
26
+ contacts = @adapter.scrape_contacts(email)
27
+ contacts.first.name.should eq "Test User"
28
+ end
29
+
30
+ it "should scrape the sender's email address" do
31
+ email = [
32
+ "Date: Fri, 30 Nov 2012 17:09:33 +0000",
33
+ "From: Test User <test@example.org>",
34
+ "Message-ID: <qwertyuioasdfghjk@mail.example.org>",
35
+ "Subject: Test Email",
36
+ "To: henry@henrysmith.org",
37
+ "",
38
+ "Hey,",
39
+ "This is a test email.",
40
+ "Bye!",
41
+ ].join("\n")
42
+ contacts = @adapter.scrape_contacts(email)
43
+ contacts.first.email_addresses.first.should eq "test@example.org"
44
+ end
45
+
46
+ it "should generate an ID for the sender based on their name" do
47
+ email = [
48
+ "Date: Fri, 30 Nov 2012 17:09:33 +0000",
49
+ "From: Test User <test@example.org>",
50
+ "Message-ID: <qwertyuioasdfghjk@mail.example.org>",
51
+ "Subject: Test Email",
52
+ "To: henry@henrysmith.org",
53
+ "",
54
+ "Hey,",
55
+ "This is a test email.",
56
+ "Bye!",
57
+ ].join("\n")
58
+ contacts = @adapter.scrape_contacts(email)
59
+ contacts.first.id.should eq "test_user"
60
+ end
61
+
62
+ it "should generate a sender ID based on email address if there's no name" do
63
+ email = [
64
+ "Date: Fri, 30 Nov 2012 17:09:33 +0000",
65
+ "From: test@example.org",
66
+ "Message-ID: <qwertyuioasdfghjk@mail.example.org>",
67
+ "Subject: Test Email",
68
+ "To: henry@henrysmith.org",
69
+ "",
70
+ "Hey,",
71
+ "This is a test email.",
72
+ "Bye!",
73
+ ].join("\n")
74
+ contacts = @adapter.scrape_contacts(email)
75
+ contacts.first.id.should eq "test@example.org"
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
@@ -0,0 +1,15 @@
1
+
2
+ describe Ppl::Adapter::EmailScraper do
3
+
4
+ before(:each) do
5
+ @adapter = Ppl::Adapter::EmailScraper.new
6
+ end
7
+
8
+ describe "#scrape_contacts" do
9
+ it "should raise not implemented error" do
10
+ expect{@adapter.scrape_contacts("")}.to raise_error(NotImplementedError)
11
+ end
12
+ end
13
+
14
+ end
15
+
@@ -127,6 +127,12 @@ describe Ppl::Application::Bootstrap do
127
127
  end
128
128
  end
129
129
 
130
+ describe "#command_scrape" do
131
+ it "should return a Ppl::Command::Scrape" do
132
+ @bootstrap.command_scrape.should be_a(Ppl::Command::Scrape)
133
+ end
134
+ end
135
+
130
136
  describe "#command_shell" do
131
137
  it "should return a Ppl::Command::Shell" do
132
138
  @bootstrap.command_shell.should be_a(Ppl::Command::Shell)
@@ -464,6 +470,9 @@ describe Ppl::Application::Bootstrap do
464
470
  it "should contain the 'rm' command" do
465
471
  @bootstrap.command_suite.find_command("rm").should_not be nil
466
472
  end
473
+ it "should contain the 'scrape' command" do
474
+ @bootstrap.command_suite.find_command("scrape").should_not be nil
475
+ end
467
476
  it "should contain the 'show' command" do
468
477
  @bootstrap.command_suite.find_command("show").should_not be nil
469
478
  end
@@ -0,0 +1,97 @@
1
+
2
+ describe Ppl::Command::Scrape do
3
+
4
+ before(:each) do
5
+ @command = Ppl::Command::Scrape.new
6
+ @input = Ppl::Application::Input.new
7
+ @input.stdin = double(IO)
8
+ @contact = Ppl::Entity::Contact.new
9
+ @output = double(Ppl::Application::Output)
10
+ @email_scraper = double(Ppl::Adapter::EmailScraper)
11
+ @storage = double(Ppl::Adapter::Storage)
12
+ @address_book = double(Ppl::Entity::AddressBook)
13
+ @command.storage = @storage
14
+ @command.email_scraper = @email_scraper
15
+ end
16
+
17
+ describe "#name" do
18
+ it "should be 'scrape'" do
19
+ @command.name.should eq "scrape"
20
+ end
21
+ end
22
+
23
+ describe "#execute" do
24
+
25
+ before(:each) do
26
+ @input.stdin.stub(:read)
27
+ @input.stdin.stub(:reopen)
28
+ @input.stdin.stub(:eof?)
29
+ @email_scraper.stub(:scrape_contacts).and_return([])
30
+ Readline.stub(:readline).and_return("")
31
+ @storage.stub(:save_contact)
32
+ end
33
+
34
+ it "should read input from stdin" do
35
+ @input.options[:sender] = true
36
+ @input.stdin.should_receive(:read)
37
+ @command.execute(@input, @output)
38
+ end
39
+
40
+ it "should pass input to the email scraper" do
41
+ @input.options[:sender] = true
42
+ @email_scraper.should_receive(:scrape_contacts)
43
+ @command.execute(@input, @output)
44
+ end
45
+
46
+ it "shouldn't do any scraping unless told which fields to scrape" do
47
+ @email_scraper.should_not_receive(:scrape_contacts)
48
+ @command.execute(@input, @output)
49
+ end
50
+
51
+ it "should always save contacts if in quiet mode" do
52
+ @input.options[:sender] = true
53
+ @input.options[:quiet] = true
54
+ @email_scraper.stub(:scrape_contacts).and_return([@contact])
55
+ Readline.should_not_receive(:readline)
56
+ @storage.should_receive(:save_contact)
57
+ @command.execute(@input, @output)
58
+ end
59
+
60
+ it "should reopen stdin to prompt the user" do
61
+ @input.options[:sender] = true
62
+ @input.stdin.should_receive(:eof?).and_return(true)
63
+ @input.stdin.should_receive(:reopen)
64
+ @email_scraper.stub(:scrape_contacts).and_return([@contact])
65
+ @command.execute(@input, @output)
66
+ end
67
+
68
+ it "should save the contact if the user approves" do
69
+ @input.options[:sender] = true
70
+ @email_scraper.stub(:scrape_contacts).and_return([@contact])
71
+ Readline.should_receive(:readline).and_return("y")
72
+ @storage.should_receive(:save_contact)
73
+ @command.execute(@input, @output)
74
+ end
75
+
76
+ it "should not save the contact if the user disapproves" do
77
+ @input.options[:sender] = true
78
+ @email_scraper.stub(:scrape_contacts).and_return([@contact])
79
+ Readline.should_receive(:readline).and_return("n")
80
+ @storage.should_not_receive(:save_contact)
81
+ @command.execute(@input, @output)
82
+ end
83
+
84
+ it "should prompt the user with the contact's name and email address" do
85
+ @input.options[:sender] = true
86
+ contact = Ppl::Entity::Contact.new
87
+ contact.name = "Test Person"
88
+ contact.email_addresses << "test@example.org"
89
+ @email_scraper.stub(:scrape_contacts).and_return([contact])
90
+ Readline.should_receive(:readline).with("Add \"Test Person <test@example.org>\" to your address book [Y/n]? ")
91
+ @command.execute(@input, @output)
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ppl
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.18.0
4
+ version: 1.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henry Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-16 00:00:00.000000000 Z
11
+ date: 2013-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colored
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
40
  version: 2.0.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: mail
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 2.5.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 2.5.3
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: morphine
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -159,6 +173,8 @@ files:
159
173
  - lib/ppl.rb
160
174
  - lib/ppl/adapter/color.rb
161
175
  - lib/ppl/adapter/color/colored.rb
176
+ - lib/ppl/adapter/email_scraper.rb
177
+ - lib/ppl/adapter/email_scraper/mail.rb
162
178
  - lib/ppl/adapter/storage.rb
163
179
  - lib/ppl/adapter/storage/disk.rb
164
180
  - lib/ppl/adapter/storage/factory.rb
@@ -191,6 +207,7 @@ files:
191
207
  - lib/ppl/command/phone.rb
192
208
  - lib/ppl/command/post.rb
193
209
  - lib/ppl/command/rm.rb
210
+ - lib/ppl/command/scrape.rb
194
211
  - lib/ppl/command/shell.rb
195
212
  - lib/ppl/command/show.rb
196
213
  - lib/ppl/command/url.rb
@@ -230,6 +247,8 @@ files:
230
247
  - ppl.gemspec
231
248
  - spec/ppl/adapter/color/colored_spec.rb
232
249
  - spec/ppl/adapter/color_spec.rb
250
+ - spec/ppl/adapter/email_scraper/mail_spec.rb
251
+ - spec/ppl/adapter/email_scraper_spec.rb
233
252
  - spec/ppl/adapter/output_spec.rb
234
253
  - spec/ppl/adapter/storage/disk_spec.rb
235
254
  - spec/ppl/adapter/storage/factory_spec.rb
@@ -262,6 +281,7 @@ files:
262
281
  - spec/ppl/command/phone_spec.rb
263
282
  - spec/ppl/command/post_spec.rb
264
283
  - spec/ppl/command/rm_spec.rb
284
+ - spec/ppl/command/scrape_spec.rb
265
285
  - spec/ppl/command/shell_spec.rb
266
286
  - spec/ppl/command/show_spec.rb
267
287
  - spec/ppl/command/url_spec.rb