portablecontacts 0.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/.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