ppl 0.0.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +7 -3
- data/Gemfile.lock +15 -7
- data/README.md +47 -34
- data/bin/ppl +7 -6
- data/lib/ppl/adapter/storage/disk.rb +40 -0
- data/lib/ppl/adapter/storage.rb +28 -0
- data/lib/ppl/adapter/vcard/vpim.rb +45 -0
- data/lib/ppl/adapter/vcard.rb +13 -0
- data/lib/ppl/application/bootstrap.rb +62 -0
- data/lib/ppl/application/command.rb +13 -0
- data/lib/ppl/application/command_suite.rb +25 -0
- data/lib/ppl/application/input.rb +13 -0
- data/lib/ppl/application/output.rb +21 -0
- data/lib/ppl/application/router.rb +19 -0
- data/lib/ppl/application/shell.rb +34 -0
- data/lib/ppl/command/command_list.rb +21 -0
- data/lib/ppl/command/contact_add.rb +26 -0
- data/lib/ppl/command/contact_delete.rb +16 -0
- data/lib/ppl/command/contact_list.rb +24 -0
- data/lib/ppl/command/contact_rename.rb +23 -0
- data/lib/ppl/command/contact_show.rb +20 -0
- data/lib/ppl/entity/address_book.rb +21 -0
- data/lib/ppl/entity/contact.rb +10 -0
- data/lib/ppl/error/contact_not_found.rb +4 -0
- data/lib/ppl/error/incorrect_usage.rb +4 -0
- data/lib/ppl.rb +51 -11
- data/ppl.gemspec +3 -3
- data/spec/ppl/adapter/output_spec.rb +43 -0
- data/spec/ppl/adapter/storage/disk_spec.rb +83 -0
- data/spec/ppl/adapter/storage_spec.rb +46 -0
- data/spec/ppl/adapter/vcard/vpim_spec.rb +77 -0
- data/spec/ppl/adapter/vcard_spec.rb +24 -0
- data/spec/ppl/application/bootstrap_spec.rb +88 -0
- data/spec/ppl/application/command_spec.rb +19 -0
- data/spec/ppl/application/command_suite_spec.rb +44 -0
- data/spec/ppl/application/input_spec.rb +21 -0
- data/spec/ppl/application/router_spec.rb +43 -0
- data/spec/ppl/application/shell_spec.rb +79 -0
- data/spec/ppl/command/command_list_spec.rb +37 -0
- data/spec/ppl/command/contact_add_spec.rb +41 -0
- data/spec/ppl/command/contact_delete_spec.rb +31 -0
- data/spec/ppl/command/contact_list_spec.rb +15 -0
- data/spec/ppl/command/contact_rename_spec.rb +42 -0
- data/spec/ppl/command/contact_show_spec.rb +35 -0
- data/spec/ppl/entity/address_book_spec.rb +26 -0
- data/spec/ppl/entity/contact_spec.rb +34 -0
- data/spec/spec_helper.rb +9 -0
- metadata +47 -14
- data/lib/ppl/address_book.rb +0 -74
- data/lib/ppl/cli.rb +0 -49
- data/lib/ppl/command/add.rb +0 -40
- data/lib/ppl/command/birthdays.rb +0 -34
- data/lib/ppl/command/help.rb +0 -32
- data/lib/ppl/command/list.rb +0 -31
- data/lib/ppl/command/rm.rb +0 -38
- data/lib/ppl/command/set.rb +0 -49
- data/lib/ppl/command/show.rb +0 -43
- data/lib/ppl/command.rb +0 -55
- data/lib/ppl/contact.rb +0 -43
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,3 @@
|
|
1
|
-
GIT
|
2
|
-
remote: git://github.com/defunkt/colored.git
|
3
|
-
revision: 829bde0f8832406be1cacc5c99c49d976e05ccfc
|
4
|
-
specs:
|
5
|
-
colored (1.2)
|
6
|
-
|
7
1
|
GIT
|
8
2
|
remote: git://github.com/sam-github/vpim.git
|
9
3
|
revision: 6753a367cda64e53eab745414cedc130d2f149c8
|
@@ -11,11 +5,25 @@ GIT
|
|
11
5
|
vpim (12.1.12)
|
12
6
|
|
13
7
|
GEM
|
8
|
+
remote: http://rubygems.org/
|
14
9
|
specs:
|
10
|
+
diff-lcs (1.1.3)
|
11
|
+
fakefs (0.4.1)
|
12
|
+
rake (10.0.1)
|
13
|
+
rspec (2.12.0)
|
14
|
+
rspec-core (~> 2.12.0)
|
15
|
+
rspec-expectations (~> 2.12.0)
|
16
|
+
rspec-mocks (~> 2.12.0)
|
17
|
+
rspec-core (2.12.0)
|
18
|
+
rspec-expectations (2.12.0)
|
19
|
+
diff-lcs (~> 1.1.3)
|
20
|
+
rspec-mocks (2.12.0)
|
15
21
|
|
16
22
|
PLATFORMS
|
17
23
|
ruby
|
18
24
|
|
19
25
|
DEPENDENCIES
|
20
|
-
|
26
|
+
fakefs
|
27
|
+
rake
|
28
|
+
rspec
|
21
29
|
vpim (= 12.1.12)!
|
data/README.md
CHANGED
@@ -11,49 +11,50 @@ like "people". You might be interested in ppl if:
|
|
11
11
|
* You want ownership of your address book data back from the cloud
|
12
12
|
* You prefer to keep your data stored in an open format
|
13
13
|
|
14
|
+
ppl is in an incomplete state right now, and is in active development. Stay
|
15
|
+
tuned.
|
14
16
|
|
15
|
-
|
16
|
-
-----
|
17
|
+
[![Build Status](https://secure.travis-ci.org/h2s/ppl.png)](http://travis-ci.org/h2s/ppl)
|
17
18
|
|
18
|
-
|
19
|
+
Installation
|
20
|
+
------------
|
19
21
|
|
20
22
|
```bash
|
21
|
-
$
|
23
|
+
$ sudo gem install ppl
|
22
24
|
```
|
23
25
|
|
26
|
+
Usage
|
27
|
+
-----
|
28
|
+
|
24
29
|
### List all contacts
|
25
30
|
|
26
31
|
```bash
|
27
|
-
$ ppl
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
$ ppl ls
|
33
|
+
dave: dave@example.org
|
34
|
+
fred: fred@example.org
|
35
|
+
john: john@example.org
|
31
36
|
```
|
32
37
|
|
33
|
-
###
|
38
|
+
### Add a contact
|
39
|
+
```bash
|
40
|
+
$ ppl add paul "Paul Baker"
|
41
|
+
```
|
34
42
|
|
43
|
+
### Show a contact
|
35
44
|
```bash
|
36
45
|
$ ppl show fred
|
37
46
|
Fred Smith
|
38
|
-
fred
|
39
|
-
|
40
|
-
Birthday 1980-01-01
|
41
|
-
Cellphone 01189998819991197253
|
42
|
-
````
|
43
|
-
|
44
|
-
### List upcoming birthdays
|
45
|
-
|
46
|
-
```bash
|
47
|
-
$ ppl birthdays
|
48
|
-
fred Fred Smith 1980-01-01 99 days
|
49
|
-
dave Dave Jones 1980-01-02 100 days
|
50
|
-
joe Joe Bloggs 1980-01-03 101 days
|
47
|
+
fred@example.org
|
51
48
|
```
|
52
49
|
|
53
50
|
### Delete a contact
|
51
|
+
```bash
|
52
|
+
$ ppl rm dave
|
53
|
+
```
|
54
54
|
|
55
|
+
### Rename a contact
|
55
56
|
```bash
|
56
|
-
$ ppl
|
57
|
+
$ ppl mv dave david
|
57
58
|
```
|
58
59
|
|
59
60
|
Roadmap
|
@@ -61,22 +62,34 @@ Roadmap
|
|
61
62
|
|
62
63
|
Support for the following commands is planned.
|
63
64
|
|
64
|
-
###
|
65
|
+
### Change a contact's email address
|
65
66
|
```bash
|
66
|
-
$ ppl
|
67
|
+
$ ppl email dave david@example.org
|
67
68
|
```
|
68
69
|
|
69
|
-
###
|
70
|
+
### Change a contact's name
|
70
71
|
```bash
|
71
|
-
$ ppl
|
72
|
+
$ ppl name john "John Smith"
|
72
73
|
```
|
73
74
|
|
74
|
-
|
75
|
-
Any commands that modify the address book will also commit their changes.
|
76
|
-
Then this will be posisble:
|
75
|
+
### Change a contact's birthday
|
77
76
|
```bash
|
78
|
-
$ ppl
|
79
|
-
Pulling latest changes from all remotes...
|
80
|
-
Pushing local changes to all remotes...
|
81
|
-
Done
|
77
|
+
$ ppl birthday john 1980-01-01
|
82
78
|
```
|
79
|
+
|
80
|
+
Contributing
|
81
|
+
------------
|
82
|
+
|
83
|
+
Bug reports, fixes, and additional features are encouraged. The project uses
|
84
|
+
[Github issues](https://github.com/h2s/ppl/issues) to track bug reports.
|
85
|
+
|
86
|
+
If you'd like to contribute code, please just bear in mind that ppl development
|
87
|
+
is test-driven and attempts to follow the "Object Mentor school of clean code"
|
88
|
+
as described in the book [Clean
|
89
|
+
Code](http://books.google.co.uk/books?id=_i6bDeoCQzsC).
|
90
|
+
|
91
|
+
License
|
92
|
+
-------
|
93
|
+
|
94
|
+
ppl is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.
|
95
|
+
|
data/bin/ppl
CHANGED
@@ -5,12 +5,13 @@ require "pathname"
|
|
5
5
|
bin_file = Pathname.new(__FILE__).realpath
|
6
6
|
$:.unshift File.expand_path("../../lib", bin_file)
|
7
7
|
|
8
|
-
require "ppl
|
8
|
+
require "ppl"
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
bootstrap = Ppl::Application::Bootstrap.new
|
11
|
+
input = bootstrap.input
|
12
|
+
output = bootstrap.output
|
13
|
+
shell = bootstrap.shell
|
14
|
+
|
15
|
+
exit(shell.run(input, output))
|
13
16
|
|
14
|
-
cli = Ppl::CLI.new
|
15
|
-
cli.run ARGV
|
16
17
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
class Ppl::Adapter::Storage::Disk < Ppl::Adapter::Storage
|
3
|
+
|
4
|
+
attr_accessor :path
|
5
|
+
attr_accessor :vcard_adapter
|
6
|
+
|
7
|
+
def initialize(path)
|
8
|
+
@path = path
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_address_book
|
12
|
+
address_book = Ppl::Entity::AddressBook.new
|
13
|
+
|
14
|
+
pattern = File.join @path, "*.vcf"
|
15
|
+
filenames = Dir.glob pattern
|
16
|
+
|
17
|
+
filenames.each do |filename|
|
18
|
+
contact_id = File.basename(filename).slice(0..-5)
|
19
|
+
contact = load_contact(contact_id)
|
20
|
+
address_book.add_contact(contact)
|
21
|
+
end
|
22
|
+
|
23
|
+
return address_book
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_contact(id)
|
27
|
+
filename = File.join @path, id + ".vcf"
|
28
|
+
contact = nil
|
29
|
+
if File.exists?(filename)
|
30
|
+
vcard = File.read filename
|
31
|
+
contact = @vcard_adapter.decode(vcard)
|
32
|
+
if !contact.nil? && contact.is_a?(Ppl::Entity::Contact)
|
33
|
+
contact.id = id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
return contact
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
class Ppl::Adapter::Storage
|
3
|
+
|
4
|
+
def delete_contact(contact)
|
5
|
+
raise NotImplementedError
|
6
|
+
end
|
7
|
+
|
8
|
+
def load_address_book
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_contact(id)
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
|
16
|
+
def require_contact(id)
|
17
|
+
contact = load_contact(id)
|
18
|
+
if contact.nil?
|
19
|
+
raise Ppl::Error::ContactNotFound, id
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def save_contact(contact)
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
require "vpim/vcard"
|
3
|
+
|
4
|
+
class Ppl::Adapter::Vcard::Vpim
|
5
|
+
|
6
|
+
def encode(contact)
|
7
|
+
vcard = Vpim::Vcard::Maker.make2 do |maker|
|
8
|
+
|
9
|
+
if !contact.birthday.nil?
|
10
|
+
maker.birthday = contact.birthday
|
11
|
+
end
|
12
|
+
|
13
|
+
maker.add_name do |name|
|
14
|
+
name.given = contact.id unless contact.id.nil?
|
15
|
+
name.fullname = contact.name unless contact.name.nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
return vcard.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def decode(string)
|
23
|
+
vcard = Vpim::Vcard.decode(string).first
|
24
|
+
contact = Ppl::Entity::Contact.new
|
25
|
+
|
26
|
+
if !vcard.birthday.nil?
|
27
|
+
contact.birthday = vcard.birthday
|
28
|
+
end
|
29
|
+
|
30
|
+
vcard.emails.each do |email|
|
31
|
+
contact.email_address = email.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
name = nil
|
35
|
+
name = vcard.name
|
36
|
+
|
37
|
+
if !name.nil?
|
38
|
+
contact.name = name.fullname
|
39
|
+
end
|
40
|
+
|
41
|
+
return contact
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
class Ppl::Application::Bootstrap
|
3
|
+
|
4
|
+
def commands
|
5
|
+
commands = [
|
6
|
+
Ppl::Command::CommandList.new,
|
7
|
+
Ppl::Command::ContactAdd.new,
|
8
|
+
Ppl::Command::ContactDelete.new,
|
9
|
+
Ppl::Command::ContactList.new,
|
10
|
+
Ppl::Command::ContactRename.new,
|
11
|
+
Ppl::Command::ContactShow.new,
|
12
|
+
]
|
13
|
+
commands.each do |command|
|
14
|
+
command.storage = storage_adapter
|
15
|
+
end
|
16
|
+
return commands
|
17
|
+
end
|
18
|
+
|
19
|
+
def command_suite
|
20
|
+
suite = Ppl::Application::CommandSuite.new
|
21
|
+
commands.each do |command|
|
22
|
+
suite.add_command(command)
|
23
|
+
end
|
24
|
+
suite.find_command("help").command_suite = suite
|
25
|
+
return suite
|
26
|
+
end
|
27
|
+
|
28
|
+
def input
|
29
|
+
input = Ppl::Application::Input.new(ARGV.dup)
|
30
|
+
return input
|
31
|
+
end
|
32
|
+
|
33
|
+
def output
|
34
|
+
output = Ppl::Application::Output.new($stdout, $stderr)
|
35
|
+
return output
|
36
|
+
end
|
37
|
+
|
38
|
+
def router
|
39
|
+
router = Ppl::Application::Router.new(command_suite)
|
40
|
+
router.default = "help"
|
41
|
+
return router
|
42
|
+
end
|
43
|
+
|
44
|
+
def shell
|
45
|
+
shell = Ppl::Application::Shell.new
|
46
|
+
shell.router = router
|
47
|
+
return shell
|
48
|
+
end
|
49
|
+
|
50
|
+
def storage_adapter
|
51
|
+
storage = Ppl::Adapter::Storage::Disk.new(Dir.pwd)
|
52
|
+
storage.vcard_adapter = vcard_adapter
|
53
|
+
return storage
|
54
|
+
end
|
55
|
+
|
56
|
+
def vcard_adapter
|
57
|
+
vcard = Ppl::Adapter::Vcard::Vpim.new
|
58
|
+
return vcard
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
require "enumerator"
|
3
|
+
|
4
|
+
class Ppl::Application::CommandSuite
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@commands = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_command(command)
|
13
|
+
@commands.push command
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
@commands.each { |command| yield command }
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_command(name)
|
21
|
+
@commands.select { |command| command.name == name }.first
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
class Ppl::Application::Output
|
3
|
+
|
4
|
+
attr_accessor :stdout
|
5
|
+
attr_accessor :stderr
|
6
|
+
|
7
|
+
def initialize(stdout, stderr)
|
8
|
+
@stdout = stdout
|
9
|
+
@stderr = stderr
|
10
|
+
end
|
11
|
+
|
12
|
+
def error(string)
|
13
|
+
@stderr.puts string
|
14
|
+
end
|
15
|
+
|
16
|
+
def line(string)
|
17
|
+
@stdout.puts string
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
class Ppl::Application::Router
|
3
|
+
|
4
|
+
attr_accessor :default
|
5
|
+
|
6
|
+
def initialize(command_suite)
|
7
|
+
@command_suite = command_suite
|
8
|
+
end
|
9
|
+
|
10
|
+
def route(argument)
|
11
|
+
command = @command_suite.find_command(argument)
|
12
|
+
if command.nil? && !@default.nil?
|
13
|
+
command = @command_suite.find_command(@default)
|
14
|
+
end
|
15
|
+
return command
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
class Ppl::Application::Shell
|
3
|
+
|
4
|
+
attr_writer :router
|
5
|
+
|
6
|
+
def run(input, output)
|
7
|
+
outcome = false
|
8
|
+
begin
|
9
|
+
command = select_command(input)
|
10
|
+
outcome = execute_command(command, input, output)
|
11
|
+
rescue
|
12
|
+
output.error("ppl: " + $!.message)
|
13
|
+
outcome = false
|
14
|
+
end
|
15
|
+
return outcome
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def select_command(input)
|
22
|
+
@router.route(input.arguments.shift)
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute_command(command, input, output)
|
26
|
+
outcome = false
|
27
|
+
if !command.nil?
|
28
|
+
outcome = command.execute(input, output)
|
29
|
+
end
|
30
|
+
return outcome
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
class Ppl::Command::CommandList < Ppl::Application::Command
|
3
|
+
|
4
|
+
attr_accessor :command_suite
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@name = "help"
|
8
|
+
@description = "Show a list of commands"
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute(input, output)
|
12
|
+
@command_suite.each do |command|
|
13
|
+
name = command.name
|
14
|
+
description = command.description
|
15
|
+
output.line("#{name}: #{description}")
|
16
|
+
end
|
17
|
+
return true
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
class Ppl::Command::ContactAdd < Ppl::Application::Command
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@name = "add"
|
6
|
+
@description = "Add a new contact"
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute(input, output)
|
10
|
+
contact_id = input.arguments.shift
|
11
|
+
contact_name = input.arguments.shift
|
12
|
+
|
13
|
+
if contact_id.nil? || contact_name.nil?
|
14
|
+
raise Ppl::Error::IncorrectUsage
|
15
|
+
end
|
16
|
+
|
17
|
+
contact = Ppl::Entity::Contact.new
|
18
|
+
contact.id = contact_id
|
19
|
+
contact.name = contact_name
|
20
|
+
|
21
|
+
@storage.save_contact(contact)
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
class Ppl::Command::ContactDelete < Ppl::Application::Command
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@name = "rm"
|
6
|
+
@description = "Delete a contact"
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute(input, output)
|
10
|
+
contact_id = input.arguments.first
|
11
|
+
contact = @storage.require_contact(contact_id)
|
12
|
+
@storage.delete_contact(contact)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
class Ppl::Command::ContactList < Ppl::Application::Command
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@name = "ls"
|
6
|
+
@description = "List all contacts"
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute(input, output)
|
10
|
+
address_book = @storage.load_address_book
|
11
|
+
|
12
|
+
address_book.each do |contact|
|
13
|
+
line = sprintf("%s: %s",
|
14
|
+
contact.id,
|
15
|
+
contact.email_address
|
16
|
+
)
|
17
|
+
output.line(line)
|
18
|
+
end
|
19
|
+
|
20
|
+
return true
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
class Ppl::Command::ContactRename < Ppl::Application::Command
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@name = "mv"
|
6
|
+
@description = "Rename a contact"
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute(input, output)
|
10
|
+
old_id = input.arguments.shift
|
11
|
+
new_id = input.arguments.shift
|
12
|
+
contact = @storage.require_contact(old_id)
|
13
|
+
|
14
|
+
@storage.delete_contact(contact)
|
15
|
+
|
16
|
+
contact.id = new_id
|
17
|
+
@storage.save_contact(contact)
|
18
|
+
|
19
|
+
return true
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
class Ppl::Command::ContactShow < Ppl::Application::Command
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@name = "show"
|
6
|
+
@description = "Display the full details of a contact"
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute(input, output)
|
10
|
+
contact_id = input.arguments.shift
|
11
|
+
contact = @storage.require_contact(contact_id)
|
12
|
+
|
13
|
+
output.line(contact.name)
|
14
|
+
output.line(contact.email_address)
|
15
|
+
|
16
|
+
return true
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
require "enumerator"
|
3
|
+
|
4
|
+
class Ppl::Entity::AddressBook
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@contacts = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_contact(contact)
|
13
|
+
@contacts.push contact
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
@contacts.each { |contact| yield contact }
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|