syc-ontact 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fe93582cead2b2ccc48d60fcaa54a8d602baec10
4
+ data.tar.gz: 501cacb2bc96c5fb42e65226012da13859a0ce97
5
+ SHA512:
6
+ metadata.gz: 4fb3c6fb9ed8d190de1a2f8e4525d99b5573b633abd5d4fe88c2ad44ebeda43036c587aa1c249fd7e964b14b021acd01cb9dec4ad42f15fd31b16f43ec21aa80
7
+ data.tar.gz: 1409a116127b05967202549f873093b370994d20d297b97d5041c5dfc4dd1a764a6e674f896619a5ab1488230c6f013a807da37005b691a5a4050f9ab6b95221
@@ -0,0 +1,23 @@
1
+ *syc-ontact* is published under the [MIT-License](http://opensource.org/licenses/MIT)
2
+
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2014 Sugar Your Coffee
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
@@ -0,0 +1,190 @@
1
+ sycontact
2
+ =========
3
+ `syc-ontact` is a command line interface for looking up contacts from any source that is providing contact information.
4
+
5
+ Installation
6
+ ============
7
+ `gem install sycontact`
8
+
9
+ Setup
10
+ =====
11
+ To use `sycontact` a source-file has to be provided. The source-file is Ruby script retrieving the data from the source. A source can be anything that can be read from the Ruby script, e.g. a web site, an LDAP server, a file with contact data.
12
+
13
+ **Note:** Without a user defined Ruby script file (a Ruby module) `sycontact` will do nothing but
14
+ creating a configuration file, a source directory and displaying the help page.
15
+
16
+ To get `sycontact` to life you have to follow these setup steps:
17
+
18
+ 1. start `sycontact` once. This will create the configuration file and the working directory
19
+ 2. provide a Ruby module describing how to retrieve the data from the source. The module name has
20
+ to be `some_name_source.rb`.
21
+
22
+ The Ruby source code below describes a source-file that is retrieving contact data from a contact directory with the name *test-contacts* below the module's directory.
23
+
24
+
25
+ ```
26
+ module AddressSource
27
+
28
+ # Where to find the contact files
29
+ URL = File.join(File.dirname(__FILE__), "test-contacts")
30
+
31
+ # Regex to extract contact data from the contact files
32
+ REGEX = { cn: /(?<=<common_name>)[\w -]*(?=<\common_name>)/,
33
+ sn: /(?<=<surname>)[\w -]*(?=<\/surname>)/,
34
+ gn: /(?<=<given_name>)[\w -]*(?=<\/given_name>)/,
35
+ c: /(?<=<country>)[\w]*(?=<\/country>)/,
36
+ l: /(?<=<location>)[\w]*(?=<\/location>)/,
37
+ st: /(?<=<state>)[\w]*(?=<\/state>)/,
38
+ street: /(?<=<street>)[\w .]*(?=<\/street>)/,
39
+ o: /(?<=<organization>)[\w -]*(?=<\/organization>)/,
40
+ ou: /(?<=<department>)[\w -]*(?=<\/department>)/,
41
+ title: /(?<=<title>)[\w .-]*(?=<\/title>)/,
42
+ description: /(?<=<description>)[\w -+]*(?=<\/description>)/,
43
+ telephone: /(?<=<telephone>)[\w +()-]*(?=<\/telephone>)/,
44
+ mobile: /(?<=<mobile>)[\w +()-]*(?=<\/mobile>)/,
45
+ mail: /(?<=<email>)[\w @.-]*(?=<\/email>)/
46
+ }
47
+
48
+ # Mandatory method! Will be invoked by `sycontact`.
49
+ # Will lookup the contact based on the pattern provided
50
+ def lookup(pattern = {})
51
+ contacts = []
52
+ create_source_files(pattern).each do |source_file|
53
+
54
+ next unless File.exist? source_file
55
+
56
+ source = File.read(source_file)
57
+
58
+ next if source.empty?
59
+
60
+ values = {}
61
+
62
+ REGEX.each do |key, regex|
63
+ value = source.scan(regex)[0]
64
+ values[key] = value if value
65
+ end
66
+
67
+ contacts << values
68
+ end
69
+
70
+ contacts.keep_if do |contact|
71
+ pattern.each.reduce(true) do |match, pattern|
72
+ contact_does_not_have_key = contact[pattern[0]].nil?
73
+ regex = Regexp.new(pattern[1].strip.downcase)
74
+ pos = regex =~ contact[pattern[0]].strip.downcase unless contact_does_not_have_key
75
+ match and (not pos.nil? or contact_does_not_have_key)
76
+ end
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ # Creates the contact file name. In this case the contact files have to be in the form
83
+ # firstname_lastname.contact or lastname_firstname.contact. If neather is given all *_*.contact
84
+ # files are retrieved
85
+ def create_source_files(pattern)
86
+ source_files = []
87
+ if pattern[:cn]
88
+ names = pattern[:cn].scan(/(^[a-zA-Z]*)[^a-zA-Z]*([a-zA-Z]*)/).flatten
89
+ names[0] = '*' if names[0].empty?
90
+ names[1] = '*' if names[1].empty?
91
+ names.permutation do |names|
92
+ file = File.join(URL, "#{names.join('_').downcase}.contact")
93
+ Dir.glob(file).each { |file| source_files << file }
94
+ end
95
+ elsif pattern[:sn] or pattern[:gn]
96
+ sn = pattern[:sn] ? pattern[:sn].strip.downcase : '*'
97
+ gn = pattern[:gn] ? pattern[:gn].strip.downcase : '*'
98
+ Dir.glob(File.join(URL, "#{gn}_#{sn}.contact")).each do |file|
99
+ source_files << file
100
+ end
101
+ else
102
+ Dir.glob(File.join(URL, "*_*.contact")).each do |file|
103
+ source_files << file
104
+ end
105
+ end
106
+ source_files
107
+ end
108
+ end
109
+ ```
110
+
111
+ **Listing 1: Source-file to provide information about how to retrieve contact information**
112
+
113
+ Below a contact file that can be read by the above source module.
114
+
115
+ ```
116
+ <common_name>Sugar Pierre</common_name>
117
+ <given_name>Pierre</given_name>
118
+ <surname>Sugar</surname>
119
+ <country>CA</country>
120
+ <location>Vancouver</location>
121
+ <state>BC</state>
122
+ <street>Robson Street</street>
123
+ <organization>SugarYourCoffee</organization>
124
+ <department>DevOps</department>
125
+ <telephone>+001 (123) 4567</telephone>
126
+ <mobile>+001 (765) 4321</mobile>
127
+ <email>pierre@sugaryourcoffee.de</email>
128
+ ```
129
+
130
+ **Listing 2: Contact file that can be read by the `AddressSource` module**
131
+
132
+ Usage
133
+ =====
134
+
135
+ Get help for sycontact
136
+
137
+ $ sycontact -h
138
+
139
+ ```
140
+ Usage: sycontact [options]
141
+ -p, --print RAW|SUMMARY|ALL Print contact attributes
142
+ SUMMARY (default)
143
+ --cn COMMON_NAME Common name e.g. 'Jane Doe' or 'Doe, Jane'
144
+ --sn SURNAME Surname e.g. 'Doe'
145
+ --gn GIVEN_NAME Given name e.g. 'Jane'
146
+ --uid USER_ID User ID
147
+ -c COUNTRY Country in ISO 3166 e.g. 'CA' for Canada
148
+ -l LOCATION City e.g. 'Vancouver'
149
+ --st STATE State e.g. 'British Columbia'
150
+ --street STREET Street e.g. 'Robson Street'
151
+ -o ORGANIZATION Organization e.g. 'Northstar'
152
+ --ou ORGANIZATIONAL_UNIT Department e.g. 'R&D'
153
+ --title TITLE Title e.g. 'Dr.'
154
+ --description DESCRIPTION Description e.g. 'Head of R&D'
155
+ --telephone TELEPHONE Telephone number e.g. '+001 (252) 4354'
156
+ --mobile MOBILE_PHONE Mobile number e.g. '+001 (252) 4345'
157
+ --mail E-MAIL E-Mail address e.g. 'jane@northstart.ca'
158
+ -h, --help Show his message
159
+ ```
160
+
161
+ Lookup a contact with summary output
162
+
163
+ $ sycontact --cn sugar
164
+ $ sycontact --cn "sugar, pierre"
165
+ $ sycontact --cn "pierre sugar"
166
+
167
+ Any of the above commands result in the following output
168
+
169
+ ```
170
+ AddressSource
171
+
172
+ CN..................Sugar Pierre
173
+ C...................DE
174
+ L...................Vancouver
175
+ O...................SugarYourCoffee
176
+ OU..................DevOps
177
+ TELEPHONE...........+001 (123) 4567
178
+ MOBILE..............+001 (765) 4321
179
+ MAIL................pierre@sugaryourcoffee.de
180
+ ```
181
+ Sources
182
+ =======
183
+ Home page: <http://syc.dyndns.org/drupal/wiki/sycontact-lookup-contacts-any-source>
184
+
185
+ Source: <https://github.com/sugaryourcoffee/syc-ontact>
186
+
187
+ Contact
188
+ =======
189
+
190
+ <pierre@sugaryourcoffee.de>
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Main program of sycontact
4
+
5
+ require 'optparse'
6
+ require 'yaml'
7
+ require_relative '../lib/sycontact/address_book_library'
8
+
9
+ # Directory holding the confifuration file and the sources
10
+ sycontact_directory = File.expand_path("~/.syc/sycontact")
11
+ # sycontact configuration file
12
+ sycontact_file = File.join(sycontact_directory, "sycontact.rc")
13
+ # Directory where address books are saved to, that is the modules holding the script to retrieve
14
+ # contacts from the contact source file (e.g. Internet, LDAP server, file)
15
+ address_books_directory = File.join(sycontact_directory, "address_books/")
16
+
17
+ unless File.exist? sycontact_directory
18
+ Dir.mkdir sycontact_directory
19
+ end
20
+
21
+ unless File.exists? address_books_directory
22
+ Dir.mkdir address_books_directory
23
+ end
24
+
25
+ unless File.exists? sycontact_file
26
+ puts "\nsycontact"
27
+ puts "========="
28
+ puts "You have to add the address source file directory to #{sycontact_file}"
29
+ puts "Look at the README.md to see how to configure sycontact\n"
30
+ config = { address_books: [ ] }
31
+ File.open(sycontact_file, 'w') { |f| YAML.dump(config, f) }
32
+ ARGV << "-h"
33
+ else
34
+ config = YAML.load_file(sycontact_file)
35
+ address_books = config[:address_books]
36
+ if address_books.empty?
37
+ puts "\nsycontact"
38
+ puts "========="
39
+ puts "You have to add the address source file directory to #{sycontact_file}\n\n"
40
+ ARGV << "-h"
41
+ end
42
+ end
43
+
44
+ options = { p: "summary" }
45
+
46
+ option_parser = OptionParser.new do |opts|
47
+
48
+ opts.on("-p", "--print RAW|SUMMARY|ALL", "Print contact attributes",
49
+ "SUMMARY (default)") do |print|
50
+ options[:p] = print
51
+ end
52
+
53
+ opts.on("--cn COMMON_NAME", "Common name e.g. 'Jane Doe' or 'Doe, Jane'") do |common_name|
54
+ options[:cn] = common_name
55
+ end
56
+
57
+ opts.on("--sn SURNAME", "Surname e.g. 'Doe'") do |surname|
58
+ options[:sn] = surname
59
+ end
60
+
61
+ opts.on("--gn GIVEN_NAME", "Given name e.g. 'Jane'") do |given_name|
62
+ options[:gn] = given_name
63
+ end
64
+
65
+ opts.on("--uid USER_ID", "User ID") do |user_id|
66
+ options[:uid] = user_id
67
+ end
68
+
69
+ opts.on("-c COUNTRY", "Country in ISO 3166 e.g. 'CA' for Canada") do |country|
70
+ options[:c] = country
71
+ end
72
+
73
+ opts.on("-l LOCATION", "City e.g. 'Vancouver'") do |location|
74
+ options[:l] = location
75
+ end
76
+
77
+ opts.on("--st STATE", "State e.g. 'British Columbia'") do |state|
78
+ options[:st] = state
79
+ end
80
+
81
+ opts.on("--street STREET", "Street e.g. 'Robson Street'") do |street|
82
+ options[:street] = street
83
+ end
84
+
85
+ opts.on("-o ORGANIZATION", "Organization e.g. 'Northstar'") do |organization|
86
+ options[:o] = organization
87
+ end
88
+
89
+ opts.on("--ou ORGANIZATIONAL_UNIT", "Department e.g. 'R&D'") do |organizational_unit|
90
+ options[:ou] = organizational_unit
91
+ end
92
+
93
+ opts.on("--title TITLE", "Title e.g. 'Dr.'") do |title|
94
+ options[:title] = title
95
+ end
96
+
97
+ opts.on("--description DESCRIPTION", "Description e.g. 'Head of R&D'") do |description|
98
+ options[:descripton] = description
99
+ end
100
+
101
+ opts.on("--telephone TELEPHONE", "Telephone number e.g. '+001 (252) 4354'") do |telephone|
102
+ options[:telephone] = telephone
103
+ end
104
+
105
+ opts.on("--mobile MOBILE_PHONE", "Mobile number e.g. '+001 (252) 4345'") do |mobile_phone|
106
+ options[:mobile] = mobile_phone
107
+ end
108
+
109
+ opts.on("--mail E-MAIL", "E-Mail address e.g. 'jane@northstart.ca'") do |email|
110
+ options[:mail] = email
111
+ end
112
+
113
+ opts.on("-h", "--help", "Show his message") do
114
+ puts opts
115
+ exit(0)
116
+ end
117
+ end
118
+
119
+ begin
120
+ option_parser.parse!
121
+ rescue OptionParser::ParseError => e
122
+ STDERR.puts e.message, "\n", options
123
+ exit(1)
124
+ end
125
+
126
+ library = Sycontact::AddressBookLibrary.new(address_books[0])
127
+
128
+ case options[:p].downcase
129
+ when "raw"
130
+ options.delete(:p)
131
+ library.lookup(options).each do |c|
132
+ puts c
133
+ end
134
+ when "all"
135
+ options.delete(:p)
136
+ library.print_all(options)
137
+ else
138
+ options.delete(:p)
139
+ library.print_summary(options)
140
+ end
@@ -0,0 +1,64 @@
1
+ # sycontac module providing functions to lookup contacts
2
+ module Sycontact
3
+
4
+ # AddressBook is a wrapper for source modules that contain a script for retrieving contact data
5
+ # from a source as Internet, LDAP server or a file
6
+ class AddressBook
7
+
8
+ # Holds the values that are used when printing the summary of a contact
9
+ SUMMARY = [ :cn, :c, :l, :o, :ou, :telephone, :mobile, :mail ]
10
+
11
+ # Creates a new AddressBook. It requires the source module file and extends AddressBook with
12
+ # the source module
13
+ def initialize(source)
14
+ source = source.sub('.rb', '')
15
+ require source
16
+ @module_name = pascalize(File.basename(source))
17
+ extend self.class.module_eval(@module_name)
18
+ end
19
+
20
+ # The source module can override the address source title, print_summary and print_all methods.
21
+ # If the methods are not overridden the default methods are invoked. The source module has to
22
+ # provide a lookup method. If the lookup method is not available "method missing" will be
23
+ # thrown
24
+ def method_missing(method, *args, &block)
25
+ case method
26
+ when :title
27
+ @module_name
28
+ when :print_summary
29
+ args.each do |contact|
30
+ puts "\n"
31
+ contact.each do |k,v|
32
+ if block_given?
33
+ yield(k, v)
34
+ else
35
+ unless SUMMARY.index(k).nil?
36
+ puts "#{k.to_s.upcase.ljust(20, '.')}#{v}\n" unless v.nil? or v.empty?
37
+ end
38
+ end
39
+ end
40
+ end
41
+ when :print_all
42
+ args.each do |contact|
43
+ puts "\n"
44
+ contact.each do |k,v|
45
+ if block_given?
46
+ yield(k, v)
47
+ else
48
+ puts "#{k.to_s.upcase.ljust(20, '.')}#{v}\n" unless v.nil? or v.empty?
49
+ end
50
+ end
51
+ end
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ # Pascalizes/camalizes a string as address_source to AddressSource
60
+ def pascalize(string)
61
+ (string.split('_').map { |word| word.capitalize }).join
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,57 @@
1
+ require_relative 'address_book'
2
+
3
+ # sycontac module providing functions to lookup contacts
4
+ module Sycontact
5
+
6
+ # AddressBookLibrary creates AddressBook objects and forwards all messages invoked on
7
+ # AddressBookLibrary to all AddressBooks.
8
+ class AddressBookLibrary
9
+
10
+ # The contacts from the last lookup invocation
11
+ attr_reader :contacts
12
+
13
+ # Creates AddressBook objects based on the address book source files contained in the
14
+ # address_book_directory
15
+ def initialize(address_book_directory)
16
+ @address_books = []
17
+ Dir.glob(File.join(address_book_directory, "*_source.rb")).each do |address_book|
18
+ @address_books << AddressBook.new(address_book)
19
+ end
20
+ end
21
+
22
+ # Looks up a contact based on the pattern and returns the contact data as a hash. The contact
23
+ # data can subsequentially retrievied with :contacts
24
+ def lookup(pattern = {})
25
+ @contacts = {}
26
+ @address_books.each do |address_book|
27
+ puts "\n#{address_book.title}\n"
28
+ @contacts[address_book.title] = address_book.lookup(pattern)
29
+ end
30
+ @contacts
31
+ end
32
+
33
+ # Invokes a lookup on all AddressBook objects and prints the result to the console with all
34
+ # attributes found in the contact source
35
+ def print_all(pattern = {})
36
+ @address_books.each do |address_book|
37
+ puts "\n#{address_book.title}"
38
+ address_book.lookup(pattern).each do |contact|
39
+ address_book.print_all(contact)
40
+ end
41
+ end
42
+ end
43
+
44
+ # Invokes a lookup on all AddressBook objects and prints a subset of the result to the console.
45
+ # The attributes that are selected for print are defined in AddressBook::SUMMARY
46
+ def print_summary(pattern = {})
47
+ @address_books.each do |address_book|
48
+ puts "\n#{address_book.title}"
49
+ address_book.lookup(pattern).each do |contact|
50
+ address_book.print_summary(contact)
51
+ end
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -0,0 +1,19 @@
1
+ require 'sycontact/address_book_library'
2
+
3
+ # sycontac module providing functions to lookup contacts
4
+ module Sycontact
5
+
6
+ describe AddressBookLibrary do
7
+
8
+ before do
9
+ address_book_directory = File.join(File.dirname(__FILE__), "files")
10
+ @library = AddressBookLibrary.new(address_book_directory)
11
+ end
12
+
13
+ it "should look up contact" do
14
+ @library.lookup(sn: "sugar").should_not be_empty
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,107 @@
1
+ require 'sycontact/address_book'
2
+
3
+ # sycontac module providing functions to lookup contacts
4
+ module Sycontact
5
+ describe AddressBook do
6
+
7
+ before do
8
+ source_file = File.join(File.dirname(__FILE__), "files/address_source.rb")
9
+ @address_book = AddressBook.new(source_file)
10
+ end
11
+
12
+ it "finds contact with sn and gn set" do
13
+ @address_book.lookup(sn: "Sugar",
14
+ gn: "Pierre").should_not be_empty
15
+ end
16
+
17
+ it "finds contact with leading and training white spaces in sn and gn" do
18
+ @address_book.lookup(sn: " Sugar ",
19
+ gn: " Pierre ").should_not be_empty
20
+ end
21
+
22
+ it "ignores capitalized values in sn and gn" do
23
+ @address_book.lookup(sn: " sugar",
24
+ gn: " pierre \n").should_not be_empty
25
+ end
26
+
27
+ it "finds contact if only sn is set" do
28
+ @address_book.lookup(sn: "sugar").should_not be_empty
29
+ end
30
+
31
+ it "finds contact with cn = 'Pierre Sugar' set" do
32
+ @address_book.lookup(cn: "Pierre Sugar").should_not be_empty
33
+ end
34
+
35
+ it "finds contact with cn = 'Sugar, Pierre' set" do
36
+ @address_book.lookup(cn: "Sugar, Pierre").should_not be_empty
37
+ end
38
+
39
+ it "ignores capitalized values in find cn" do
40
+ @address_book.lookup(cn: "pierre Sugar").should_not be_empty
41
+ @address_book.lookup(cn: "sugar, pierre").should_not be_empty
42
+ end
43
+
44
+ it "finds contact with cn when only one part of the name is set" do
45
+ @address_book.lookup(cn: "Pierre").should_not be_empty
46
+ @address_book.lookup(cn: "Sugar").size.should eq 2
47
+ end
48
+
49
+ it "finds multiple contacts based on attributes other than cn, gn and sn" do
50
+ @address_book.lookup(mail: "amanda@sugar.com").should_not be_empty
51
+ end
52
+
53
+ it "find should return nil when no attribute matches" do
54
+ @address_book.lookup(mail: "user@example.com").should be_empty
55
+ end
56
+
57
+ it "should return title" do
58
+ @address_book.title.should eq "AddressSource"
59
+ end
60
+
61
+ it "should return print summary of result" do
62
+ output = []
63
+ @address_book.lookup(cn: "Pierre Sugar").each do |contact|
64
+ @address_book.print_summary(contact) do |k, v|
65
+ output << "#{k} has the value of #{v}"
66
+ end
67
+ end
68
+ output.should eq [
69
+ "sn has the value of Sugar",
70
+ "gn has the value of Pierre",
71
+ "c has the value of DE",
72
+ "l has the value of Vancouver",
73
+ "st has the value of BC",
74
+ "street has the value of Robson Street",
75
+ "o has the value of SugarYourCoffee",
76
+ "ou has the value of DevOps",
77
+ "title has the value of No Title",
78
+ "description has the value of Development and Operations",
79
+ "telephone has the value of +001 (123) 4567",
80
+ "mobile has the value of +001 (765) 4321",
81
+ "mail has the value of pierre@sugaryourcoffee.de"
82
+ ]
83
+ end
84
+
85
+ it "should print all of result" do
86
+ output = []
87
+ @address_book.lookup(cn: "Pierre Sugar").each do |contact|
88
+ output << @address_book.print_all(contact)
89
+ end
90
+ output.should eq [
91
+ [{:sn=>"Sugar",
92
+ :gn=>"Pierre",
93
+ :c=>"DE",
94
+ :l=>"Vancouver",
95
+ :st=>"BC",
96
+ :street=>"Robson Street",
97
+ :o=>"SugarYourCoffee",
98
+ :ou=>"DevOps",
99
+ :title=>"No Title",
100
+ :description=>"Development and Operations",
101
+ :telephone=>"+001 (123) 4567",
102
+ :mobile=>"+001 (765) 4321",
103
+ :mail=>"pierre@sugaryourcoffee.de"}]
104
+ ]
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,86 @@
1
+ # Test module for rspec providing functions to lookup contacts in the test-contacts directory
2
+ module AddressSource
3
+
4
+ # URL where the contact source files can be found
5
+ URL = File.join(File.dirname(__FILE__), "test-contacts")
6
+
7
+ # REGEX to extract contact data from the contact source file
8
+ REGEX = { cn: /(?<=<common_name>)[\w -]*(?=<\common_name>)/,
9
+ sn: /(?<=<surname>)[\w -]*(?=<\/surname>)/,
10
+ gn: /(?<=<given_name>)[\w -]*(?=<\/given_name>)/,
11
+ c: /(?<=<country>)[\w]*(?=<\/country>)/,
12
+ l: /(?<=<location>)[\w]*(?=<\/location>)/,
13
+ st: /(?<=<state>)[\w]*(?=<\/state>)/,
14
+ street: /(?<=<street>)[\w .]*(?=<\/street>)/,
15
+ o: /(?<=<organization>)[\w -]*(?=<\/organization>)/,
16
+ ou: /(?<=<department>)[\w -]*(?=<\/department>)/,
17
+ title: /(?<=<title>)[\w .-]*(?=<\/title>)/,
18
+ description: /(?<=<description>)[\w -+]*(?=<\/description>)/,
19
+ telephone: /(?<=<telephone>)[\w +()-]*(?=<\/telephone>)/,
20
+ mobile: /(?<=<mobile>)[\w +()-]*(?=<\/mobile>)/,
21
+ mail: /(?<=<email>)[\w @.-]*(?=<\/email>)/
22
+ }
23
+
24
+ # Looks up a contact based on the pattern
25
+ def lookup(pattern = {})
26
+ contacts = []
27
+ create_source_files(pattern).each do |source_file|
28
+
29
+ next unless File.exist? source_file
30
+
31
+ source = File.read(source_file)
32
+
33
+ next if source.empty?
34
+
35
+ values = {}
36
+
37
+ REGEX.each do |key, regex|
38
+ value = source.scan(regex)[0]
39
+ values[key] = value if value
40
+ end
41
+
42
+ contacts << values
43
+ end
44
+
45
+ contacts.keep_if do |contact|
46
+ pattern.each.reduce(true) do |match, pattern|
47
+ contact_does_not_have_key = contact[pattern[0]].nil?
48
+ regex = Regexp.new(pattern[1].strip.downcase)
49
+ pos = regex =~ contact[pattern[0]].strip.downcase unless contact_does_not_have_key
50
+ match and (not pos.nil? or contact_does_not_have_key)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Overrides the default title method in AddressBook
56
+ def title
57
+ "AddressSource"
58
+ end
59
+
60
+ private
61
+
62
+ # Creates a source file name based on the pattern
63
+ def create_source_files(pattern)
64
+ source_files = []
65
+ if pattern[:cn]
66
+ names = pattern[:cn].scan(/(^[a-zA-Z]*)[^a-zA-Z]*([a-zA-Z]*)/).flatten
67
+ names[0] = '*' if names[0].empty?
68
+ names[1] = '*' if names[1].empty?
69
+ names.permutation do |names|
70
+ file = File.join(URL, "#{names.join('_').downcase}.contact")
71
+ Dir.glob(file).each { |file| source_files << file }
72
+ end
73
+ elsif pattern[:sn] or pattern[:gn]
74
+ sn = pattern[:sn] ? pattern[:sn].strip.downcase : '*'
75
+ gn = pattern[:gn] ? pattern[:gn].strip.downcase : '*'
76
+ Dir.glob(File.join(URL, "#{gn}_#{sn}.contact")).each do |file|
77
+ source_files << file
78
+ end
79
+ else
80
+ Dir.glob(File.join(URL, "*_*.contact")).each do |file|
81
+ source_files << file
82
+ end
83
+ end
84
+ source_files
85
+ end
86
+ end
@@ -0,0 +1,14 @@
1
+ # Test file for rspec
2
+ <given_name>Amanda</given_name>
3
+ <surname>Sugar</surname>
4
+ <country>DE</country>
5
+ <location>City</location>
6
+ <state>State</state>
7
+ <street>Street</street>
8
+ <organization>Company</organization>
9
+ <department>Department</department>
10
+ <title>Dr.</title>
11
+ <description>Description of Amanda Sugar</description>
12
+ <telephone>+49 (123) 4567</telephone>
13
+ <mobile>+49 (765) 4321</mobile>
14
+ <email>amanda@sugar.com</email>
@@ -0,0 +1,14 @@
1
+ # Test file for rspec
2
+ <given_name>Pierre</given_name>
3
+ <surname>Sugar</surname>
4
+ <country>DE</country>
5
+ <location>Vancouver</location>
6
+ <state>BC</state>
7
+ <street>Robson Street</street>
8
+ <organization>SugarYourCoffee</organization>
9
+ <department>DevOps</department>
10
+ <title>No Title</title>
11
+ <description>Development and Operations</description>
12
+ <telephone>+001 (123) 4567</telephone>
13
+ <mobile>+001 (765) 4321</mobile>
14
+ <email>pierre@sugaryourcoffee.de</email>
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: syc-ontact
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pierre Sugar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: "sycontact\n=========\n`syc-ontact` is a command line interface for looking
28
+ up contacts from any source that is providing contact information.\n\nInstallation\n============\n`gem
29
+ install sycontact`\n\nSetup\n=====\nTo use `sycontact` a source-file has to be provided.
30
+ The source-file is Ruby script retrieving the data from the source. A source can
31
+ be anything that can be read from the Ruby script, e.g. a web site, an LDAP server,
32
+ a file with contact data.\n\n**Note:** Without a user defined Ruby script file (a
33
+ Ruby module) `sycontact` will do nothing but \n creating a configuration
34
+ file, a source directory and displaying the help page.\n\nTo get `sycontact` to
35
+ life you have to follow these setup steps:\n\n1. start `sycontact` once. This will
36
+ create the configuration file and the working directory\n2. provide a Ruby module
37
+ describing how to retrieve the data from the source. The module name has\n to
38
+ be `some_name_source.rb`.\n\nThe Ruby source code below describes a source-file
39
+ that is retrieving contact data from a contact directory with the name *test-contacts*
40
+ below the module's directory.\n\n\n```\nmodule AddressSource\n\n # Where to find
41
+ the contact files\n URL = File.join(File.dirname(__FILE__), \"test-contacts\")\n\n
42
+ \ # Regex to extract contact data from the contact files\n REGEX = { cn: /(?<=<common_name>)[\\w
43
+ -]*(?=<\\common_name>)/,\n sn: /(?<=<surname>)[\\w -]*(?=<\\/surname>)/,\n
44
+ \ gn: /(?<=<given_name>)[\\w -]*(?=<\\/given_name>)/,\n c:
45
+ /(?<=<country>)[\\w]*(?=<\\/country>)/,\n l: /(?<=<location>)[\\w]*(?=<\\/location>)/,\n
46
+ \ st: /(?<=<state>)[\\w]*(?=<\\/state>)/,\n street: /(?<=<street>)[\\w
47
+ .]*(?=<\\/street>)/,\n o: /(?<=<organization>)[\\w -]*(?=<\\/organization>)/,\n
48
+ \ ou: /(?<=<department>)[\\w -]*(?=<\\/department>)/,\n title:
49
+ /(?<=<title>)[\\w .-]*(?=<\\/title>)/,\n description: /(?<=<description>)[\\w
50
+ -+]*(?=<\\/description>)/,\n telephone: /(?<=<telephone>)[\\w +()-]*(?=<\\/telephone>)/,\n
51
+ \ mobile: /(?<=<mobile>)[\\w +()-]*(?=<\\/mobile>)/,\n mail:
52
+ /(?<=<email>)[\\w @.-]*(?=<\\/email>)/\n }\n\n # Mandatory method! Will
53
+ be invoked by `sycontact`.\n # Will lookup the contact based on the pattern provided\n
54
+ \ def lookup(pattern = {})\n contacts = []\n create_source_files(pattern).each
55
+ do |source_file|\n\n next unless File.exist? source_file\n\n source =
56
+ File.read(source_file)\n\n next if source.empty?\n\n values = {}\n\n REGEX.each
57
+ do |key, regex|\n value = source.scan(regex)[0]\n values[key] = value
58
+ if value\n end\n\n contacts << values\n end\n\n contacts.keep_if
59
+ do |contact|\n pattern.each.reduce(true) do |match, pattern| \n contact_does_not_have_key
60
+ = contact[pattern[0]].nil?\n regex = Regexp.new(pattern[1].strip.downcase)\n
61
+ \ pos = regex =~ contact[pattern[0]].strip.downcase unless contact_does_not_have_key\n
62
+ \ match and (not pos.nil? or contact_does_not_have_key)\n end\n end\n
63
+ \ end\n\n private\n\n # Creates the contact file name. In this case the contact
64
+ files have to be in the form\n # firstname_lastname.contact or lastname_firstname.contact.
65
+ If neather is given all *_*.contact\n # files are retrieved\n def create_source_files(pattern)\n
66
+ \ source_files = []\n if pattern[:cn]\n names = pattern[:cn].scan(/(^[a-zA-Z]*)[^a-zA-Z]*([a-zA-Z]*)/).flatten\n
67
+ \ names[0] = '*' if names[0].empty?\n names[1] = '*' if names[1].empty?\n
68
+ \ names.permutation do |names|\n file = File.join(URL, \"#{names.join('_').downcase}.contact\")\n
69
+ \ Dir.glob(file).each { |file| source_files << file }\n end\n elsif
70
+ pattern[:sn] or pattern[:gn]\n sn = pattern[:sn] ? pattern[:sn].strip.downcase
71
+ : '*'\n gn = pattern[:gn] ? pattern[:gn].strip.downcase : '*'\n Dir.glob(File.join(URL,
72
+ \"#{gn}_#{sn}.contact\")).each do |file|\n source_files << file\n end\n
73
+ \ else\n Dir.glob(File.join(URL, \"*_*.contact\")).each do |file|\n source_files
74
+ << file\n end\n end\n source_files\n end\nend\n```\n\n**Listing
75
+ 1: Source-file to provide information about how to retrieve contact information**\n\nBelow
76
+ a contact file that can be read by the above source module.\n\n```\n<common_name>Sugar
77
+ Pierre</common_name>\n<given_name>Pierre</given_name>\n<surname>Sugar</surname>\n<country>CA</country>\n<location>Vancouver</location>\n<state>BC</state>\n<street>Robson
78
+ Street</street>\n<organization>SugarYourCoffee</organization>\n<department>DevOps</department>\n<telephone>+001
79
+ (123) 4567</telephone>\n<mobile>+001 (765) 4321</mobile>\n<email>pierre@sugaryourcoffee.de</email>\n```\n\n**Listing
80
+ 2: Contact file that can be read by the `AddressSource` module**\n\nUsage\n=====\n\nGet
81
+ help for sycontact\n\n $ sycontact -h\n\n```\nUsage: sycontact [options]\n -p,
82
+ --print RAW|SUMMARY|ALL Print contact attributes\n SUMMARY
83
+ (default)\n --cn COMMON_NAME Common name e.g. 'Jane Doe' or 'Doe,
84
+ Jane'\n --sn SURNAME Surname e.g. 'Doe'\n --gn GIVEN_NAME
85
+ \ Given name e.g. 'Jane'\n --uid USER_ID User
86
+ ID\n -c COUNTRY Country in ISO 3166 e.g. 'CA' for Canada\n
87
+ \ -l LOCATION City e.g. 'Vancouver'\n --st STATE State
88
+ e.g. 'British Columbia'\n --street STREET Street e.g. 'Robson
89
+ Street'\n -o ORGANIZATION Organization e.g. 'Northstar'\n --ou
90
+ ORGANIZATIONAL_UNIT Department e.g. 'R&D'\n --title TITLE Title
91
+ e.g. 'Dr.'\n --description DESCRIPTION Description e.g. 'Head of R&D'\n
92
+ \ --telephone TELEPHONE Telephone number e.g. '+001 (252) 4354'\n --mobile
93
+ MOBILE_PHONE Mobile number e.g. '+001 (252) 4345'\n --mail E-MAIL
94
+ \ E-Mail address e.g. 'jane@northstart.ca'\n -h, --help Show
95
+ his message\n```\n\nLookup a contact with summary output\n\n $ sycontact --cn
96
+ sugar\n $ sycontact --cn \"sugar, pierre\"\n $ sycontact --cn \"pierre sugar\"\n\nAny
97
+ of the above commands result in the following output\n\n```\nAddressSource\n\nCN..................Sugar
98
+ Pierre\nC...................DE\nL...................Vancouver\nO...................SugarYourCoffee\nOU..................DevOps\nTELEPHONE...........+001
99
+ (123) 4567\nMOBILE..............+001 (765) 4321\nMAIL................pierre@sugaryourcoffee.de\n```\nSources\n=======\nHome
100
+ page: <http://syc.dyndns.org/drupal/wiki/sycontact-lookup-contacts-any-source>\n\nSource:
101
+ <https://github.com/sugaryourcoffee/syc-ontact>\n\nContact\n=======\n\n<pierre@sugaryourcoffee.de>\n"
102
+ email: pierre@sugaryourcoffee.de
103
+ executables:
104
+ - sycontact
105
+ extensions: []
106
+ extra_rdoc_files: []
107
+ files:
108
+ - LICENSE.md
109
+ - README.md
110
+ - bin/sycontact
111
+ - lib/sycontact/address_book.rb
112
+ - lib/sycontact/address_book_library.rb
113
+ - spec/sycontact/address_book_library_spec.rb
114
+ - spec/sycontact/address_book_spec.rb
115
+ - spec/sycontact/files/address_source.rb
116
+ - spec/sycontact/files/test-contacts/amanda_sugar.contact
117
+ - spec/sycontact/files/test-contacts/pierre_sugar.contact
118
+ homepage: https://github.com/sugaryourcoffee/syc-ontact
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '1.9'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.2.0
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Lookup contacts from any source by providing customized source files
142
+ test_files:
143
+ - spec/sycontact/files/test-contacts/pierre_sugar.contact
144
+ - spec/sycontact/files/test-contacts/amanda_sugar.contact
145
+ - spec/sycontact/files/address_source.rb
146
+ - spec/sycontact/address_book_library_spec.rb
147
+ - spec/sycontact/address_book_spec.rb