portablecontacts 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Pelle Braendgaard
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,48 @@
1
+ = Portable Contacts
2
+
3
+ This is a ruby client implementation of Portable Contacts a standard for exchanging profile and address book data.
4
+
5
+ The current draft of the standard is http://portablecontacts.net/draft-spec.html
6
+
7
+ Portable Contacts is currently supported by Plaxo, Google, Yahoo, and most OpenSocial containers.
8
+
9
+ This client uses OAuth and JSON exclusively. There are no plans for supporting basic authentication or xml.
10
+
11
+ To use it you need an OAuth AccessToken (see http://oauth.rubyforge.org/). If you are using Rails, you may find the easiest way of doing this as using the OAuth Plugin http://stakeventures.com/articles/2009/07/21/consuming-oauth-intelligently-in-rails
12
+
13
+ The Gem requires:
14
+
15
+ * ActiveSupport
16
+ * OAuth Gem
17
+ * JSON gem
18
+
19
+ == Example Code
20
+
21
+ @access_token = ... # instantiate access token
22
+
23
+ @client = PortableContacts::Client.new "http://www-opensocial.googleusercontent.com/api/people", @access_token
24
+
25
+ # Get users profile
26
+
27
+ @profile = @client.me
28
+
29
+ puts @profile.display_name
30
+ => "Bob Sample"
31
+
32
+ # Get users contacts
33
+ @contacts = @client.all
34
+
35
+ == Note on Patches/Pull Requests
36
+
37
+ * Fork the project.
38
+ * Make your feature addition or bug fix.
39
+ * Add tests for it. This is important so I don't break it in a
40
+ future version unintentionally.
41
+ * Commit, do not mess with rakefile, version, or history.
42
+ (if you want to have your own version, that is fine but
43
+ bump version in a commit by itself I can ignore when I pull)
44
+ * Send me a pull request. Bonus points for topic branches.
45
+
46
+ == Copyright
47
+
48
+ Copyright (c) 2009 Pelle Braendgaard. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "portablecontacts"
8
+ gem.summary = %Q{Portable Contacts client for Ruby}
9
+ gem.description = %Q{A client library for the portable contacts standard}
10
+ gem.email = "pelleb@gmail.com"
11
+ gem.homepage = "http://github.com/pelle/portablecontacts"
12
+ gem.authors = ["Pelle Braendgaard"]
13
+ gem.rubyforge_project = "portablecontact"
14
+ gem.add_dependency('oauth', '>= 0.3.6')
15
+ gem.add_dependency('json')
16
+ gem.add_development_dependency "rspec"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::RubyforgeTasks.new do |rubyforge|
20
+ rubyforge.doc_task = "rdoc"
21
+ end
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
24
+ end
25
+
26
+ require 'spec/rake/spectask'
27
+ Spec::Rake::SpecTask.new(:spec) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.spec_files = FileList['spec/**/*_spec.rb']
30
+ end
31
+
32
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
33
+ spec.libs << 'lib' << 'spec'
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+ task :spec => :check_dependencies
39
+
40
+ task :default => :spec
41
+
42
+ require 'rake/rdoctask'
43
+ Rake::RDocTask.new do |rdoc|
44
+ if File.exist?('VERSION')
45
+ version = File.read('VERSION')
46
+ else
47
+ version = ""
48
+ end
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "portablecontacts #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,218 @@
1
+ require 'uri'
2
+ require 'json'
3
+ require 'activesupport'
4
+ module PortableContacts
5
+
6
+ # This is the main PortableContacts Client.
7
+ #
8
+ # == query options
9
+ #
10
+ # The library supports the various filter and sorting parameters. The format of this may change for this library, so limit use to :
11
+ # :count and :start_index for now.
12
+ #
13
+ class Client
14
+ attr :base_url, :access_token
15
+
16
+ # First parameter is the portable contacts base_url. Find this on your PortableContact providers documentation or through XRDS.
17
+ #
18
+ # * Google's is http://www-opensocial.googleusercontent.com/api/people
19
+ # * Yahoo's is http://appstore.apps.yahooapis.com/social/rest/people
20
+ #
21
+ # The second parameter is an OAuth::AccessToken instantiated for the provider.
22
+ #
23
+ def initialize(base_url,access_token)
24
+ @base_url=base_url
25
+ @access_token=access_token
26
+ end
27
+
28
+ # Returns the AccessToken users contact details. Note this requests all fields from provider
29
+ # Returns an PortableContacts::Person object
30
+ def me(options={})
31
+ single(get("/@me/@self",options.reverse_merge(:fields=>:all)))
32
+ end
33
+
34
+ # Returns the contacts of the user. It defaults to all fields and 100 entries
35
+ #
36
+ # @contacts = @client.all # return 100 contacts
37
+ # @contacts = @client.all :count=>10 # return 10 contacts
38
+ # @contacts = @client.all :count=>10, :start_index=>10 # returns the second page of 10 contacts
39
+ # puts @contacts.total_entries # returns the total amount of contacts on the server
40
+ #
41
+ # Returns a PortableContacts::Collection which is a subclass of Array
42
+ def all(options={})
43
+ collection(get("/@me/@all",options.reverse_merge(:fields=>:all,:count=>100)))
44
+ end
45
+
46
+ # Returns the full contact infor for a particular userid. TODO This is not tested well
47
+ # Returns an PortableContacts::Person object
48
+ def find(id, options={})
49
+ single(get("/@me/@all/#{id}",options.reverse_merge(:fields=>:all)))
50
+ end
51
+
52
+ private
53
+
54
+ def get(path,options={})
55
+ parse(@access_token.get( url_for(path)+options_for(options), {'Accept' => 'application/json'}))
56
+ end
57
+
58
+ def url_for(path)
59
+ "#{@base_url}#{path}"
60
+ end
61
+
62
+ def options_for(options={})
63
+ return "" if options.nil? || options.empty?
64
+ options.symbolize_keys! if options.respond_to? :symbolize_keys!
65
+ "?#{(fields_options(options[:fields])+filter_options(options[:filter])+sort_options(options[:sort])+pagination_options(options)).sort.join("&")}"
66
+ end
67
+
68
+ def single(data)
69
+ if data.is_a?(Hash) && data['entry']
70
+ PortableContacts::Person.new(data['entry'])
71
+ else
72
+ data
73
+ end
74
+ end
75
+
76
+ def collection(data)
77
+ if data.is_a?(Hash)
78
+ PortableContacts::Collection.new data
79
+ else
80
+ data
81
+ end
82
+ end
83
+
84
+ def parse(response)
85
+ return false unless response
86
+ if ["200","201"].include? response.code
87
+ unless response.body.blank?
88
+ JSON.parse(response.body)
89
+ else
90
+ true
91
+ end
92
+ else
93
+ false
94
+ end
95
+ end
96
+
97
+ def fields_options(options=nil)
98
+ return [] if options.nil?
99
+ if options.is_a? Symbol
100
+ return ["fields=#{(options==:all ? "@all": URI.escape(options.to_s))}"]
101
+ elsif options.respond_to?(:collect)
102
+ ["fields=#{options.collect{|f|f.to_s}.join(',')}"]
103
+ else
104
+ []
105
+ end
106
+ end
107
+
108
+ def sort_options(options=nil)
109
+ return [] if options.nil?
110
+ if options.is_a? Symbol
111
+ return ["sortBy=#{URI.escape(options.to_s)}"]
112
+ end
113
+ if options.is_a?(Hash) and options[:by]||options['by']
114
+ return to_query_list(options,"sort")
115
+ end
116
+ return []
117
+ end
118
+
119
+ def pagination_options(options=nil)
120
+ return [] if options.nil? || options.empty?
121
+ params=[]
122
+ if options[:count]
123
+ params<<"count=#{options[:count]}"
124
+ end
125
+ if options[:start_index]
126
+ params<<"startIndex=#{options[:start_index]}"
127
+ end
128
+ params
129
+ end
130
+
131
+ def filter_options(options={})
132
+ return [] if options.nil? || options.empty?
133
+ options[:op] ||= "equals"
134
+ to_query_list(options,"filter")
135
+ end
136
+
137
+ def to_query_list(params,pre_fix='')
138
+ params.collect{ |k,v| "#{pre_fix}#{k.to_s.capitalize}=#{URI.escape(v.to_s)}"}
139
+ end
140
+ end
141
+
142
+ class Person
143
+
144
+ # Encapsulates a person. Each of the portable contact and opensocial contact fields has a rubyfied (underscored) accessor method.
145
+ #
146
+ # @person = @person.display_name
147
+ #
148
+
149
+ def initialize(data={})
150
+ @data=data
151
+ end
152
+
153
+ SINGULAR_FIELDS = [
154
+ # Portable contacts singular fields
155
+ :id, :display_name, :name, :nickname, :published, :updated, :birthday, :anniversary,
156
+ :gender, :note, :preferred_username, :utc_offset, :connected,
157
+
158
+ # OpenSocial singular fields
159
+ :about_me, :body_type, :current_location, :drinker, :ethnicity, :fashion, :happiest_when,
160
+ :humor, :living_arrangement, :looking_for, :profile_song, :profile_video, :relationship_status,
161
+ :religion, :romance, :scared_of, :sexual_orientation, :smoker, :status
162
+ ]
163
+ PLURAL_FIELDS = [
164
+ # Portable contacts plural fields
165
+ :emails, :urls, :phone_numbers, :ims, :photos, :tags, :relationships, :addresses,
166
+ :organizations, :accounts,
167
+
168
+ # OpenSocial plural fields
169
+ :activities, :books, :cars, :children, :food, :heroes, :interests, :job_interests,
170
+ :languages, :languages_spoken, :movies, :music, :pets, :political_views, :quotes,
171
+ :sports, :turn_offs, :turn_ons, :tv_shows
172
+ ]
173
+
174
+ ENTRY_FIELDS = SINGULAR_FIELDS + PLURAL_FIELDS
175
+
176
+
177
+ def [](key)
178
+ @data[key.to_s.camelize(:lower)]
179
+ end
180
+
181
+ # primary email address
182
+ def email
183
+ @email||= begin
184
+ (emails.detect {|e| e['primary']=='true '} || emails.first)["value"] unless emails.empty?
185
+ end
186
+ end
187
+
188
+ def id
189
+ self["id"]
190
+ end
191
+
192
+ protected
193
+
194
+ def method_missing(method,*args)
195
+ if respond_to?(method)
196
+ return self[method]
197
+ end
198
+ super
199
+ end
200
+
201
+ def respond_to?(method)
202
+ ENTRY_FIELDS.include?(method) || @data.has_key?(method.to_s.camelize(:lower)) || super
203
+ end
204
+ end
205
+
206
+ class Collection < Array
207
+ attr_reader :total_entries, :per_page, :start_index
208
+
209
+ def initialize(data)
210
+ super data["entry"].collect{|e| PortableContacts::Person.new(e) }
211
+ @total_entries=data["totalResults"].to_i
212
+ @per_page=data["itemsPerPage"].to_i
213
+ @start_index=data["startIndex"].to_i
214
+ end
215
+
216
+ end
217
+
218
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__),'portable_contacts')
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{portablecontacts}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Pelle Braendgaard"]
12
+ s.date = %q{2009-10-02}
13
+ s.description = %q{A client library for the portable contacts standard}
14
+ s.email = %q{pelleb@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/portable_contacts.rb",
27
+ "lib/portablecontacts.rb",
28
+ "portablecontacts.gemspec",
29
+ "spec/fixtures/multiple.json",
30
+ "spec/fixtures/single.json",
31
+ "spec/portable_contacts_spec.rb",
32
+ "spec/spec_helper.rb"
33
+ ]
34
+ s.homepage = %q{http://github.com/pelle/portablecontacts}
35
+ s.rdoc_options = ["--charset=UTF-8"]
36
+ s.require_paths = ["lib"]
37
+ s.rubyforge_project = %q{portablecontact}
38
+ s.rubygems_version = %q{1.3.5}
39
+ s.summary = %q{Portable Contacts client for Ruby}
40
+ s.test_files = [
41
+ "spec/portable_contacts_spec.rb",
42
+ "spec/spec_helper.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<oauth>, [">= 0.3.6"])
51
+ s.add_runtime_dependency(%q<json>, [">= 0"])
52
+ s.add_development_dependency(%q<rspec>, [">= 0"])
53
+ else
54
+ s.add_dependency(%q<oauth>, [">= 0.3.6"])
55
+ s.add_dependency(%q<json>, [">= 0"])
56
+ s.add_dependency(%q<rspec>, [">= 0"])
57
+ end
58
+ else
59
+ s.add_dependency(%q<oauth>, [">= 0.3.6"])
60
+ s.add_dependency(%q<json>, [">= 0"])
61
+ s.add_dependency(%q<rspec>, [">= 0"])
62
+ end
63
+ end
@@ -0,0 +1,96 @@
1
+ {
2
+ "startIndex": 10,
3
+ "itemsPerPage": 10,
4
+ "totalResults": 12,
5
+ "entry": [
6
+ {
7
+ "id": "123",
8
+ "displayName": "Minimal Contact"
9
+ },
10
+ {
11
+ "id": "703887",
12
+ "displayName": "Mork Hashimoto",
13
+ "name": {
14
+ "familyName": "Hashimoto",
15
+ "givenName": "Mork"
16
+ },
17
+ "birthday": "0000-01-16",
18
+ "gender": "male",
19
+ "drinker": "heavily",
20
+ "tags": [
21
+ "plaxo guy",
22
+ "favorite"
23
+ ],
24
+ "emails": [
25
+ {
26
+ "value": "mhashimoto-04@plaxo.com",
27
+ "type": "work",
28
+ "primary": "true"
29
+ },
30
+ {
31
+ "value": "mhashimoto-04@plaxo.com",
32
+ "type": "home"
33
+ },
34
+ {
35
+ "value": "mhashimoto@plaxo.com",
36
+ "type": "home"
37
+ }
38
+ ],
39
+ "urls": [
40
+ {
41
+ "value": "http://www.seeyellow.com",
42
+ "type": "work"
43
+ },
44
+ {
45
+ "value": "http://www.angryalien.com",
46
+ "type": "home"
47
+ }
48
+ ],
49
+ "phoneNumbers": [
50
+ {
51
+ "value": "KLONDIKE5",
52
+ "type": "work"
53
+ },
54
+ {
55
+ "value": "650-123-4567",
56
+ "type": "mobile"
57
+ }
58
+ ],
59
+ "photos": [
60
+ {
61
+ "value": "http://sample.site.org/photos/12345.jpg",
62
+ "type": "thumbnail"
63
+ }
64
+ ],
65
+ "ims": [
66
+ {
67
+ "value": "plaxodev8",
68
+ "type": "aim"
69
+ }
70
+ ],
71
+ "addresses": [
72
+ {
73
+ "type": "home",
74
+ "streetAddress": "742 Evergreen Terrace\nSuite 123",
75
+ "locality": "Springfield",
76
+ "region": "VT",
77
+ "postalCode": "12345",
78
+ "country": "USA",
79
+ "formatted": "742 Evergreen Terrace\nSuite 123\nSpringfield, VT 12345 USA"
80
+ }
81
+ ],
82
+ "organizations": [
83
+ {
84
+ "name": "Burns Worldwide",
85
+ "title": "Head Bee Guy"
86
+ }
87
+ ],
88
+ "accounts": [
89
+ {
90
+ "domain": "plaxo.com",
91
+ "userid": "2706"
92
+ }
93
+ ]
94
+ }
95
+ ]
96
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "entry":
3
+ {
4
+ "id": "703887",
5
+ "displayName": "Mork Hashimoto",
6
+ "name": {
7
+ "familyName": "Hashimoto",
8
+ "givenName": "Mork"
9
+ },
10
+ "birthday": "0000-01-16",
11
+ "gender": "male",
12
+ "drinker": "heavily",
13
+ "tags": [
14
+ "plaxo guy",
15
+ "favorite"
16
+ ],
17
+ "emails": [
18
+ {
19
+ "value": "mhashimoto-04@plaxo.com",
20
+ "type": "work",
21
+ "primary": "true"
22
+ },
23
+ {
24
+ "value": "mhashimoto-04@plaxo.com",
25
+ "type": "home"
26
+ },
27
+ {
28
+ "value": "mhashimoto@plaxo.com",
29
+ "type": "home"
30
+ }
31
+ ],
32
+ "urls": [
33
+ {
34
+ "value": "http://www.seeyellow.com",
35
+ "type": "work"
36
+ },
37
+ {
38
+ "value": "http://www.angryalien.com",
39
+ "type": "home"
40
+ }
41
+ ],
42
+ "phoneNumbers": [
43
+ {
44
+ "value": "KLONDIKE5",
45
+ "type": "work"
46
+ },
47
+ {
48
+ "value": "650-123-4567",
49
+ "type": "mobile"
50
+ }
51
+ ],
52
+ "photos": [
53
+ {
54
+ "value": "http://sample.site.org/photos/12345.jpg",
55
+ "type": "thumbnail"
56
+ }
57
+ ],
58
+ "ims": [
59
+ {
60
+ "value": "plaxodev8",
61
+ "type": "aim"
62
+ }
63
+ ],
64
+ "addresses": [
65
+ {
66
+ "type": "home",
67
+ "streetAddress": "742 Evergreen Terrace\nSuite 123",
68
+ "locality": "Springfield",
69
+ "region": "VT",
70
+ "postalCode": "12345",
71
+ "country": "USA",
72
+ "formatted": "742 Evergreen Terrace\nSuite 123\nSpringfield, VT 12345 USA"
73
+ }
74
+ ],
75
+ "organizations": [
76
+ {
77
+ "name": "Burns Worldwide",
78
+ "title": "Head Bee Guy"
79
+ }
80
+ ],
81
+ "accounts": [
82
+ {
83
+ "domain": "plaxo.com",
84
+ "userid": "2706"
85
+ }
86
+ ]
87
+ }
88
+ }
@@ -0,0 +1,234 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe PortableContacts::Client do
4
+
5
+ def access_token
6
+ @access_token ||= mock("accesstoken")
7
+ end
8
+
9
+ before(:each) do
10
+ @client = PortableContacts::Client.new "http://sample.com/portable_contacts", access_token
11
+ end
12
+
13
+ ["/@me/@all", "/@me/@all/(id)", "/@me/@self"].each do |path|
14
+
15
+ it "should generate url for #{path}" do
16
+ @client.send( :url_for, path).should=="http://sample.com/portable_contacts#{path}"
17
+ end
18
+
19
+ end
20
+
21
+ it "should return empty string for no options" do
22
+ @client.send(:options_for).should==""
23
+ end
24
+
25
+ describe "fields" do
26
+ it "should handle all" do
27
+ @client.send(:options_for, :fields=>:all).should=="?fields=@all"
28
+ end
29
+
30
+ it "should handle single field" do
31
+ @client.send(:options_for, :fields=>:name).should=="?fields=name"
32
+ end
33
+
34
+ it "should handle array of fields" do
35
+ @client.send(:options_for, :fields=>[:name,:emails,:nickname,:id]).should=="?fields=name,emails,nickname,id"
36
+ end
37
+
38
+ end
39
+
40
+ describe "filtering" do
41
+
42
+ it "should handle default operation" do
43
+ @client.send(:options_for, :filter=>{:by=>:name,:value=>"Bob"}).should=="?filterBy=name&filterOp=equals&filterValue=Bob"
44
+ end
45
+
46
+ ["displayName","id","nickname"].each do |field|
47
+ ["equals", "contains", "startsWith", "present"].each do |op|
48
+
49
+ it "should create filter for #{field} with #{op}" do
50
+ @client.send(:options_for, :filter=>{:by=>field, :op=>op, :value=>"bb"}).should=="?filterBy=#{field}&filterOp=#{op}&filterValue=bb"
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
57
+ describe "sorting" do
58
+
59
+ it "should handle straight sort" do
60
+ @client.send(:options_for, :sort=>:name).should=="?sortBy=name"
61
+ end
62
+
63
+ it "should handle sort as a hash" do
64
+ @client.send(:options_for, :sort=>{:by=>:name}).should=="?sortBy=name"
65
+ end
66
+
67
+ it "should handle sort as a hash" do
68
+ @client.send(:options_for, :sort=>{:by=>:name,:order=>:descending}).should=="?sortBy=name&sortOrder=descending"
69
+ end
70
+
71
+ end
72
+
73
+ describe "paging" do
74
+
75
+ it "should handle count" do
76
+ @client.send(:options_for, :count=>133).should=="?count=133"
77
+ end
78
+
79
+ it "should handle startIndex" do
80
+ @client.send(:options_for, :start_index=>20).should=="?startIndex=20"
81
+ end
82
+
83
+ it "should handle count with startIndex" do
84
+ @client.send(:options_for, :count=>10,:start_index=>40).should=="?count=10&startIndex=40"
85
+ end
86
+
87
+ end
88
+
89
+ it "should handle all parameters at once" do
90
+ @client.send(:options_for,
91
+ :fields=>[:name,:emails,:nickname,:id],
92
+ :filter=>{:by=>:name,:value=>"Bob"},
93
+ :sort=>{:by=>:name,:order=>:descending},
94
+ :count=>10,:start_index=>40).should=="?count=10&fields=name,emails,nickname,id&filterBy=name&filterOp=equals&filterValue=Bob&sortBy=name&sortOrder=descending&startIndex=40"
95
+ end
96
+
97
+ describe "Rails Specific" do
98
+ require 'activesupport'
99
+
100
+ it "should handle all parameters at once with string keys" do
101
+ @client.send(:options_for,
102
+ 'fields'=>['name','emails','nickname','id'],
103
+ 'filter'=>{'by'=>'name','value'=>"Bob"},
104
+ 'sort'=>{'by'=>'name','order'=>'descending'},
105
+ 'count'=>10,'start_index'=>40).should=="?count=10&fields=name,emails,nickname,id&filterBy=name&filterOp=equals&filterValue=Bob&sortBy=name&sortOrder=descending&startIndex=40"
106
+ end
107
+ end
108
+
109
+ describe "parsing" do
110
+
111
+ def parse_json_file(name)
112
+ File.open File.join(File.dirname(__FILE__),'fixtures', name) do |f|
113
+ JSON.parse f.read
114
+ end
115
+ end
116
+
117
+ describe "single entry response" do
118
+ before(:each) do
119
+ @entry = @client.send(:single,parse_json_file('single.json'))
120
+ end
121
+
122
+ it "should contain displayName" do
123
+ @entry.display_name.should=="Mork Hashimoto"
124
+ end
125
+
126
+ it "should contain tags" do
127
+ @entry.tags.should == [
128
+ "plaxo guy",
129
+ "favorite"
130
+ ]
131
+ end
132
+
133
+ it "should emails" do
134
+ @entry.emails.should == [
135
+ {
136
+ "value"=> "mhashimoto-04@plaxo.com",
137
+ "type"=> "work",
138
+ "primary"=> "true"
139
+ },
140
+ {
141
+ "value"=> "mhashimoto-04@plaxo.com",
142
+ "type"=> "home"
143
+ },
144
+ {
145
+ "value"=> "mhashimoto@plaxo.com",
146
+ "type"=> "home"
147
+ }
148
+ ]
149
+ end
150
+
151
+ it "should have email" do
152
+ @entry.email.should=="mhashimoto-04@plaxo.com"
153
+ end
154
+ end
155
+
156
+ describe "multiple entries" do
157
+ before(:each) do
158
+ @entries = @client.send(:collection,parse_json_file('multiple.json'))
159
+ end
160
+
161
+ it "should have correct start index" do
162
+ @entries.start_index.should == 10
163
+ end
164
+
165
+ it "should have correct per page" do
166
+ @entries.per_page.should == 10
167
+ end
168
+
169
+ it "should have correct total entries" do
170
+ @entries.total_entries.should == 12
171
+ end
172
+
173
+ it "should have 2 enties" do
174
+ @entries.length.should==2
175
+ end
176
+
177
+ describe "first entry" do
178
+
179
+ before(:each) do
180
+ @entry=@entries.first
181
+ end
182
+
183
+ it "should have correct id" do
184
+ @entry.id.should=="123"
185
+ end
186
+
187
+ it "should contain displayName" do
188
+ @entry.display_name.should=="Minimal Contact"
189
+ end
190
+
191
+ end
192
+
193
+ # The same as data in single entry
194
+ describe "last entry" do
195
+
196
+ before(:each) do
197
+ @entry=@entries.last
198
+ end
199
+ it "should contain displayName" do
200
+ @entry.display_name.should=="Mork Hashimoto"
201
+ end
202
+
203
+ it "should contain tags" do
204
+ @entry.tags.should == [
205
+ "plaxo guy",
206
+ "favorite"
207
+ ]
208
+ end
209
+
210
+ it "should emails" do
211
+ @entry.emails.should == [
212
+ {
213
+ "value"=> "mhashimoto-04@plaxo.com",
214
+ "type"=> "work",
215
+ "primary"=> "true"
216
+ },
217
+ {
218
+ "value"=> "mhashimoto-04@plaxo.com",
219
+ "type"=> "home"
220
+ },
221
+ {
222
+ "value"=> "mhashimoto@plaxo.com",
223
+ "type"=> "home"
224
+ }
225
+ ]
226
+ end
227
+
228
+ it "should have email" do
229
+ @entry.email.should=="mhashimoto-04@plaxo.com"
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'portablecontacts'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: portablecontacts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Pelle Braendgaard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-02 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: oauth
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.3.6
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rspec
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: A client library for the portable contacts standard
46
+ email: pelleb@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - .document
56
+ - .gitignore
57
+ - LICENSE
58
+ - README.rdoc
59
+ - Rakefile
60
+ - VERSION
61
+ - lib/portable_contacts.rb
62
+ - lib/portablecontacts.rb
63
+ - portablecontacts.gemspec
64
+ - spec/fixtures/multiple.json
65
+ - spec/fixtures/single.json
66
+ - spec/portable_contacts_spec.rb
67
+ - spec/spec_helper.rb
68
+ has_rdoc: true
69
+ homepage: http://github.com/pelle/portablecontacts
70
+ licenses: []
71
+
72
+ post_install_message:
73
+ rdoc_options:
74
+ - --charset=UTF-8
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: "0"
88
+ version:
89
+ requirements: []
90
+
91
+ rubyforge_project: portablecontact
92
+ rubygems_version: 1.3.5
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Portable Contacts client for Ruby
96
+ test_files:
97
+ - spec/portable_contacts_spec.rb
98
+ - spec/spec_helper.rb