syc-ontact 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.
@@ -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