ppl 0.0.5 → 0.2.0
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.
- 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
|
+
[](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
|
+
|