lperichon-contacts 1.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/.gitmodules +3 -0
- data/LICENSE +18 -0
- data/README.markdown +111 -0
- data/Rakefile +55 -0
- data/lib/contacts/consumer.rb +123 -0
- data/lib/contacts/google.rb +70 -0
- data/lib/contacts/oauth_consumer.rb +70 -0
- data/lib/contacts/util.rb +24 -0
- data/lib/contacts/version.rb +9 -0
- data/lib/contacts/windows_live.rb +188 -0
- data/lib/contacts/yahoo.rb +76 -0
- data/lib/contacts.rb +85 -0
- data/rails/init.rb +2 -0
- data/spec/contact_spec.rb +41 -0
- data/spec/feeds/contacts.yml +10 -0
- data/spec/feeds/flickr/auth.getFrob.xml +4 -0
- data/spec/feeds/flickr/auth.getToken.xml +5 -0
- data/spec/feeds/google-many.xml +48 -0
- data/spec/feeds/google-single.xml +46 -0
- data/spec/feeds/wl_contacts.xml +29 -0
- data/spec/feeds/yh_contacts.txt +119 -0
- data/spec/feeds/yh_credential.xml +28 -0
- data/spec/flickr/auth_spec.rb +80 -0
- data/spec/gmail/auth_spec.rb +70 -0
- data/spec/gmail/fetching_spec.rb +196 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +84 -0
- data/spec/windows_live/windows_live_spec.rb +34 -0
- data/spec/yahoo/yahoo_spec.rb +83 -0
- metadata +90 -0
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'contacts/google'
|
3
|
+
|
4
|
+
describe Contacts::Google do
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@gmail = create
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
Contacts::Google.new('dummytoken')
|
12
|
+
end
|
13
|
+
|
14
|
+
after :each do
|
15
|
+
FakeWeb.clean_registry
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'fetches contacts feed via HTTP GET' do
|
19
|
+
it 'with defaults' do
|
20
|
+
FakeWeb::register_uri(:get, 'www.google.com/m8/feeds/contacts/default/thin',
|
21
|
+
:string => 'thin results',
|
22
|
+
:verify => lambda { |req|
|
23
|
+
req['Authorization'].should == %(AuthSub token="dummytoken")
|
24
|
+
req['Accept-Encoding'].should == 'gzip'
|
25
|
+
req['User-Agent'].should == "Ruby Contacts v#{Contacts::VERSION::STRING} (gzip)"
|
26
|
+
}
|
27
|
+
)
|
28
|
+
|
29
|
+
response = @gmail.get({})
|
30
|
+
response.body.should == 'thin results'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'with explicit user ID and full projection' do
|
34
|
+
@gmail = Contacts::Google.new('dummytoken', 'person@example.com')
|
35
|
+
@gmail.projection = 'full'
|
36
|
+
|
37
|
+
FakeWeb::register_uri(:get, 'www.google.com/m8/feeds/contacts/person%40example.com/full',
|
38
|
+
:string => 'full results'
|
39
|
+
)
|
40
|
+
|
41
|
+
response = @gmail.get({})
|
42
|
+
response.body.should == 'full results'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'handles a normal response body' do
|
47
|
+
response = mock('HTTP response')
|
48
|
+
@gmail.expects(:get).returns(response)
|
49
|
+
|
50
|
+
response.expects(:'[]').with('Content-Encoding').returns(nil)
|
51
|
+
response.expects(:body).returns('<feed/>')
|
52
|
+
|
53
|
+
@gmail.expects(:parse_contacts).with('<feed/>')
|
54
|
+
@gmail.contacts
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'handles gzipped response' do
|
58
|
+
response = mock('HTTP response')
|
59
|
+
@gmail.expects(:get).returns(response)
|
60
|
+
|
61
|
+
gzipped = StringIO.new
|
62
|
+
gzwriter = Zlib::GzipWriter.new gzipped
|
63
|
+
gzwriter.write(('a'..'z').to_a.join)
|
64
|
+
gzwriter.close
|
65
|
+
|
66
|
+
response.expects(:'[]').with('Content-Encoding').returns('gzip')
|
67
|
+
response.expects(:body).returns gzipped.string
|
68
|
+
|
69
|
+
@gmail.expects(:parse_contacts).with('abcdefghijklmnopqrstuvwxyz')
|
70
|
+
@gmail.contacts
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'raises a fetching error when something goes awry' do
|
74
|
+
FakeWeb::register_uri(:get, 'www.google.com/m8/feeds/contacts/default/thin',
|
75
|
+
:status => [404, 'YOU FAIL']
|
76
|
+
)
|
77
|
+
|
78
|
+
lambda {
|
79
|
+
@gmail.get({})
|
80
|
+
}.should raise_error(Net::HTTPServerException)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'parses the resulting feed into name/email pairs' do
|
84
|
+
@gmail.stubs(:get)
|
85
|
+
@gmail.expects(:response_body).returns(sample_xml('google-single'))
|
86
|
+
|
87
|
+
found = @gmail.contacts
|
88
|
+
found.size.should == 1
|
89
|
+
contact = found.first
|
90
|
+
contact.name.should == 'Fitzgerald'
|
91
|
+
contact.emails.should == ['fubar@gmail.com']
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'parses a complex feed into name/email pairs' do
|
95
|
+
@gmail.stubs(:get)
|
96
|
+
@gmail.expects(:response_body).returns(sample_xml('google-many'))
|
97
|
+
|
98
|
+
found = @gmail.contacts
|
99
|
+
found.size.should == 3
|
100
|
+
found[0].name.should == 'Elizabeth Bennet'
|
101
|
+
found[0].emails.should == ['liz@gmail.com', 'liz@example.org']
|
102
|
+
found[1].name.should == 'William Paginate'
|
103
|
+
found[1].emails.should == ['will_paginate@googlegroups.com']
|
104
|
+
found[2].name.should be_nil
|
105
|
+
found[2].emails.should == ['anonymous@example.com']
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'makes modification time available after parsing' do
|
109
|
+
@gmail.updated_at.should be_nil
|
110
|
+
@gmail.stubs(:get)
|
111
|
+
@gmail.expects(:response_body).returns(sample_xml('google-single'))
|
112
|
+
|
113
|
+
@gmail.contacts
|
114
|
+
u = @gmail.updated_at
|
115
|
+
u.year.should == 2008
|
116
|
+
u.day.should == 5
|
117
|
+
@gmail.updated_at_string.should == '2008-03-05T12:36:38.836Z'
|
118
|
+
end
|
119
|
+
|
120
|
+
describe 'GET query parameter handling' do
|
121
|
+
|
122
|
+
before :each do
|
123
|
+
@gmail = create
|
124
|
+
@gmail.stubs(:response_body)
|
125
|
+
@gmail.stubs(:parse_contacts)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'abstracts ugly parameters behind nicer ones' do
|
129
|
+
expect_params 'max-results' => '25',
|
130
|
+
'orderby' => 'lastmodified',
|
131
|
+
'sortorder' => 'ascending',
|
132
|
+
'start-index' => '11',
|
133
|
+
'updated-min' => 'datetime'
|
134
|
+
|
135
|
+
@gmail.contacts :limit => 25,
|
136
|
+
:offset => 10,
|
137
|
+
:order => 'lastmodified',
|
138
|
+
:descending => false,
|
139
|
+
:updated_after => 'datetime'
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should have implicit :descending with :order' do
|
143
|
+
expect_params 'orderby' => 'lastmodified',
|
144
|
+
'sortorder' => 'descending',
|
145
|
+
'max-results' => '200'
|
146
|
+
|
147
|
+
@gmail.contacts :order => 'lastmodified'
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should have default :limit of 200' do
|
151
|
+
expect_params 'max-results' => '200'
|
152
|
+
@gmail.contacts
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should skip nil values in parameters' do
|
156
|
+
expect_params 'start-index' => '1'
|
157
|
+
@gmail.contacts :limit => nil, :offset => 0
|
158
|
+
end
|
159
|
+
|
160
|
+
def expect_params(params)
|
161
|
+
query_string = Contacts::Google.query_string(params)
|
162
|
+
FakeWeb::register_uri(:get, "www.google.com/m8/feeds/contacts/default/thin?#{query_string}")
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
describe 'Retrieving all contacts (in chunks)' do
|
168
|
+
|
169
|
+
before :each do
|
170
|
+
@gmail = create
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'should make only one API call when no more is needed' do
|
174
|
+
@gmail.expects(:contacts).with(instance_of(Hash)).once.returns((0..8).to_a)
|
175
|
+
|
176
|
+
@gmail.all_contacts({}, 10).should == (0..8).to_a
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should make multiple calls to :contacts when needed' do
|
180
|
+
@gmail.expects(:contacts).with(has_entries(:offset => 0 , :limit => 10)).returns(( 0..9 ).to_a)
|
181
|
+
@gmail.expects(:contacts).with(has_entries(:offset => 10, :limit => 10)).returns((10..19).to_a)
|
182
|
+
@gmail.expects(:contacts).with(has_entries(:offset => 20, :limit => 10)).returns((20..24).to_a)
|
183
|
+
|
184
|
+
@gmail.all_contacts({}, 10).should == (0..24).to_a
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should make one extra API call when not sure whether there are more contacts' do
|
188
|
+
@gmail.expects(:contacts).with(has_entries(:offset => 0 , :limit => 10)).returns((0..9).to_a)
|
189
|
+
@gmail.expects(:contacts).with(has_entries(:offset => 10, :limit => 10)).returns([])
|
190
|
+
|
191
|
+
@gmail.all_contacts({}, 10).should == (0..9).to_a
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
data/spec/rcov.opts
ADDED
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'rspec', '~> 1.1.3'
|
3
|
+
require 'spec'
|
4
|
+
gem 'mocha', '~> 0.9.0'
|
5
|
+
require 'mocha'
|
6
|
+
|
7
|
+
require 'cgi'
|
8
|
+
require 'fake_web'
|
9
|
+
FakeWeb.allow_net_connect = false
|
10
|
+
|
11
|
+
module SampleFeeds
|
12
|
+
FEED_DIR = File.dirname(__FILE__) + '/feeds/'
|
13
|
+
|
14
|
+
def sample_xml(name)
|
15
|
+
File.read "#{FEED_DIR}#{name}.xml"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module HttpMocks
|
20
|
+
def mock_response(type = :success)
|
21
|
+
klass = case type
|
22
|
+
when :success then Net::HTTPSuccess
|
23
|
+
when :redirect then Net::HTTPRedirection
|
24
|
+
when :fail then Net::HTTPClientError
|
25
|
+
else type
|
26
|
+
end
|
27
|
+
|
28
|
+
klass.new(nil, nil, nil)
|
29
|
+
end
|
30
|
+
|
31
|
+
def mock_connection(ssl = true)
|
32
|
+
connection = mock('HTTP connection')
|
33
|
+
connection.stubs(:start)
|
34
|
+
connection.stubs(:finish)
|
35
|
+
if ssl
|
36
|
+
connection.expects(:use_ssl=).with(true)
|
37
|
+
connection.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
|
38
|
+
end
|
39
|
+
connection
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Spec::Runner.configure do |config|
|
44
|
+
config.include SampleFeeds, HttpMocks
|
45
|
+
# config.predicate_matchers[:swim] = :can_swim?
|
46
|
+
|
47
|
+
config.mock_with :mocha
|
48
|
+
end
|
49
|
+
|
50
|
+
module Mocha
|
51
|
+
module ParameterMatchers
|
52
|
+
def query_string(entries, partial = false)
|
53
|
+
QueryStringMatcher.new(entries, partial)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class QueryStringMatcher < Mocha::ParameterMatchers::Base
|
59
|
+
|
60
|
+
def initialize(entries, partial)
|
61
|
+
@entries = entries
|
62
|
+
@partial = partial
|
63
|
+
end
|
64
|
+
|
65
|
+
def matches?(available_parameters)
|
66
|
+
string = available_parameters.shift.split('?').last
|
67
|
+
broken = string.split('&').map { |pair| pair.split('=').map { |value| CGI.unescape(value) } }
|
68
|
+
hash = Hash[*broken.flatten]
|
69
|
+
|
70
|
+
if @partial
|
71
|
+
has_entry_matchers = @entries.map do |key, value|
|
72
|
+
Mocha::ParameterMatchers::HasEntry.new(key, value)
|
73
|
+
end
|
74
|
+
Mocha::ParameterMatchers::AllOf.new(*has_entry_matchers).matches?([hash])
|
75
|
+
else
|
76
|
+
@entries == hash
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def mocha_inspect
|
81
|
+
"query_string(#{@entries.mocha_inspect})"
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'contacts/windows_live'
|
3
|
+
|
4
|
+
describe Contacts::WindowsLive do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@path = Dir.getwd + '/spec/feeds/'
|
8
|
+
@wl = Contacts::WindowsLive.new(@path + 'contacts.yml')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'parse the XML contacts document' do
|
12
|
+
contacts = Contacts::WindowsLive.parse_xml(contacts_xml)
|
13
|
+
|
14
|
+
contacts[0].name.should be_nil
|
15
|
+
contacts[0].email.should == 'froz@gmail.com'
|
16
|
+
contacts[1].name.should == 'Rafael Timbo'
|
17
|
+
contacts[1].email.should == 'timbo@hotmail.com'
|
18
|
+
contacts[2].name.should be_nil
|
19
|
+
contacts[2].email.should == 'betinho@hotmail.com'
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should can be initialized by a YAML file' do
|
24
|
+
wll = @wl.instance_variable_get('@wll')
|
25
|
+
|
26
|
+
wll.appid.should == 'your_app_id'
|
27
|
+
wll.securityalgorithm.should == 'wsignin1.0'
|
28
|
+
wll.returnurl.should == 'http://yourserver.com/your_return_url'
|
29
|
+
end
|
30
|
+
|
31
|
+
def contacts_xml
|
32
|
+
File.open(@path + 'wl_contacts.xml', 'r+').read
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'contacts/yahoo'
|
3
|
+
|
4
|
+
describe Contacts::Yahoo do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@path = Dir.getwd + '/spec/feeds/'
|
8
|
+
@yahoo = Contacts::Yahoo.new(@path + 'contacts.yml')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should generate an athentication URL' do
|
12
|
+
auth_url = @yahoo.get_authentication_url()
|
13
|
+
auth_url.should match(/https:\/\/api.login.yahoo.com\/WSLogin\/V1\/wslogin\?appid=i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--&ts=.*&sig=.*/)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should have a simple interface to grab the contacts' do
|
17
|
+
@yahoo.expects(:access_user_credentials).returns(read_file('yh_credential.xml'))
|
18
|
+
@yahoo.expects(:access_address_book_api).returns(read_file('yh_contacts.txt'))
|
19
|
+
|
20
|
+
redirect_path = '/?appid=i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--&token=AB.KoEg8vBwvJKFkwfcDTJEMKhGeAD6KhiDe0aZLCvoJzMeQG00-&appdata=&ts=1218501215&sig=d381fba89c7e9d3c14788720733c3fbf'
|
21
|
+
|
22
|
+
results = @yahoo.contacts(redirect_path)
|
23
|
+
results.should have_contact('Hugo Barauna', 'hugo.barauna@gmail.com')
|
24
|
+
results.should have_contact('Nina Benchimol', 'nina@hotmail.com')
|
25
|
+
results.should have_contact('Andrea Dimitri', 'and@yahoo.com')
|
26
|
+
results.should have_contact('Ricardo Fiorelli', 'ricardo@poli.usp.br')
|
27
|
+
results.should have_contact('Priscila', 'pizinha@yahoo.com.br')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should validate yahoo redirect signature' do
|
31
|
+
redirect_path = '/?appid=i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--&token=AB.KoEg8vBwvJKFkwfcDTJEMKhGeAD6KhiDe0aZLCvoJzMeQG00-&appdata=&ts=1218501215&sig=d381fba89c7e9d3c14788720733c3fbf'
|
32
|
+
|
33
|
+
@yahoo.validate_signature(redirect_path).should be_true
|
34
|
+
@yahoo.token.should == 'AB.KoEg8vBwvJKFkwfcDTJEMKhGeAD6KhiDe0aZLCvoJzMeQG00-'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should detect when the redirect is not valid' do
|
38
|
+
redirect_path = '/?appid=i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--&token=AB.KoEg8vBwvJKFkwfcDTJEMKhGeAD6KhiDe0aZLCvoJzMeQG00-&appdata=&ts=1218501215&sig=de4fe4ebd50a8075f75dcc23f6aca04f'
|
39
|
+
|
40
|
+
lambda{ @yahoo.validate_signature(redirect_path) }.should raise_error
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should generate the credential request URL' do
|
44
|
+
redirect_path = '/?appid=i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--&token=AB.KoEg8vBwvJKFkwfcDTJEMKhGeAD6KhiDe0aZLCvoJzMeQG00-&appdata=&ts=1218501215&sig=d381fba89c7e9d3c14788720733c3fbf'
|
45
|
+
@yahoo.validate_signature(redirect_path)
|
46
|
+
|
47
|
+
@yahoo.get_credential_url.should match(/https:\/\/api.login.yahoo.com\/WSLogin\/V1\/wspwtoken_login\?appid=i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--&ts=.*&token=.*&sig=.*/)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should parse the credential XML' do
|
51
|
+
@yahoo.parse_credentials(read_file('yh_credential.xml'))
|
52
|
+
|
53
|
+
@yahoo.wssid.should == 'tr.jZsW/ulc'
|
54
|
+
@yahoo.cookie.should == 'Y=cdunlEx76ZEeIdWyeJNOegxfy.jkeoULJCnc7Q0Vr8D5P.u.EE2vCa7G2MwBoULuZhvDZuJNqhHwF3v5RJ4dnsWsEDGOjYV1k6snoln3RlQmx0Ggxs0zAYgbaA4BFQk5ieAkpipq19l6GoD_k8IqXRfJN0Q54BbekC_O6Tj3zl2wV3YQK6Mi2MWBQFSBsO26Tw_1yMAF8saflF9EX1fQl4N.1yBr8UXb6LLDiPQmlISq1_c6S6rFbaOhSZMgO78f2iqZmUAk9RmCHrqPJiHEo.mJlxxHaQsuqTMf7rwLEHqK__Gi_bLypGtaslqeWyS0h2J.B5xwRC8snfEs3ct_kLXT3ngP_pK3MeMf2pe1TiJ4JXVciY9br.KJFUgNd4J6rmQsSFj4wPLoMGCETfVc.M8KLiaFHasZqXDyCE7tvd1khAjQ_xLfQKlg1GlBOWmbimQ1FhdHnsVj3svXjEGquRh8JI2sHIQrzoiqAPBf9WFKQcH0t_1dxf4MOH.7gJaYDPEozCW5EcCsYjuHup9xJKxyTddh5pk8yUg5bURzA.TwPalExMKsbv.RWFBhzWKuTp5guNcqjmUHcCoT19_qFENHX41Xf3texAnsDDGj'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should parse the contacts json response' do
|
58
|
+
json = read_file('yh_contacts.txt')
|
59
|
+
|
60
|
+
Contacts::Yahoo.parse_contacts(json).should have_contact('Hugo Barauna', 'hugo.barauna@gmail.com')
|
61
|
+
Contacts::Yahoo.parse_contacts(json).should have_contact('Nina Benchimol', 'nina@hotmail.com')
|
62
|
+
Contacts::Yahoo.parse_contacts(json).should have_contact('Andrea Dimitri', 'and@yahoo.com')
|
63
|
+
Contacts::Yahoo.parse_contacts(json).should have_contact('Ricardo Fiorelli', 'ricardo@poli.usp.br')
|
64
|
+
Contacts::Yahoo.parse_contacts(json).should have_contact('Priscila', 'pizinha@yahoo.com.br')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should can be initialized by a YAML file' do
|
68
|
+
@yahoo.appid.should == 'i%3DB%26p%3DUw70JGIdHWVRbpqYItcMw--'
|
69
|
+
@yahoo.secret.should == 'a34f389cbd135de4618eed5e23409d34450'
|
70
|
+
end
|
71
|
+
|
72
|
+
def read_file(file)
|
73
|
+
File.open(@path + file, 'r+').read
|
74
|
+
end
|
75
|
+
|
76
|
+
def have_contact(name, email)
|
77
|
+
matcher_class = Class.new()
|
78
|
+
matcher_class.instance_eval do
|
79
|
+
define_method(:matches?) {|some_contacts| some_contacts.any? {|a_contact| a_contact.name == name && a_contact.emails.include?(email)}}
|
80
|
+
end
|
81
|
+
matcher_class.new
|
82
|
+
end
|
83
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lperichon-contacts
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
version: "1.0"
|
9
|
+
platform: ruby
|
10
|
+
authors: []
|
11
|
+
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2010-07-12 00:00:00 -03:00
|
17
|
+
default_executable:
|
18
|
+
dependencies: []
|
19
|
+
|
20
|
+
description:
|
21
|
+
email:
|
22
|
+
executables: []
|
23
|
+
|
24
|
+
extensions: []
|
25
|
+
|
26
|
+
extra_rdoc_files: []
|
27
|
+
|
28
|
+
files:
|
29
|
+
- .gitmodules
|
30
|
+
- LICENSE
|
31
|
+
- README.markdown
|
32
|
+
- Rakefile
|
33
|
+
- lib/contacts.rb
|
34
|
+
- lib/contacts/consumer.rb
|
35
|
+
- lib/contacts/google.rb
|
36
|
+
- lib/contacts/oauth_consumer.rb
|
37
|
+
- lib/contacts/util.rb
|
38
|
+
- lib/contacts/version.rb
|
39
|
+
- lib/contacts/windows_live.rb
|
40
|
+
- lib/contacts/yahoo.rb
|
41
|
+
- rails/init.rb
|
42
|
+
- spec/contact_spec.rb
|
43
|
+
- spec/feeds/contacts.yml
|
44
|
+
- spec/feeds/flickr/auth.getFrob.xml
|
45
|
+
- spec/feeds/flickr/auth.getToken.xml
|
46
|
+
- spec/feeds/google-many.xml
|
47
|
+
- spec/feeds/google-single.xml
|
48
|
+
- spec/feeds/wl_contacts.xml
|
49
|
+
- spec/feeds/yh_contacts.txt
|
50
|
+
- spec/feeds/yh_credential.xml
|
51
|
+
- spec/flickr/auth_spec.rb
|
52
|
+
- spec/gmail/auth_spec.rb
|
53
|
+
- spec/gmail/fetching_spec.rb
|
54
|
+
- spec/rcov.opts
|
55
|
+
- spec/spec.opts
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
- spec/windows_live/windows_live_spec.rb
|
58
|
+
- spec/yahoo/yahoo_spec.rb
|
59
|
+
has_rdoc: true
|
60
|
+
homepage:
|
61
|
+
licenses: []
|
62
|
+
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.3.6
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: Fetch users' contact lists without asking them to provide their passwords, as painlessly as possible.
|
89
|
+
test_files: []
|
90
|
+
|