muck-portablecontacts 0.2.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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
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.
@@ -0,0 +1,53 @@
1
+ = Portable Contacts
2
+
3
+ This is a slightly modified fork of the original project primarily using changes from: https://github.com/kickstarter/portablecontacts.
4
+ To install this gem use:
5
+
6
+ gem install muck-portablecontacts
7
+
8
+ This is a ruby client implementation of Portable Contacts a standard for exchanging profile and address book data.
9
+
10
+ The current draft of the standard is http://portablecontacts.net/draft-spec.html
11
+
12
+ Portable Contacts is currently supported by Plaxo, Google, Yahoo, and most OpenSocial containers.
13
+
14
+ This client uses OAuth and JSON exclusively. There are no plans for supporting basic authentication or xml.
15
+
16
+ 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
17
+
18
+ The Gem requires:
19
+
20
+ * ActiveSupport
21
+ * OAuth Gem
22
+ * JSON gem
23
+
24
+ == Example Code
25
+
26
+ @access_token = ... # instantiate access token
27
+
28
+ @client = PortableContacts::Client.new "http://www-opensocial.googleusercontent.com/api/people", @access_token
29
+
30
+ # Get users profile
31
+
32
+ @profile = @client.me
33
+
34
+ puts @profile.display_name
35
+ => "Bob Sample"
36
+
37
+ # Get users contacts
38
+ @contacts = @client.all
39
+
40
+ == Note on Patches/Pull Requests
41
+
42
+ * Fork the project.
43
+ * Make your feature addition or bug fix.
44
+ * Add tests for it. This is important so I don't break it in a
45
+ future version unintentionally.
46
+ * Commit, do not mess with rakefile, version, or history.
47
+ (if you want to have your own version, that is fine but
48
+ bump version in a commit by itself I can ignore when I pull)
49
+ * Send me a pull request. Bonus points for topic branches.
50
+
51
+ == Copyright
52
+
53
+ Copyright (c) 2009 Pelle Braendgaard. See LICENSE for details.
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "muck-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 "activesupport"
15
+ gem.add_dependency('oauth', '>= 0.3.6')
16
+ gem.add_dependency('json')
17
+ gem.add_development_dependency "rspec"
18
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
19
+ end
20
+ Jeweler::GemcutterTasks.new
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
23
+ end
24
+
25
+ require 'spec/rake/spectask'
26
+ Spec::Rake::SpecTask.new(:spec) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.spec_files = FileList['spec/**/*_spec.rb']
29
+ end
30
+
31
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.pattern = 'spec/**/*_spec.rb'
34
+ spec.rcov = true
35
+ end
36
+
37
+ task :spec => :check_dependencies
38
+
39
+ task :default => :spec
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ if File.exist?('VERSION')
44
+ version = File.read('VERSION')
45
+ else
46
+ version = ""
47
+ end
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "portablecontacts #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,228 @@
1
+ require 'uri'
2
+ require 'json'
3
+
4
+ begin
5
+ require 'active_support/core_ext/string' # Rails 3+
6
+ rescue LoadError
7
+ require 'activesupport' # older versions of Rails
8
+ end
9
+
10
+ module PortableContacts
11
+
12
+ # This is the main PortableContacts Client.
13
+ #
14
+ # == query options
15
+ #
16
+ # The library supports the various filter and sorting parameters. The format of this may change for this library, so limit use to :
17
+ # :count and :start_index for now.
18
+ #
19
+ class Client
20
+ attr :base_url, :access_token
21
+
22
+ # First parameter is the portable contacts base_url. Find this on your PortableContact providers documentation or through XRDS.
23
+ #
24
+ # * Google's is http://www-opensocial.googleusercontent.com/api/people
25
+ # * Yahoo's is http://appstore.apps.yahooapis.com/social/rest/people
26
+ #
27
+ # The second parameter is an OAuth::AccessToken instantiated for the provider.
28
+ #
29
+ def initialize(base_url, access_token)
30
+ @base_url = base_url
31
+ @access_token = access_token
32
+ end
33
+
34
+ # Returns the AccessToken users contact details. Note this requests all fields from provider
35
+ # Returns an PortableContacts::Person object
36
+ def me(options={})
37
+ single(get("/@me/@self", options.reverse_merge(:fields => :all)))
38
+ end
39
+
40
+ # Returns the contacts of the user. It defaults to all fields and 100 entries
41
+ #
42
+ # @contacts = @client.all # return 100 contacts
43
+ # @contacts = @client.all :count=>10 # return 10 contacts
44
+ # @contacts = @client.all :count=>10, :start_index=>10 # returns the second page of 10 contacts
45
+ # puts @contacts.total_entries # returns the total amount of contacts on the server
46
+ #
47
+ # Returns a PortableContacts::Collection which is a subclass of Array
48
+ def all(options={})
49
+ collection(get("/@me/@all", options.reverse_merge(:fields => :all, :count => 100)))
50
+ end
51
+
52
+ # Returns the full contact infor for a particular userid. TODO This is not tested well
53
+ # Returns an PortableContacts::Person object
54
+ def find(id, options={})
55
+ single(get("/@me/@all/#{id}", options.reverse_merge(:fields => :all)))
56
+ end
57
+
58
+ private
59
+
60
+ def get(path, options={})
61
+ parse(@access_token.get(url_for(path) + options_for(options), {'Accept' => 'application/json'}))
62
+ end
63
+
64
+ def url_for(path)
65
+ "#{@base_url}#{path}"
66
+ end
67
+
68
+ def options_for(options={})
69
+ return "" if options.nil? || options.empty?
70
+ options.symbolize_keys! if options.respond_to? :symbolize_keys!
71
+ "?#{(fields_options(options[:fields]) + filter_options(options[:filter]) + sort_options(options[:sort]) + pagination_options(options)).sort.join("&")}"
72
+ end
73
+
74
+ def single(data)
75
+ if data.is_a?(Hash) && data['entry']
76
+ PortableContacts::Person.new(data['entry'])
77
+ else
78
+ data
79
+ end
80
+ end
81
+
82
+ def collection(data)
83
+ if data.is_a?(Hash)
84
+ PortableContacts::Collection.new data
85
+ else
86
+ data
87
+ end
88
+ end
89
+
90
+ def parse(response)
91
+ return false unless response
92
+ if ["200","201"].include? response.code
93
+ unless response.body.blank?
94
+ JSON.parse(response.body)
95
+ else
96
+ true
97
+ end
98
+ else
99
+ false
100
+ end
101
+ end
102
+
103
+ def fields_options(options = nil)
104
+ return [] if options.nil?
105
+ if options.is_a? Symbol
106
+ return ["fields=#{(options == :all ? "@all": URI.escape(options.to_s))}"]
107
+ elsif options.respond_to?(:collect)
108
+ ["fields=#{options.collect{|f| f.to_s}.join(',')}"]
109
+ else
110
+ []
111
+ end
112
+ end
113
+
114
+ def sort_options(options = nil)
115
+ return [] if options.nil?
116
+ if options.is_a? Symbol
117
+ return ["sortBy=#{URI.escape(options.to_s)}"]
118
+ end
119
+ if options.is_a?(Hash) and options[:by] || options['by']
120
+ return to_query_list(options, "sort")
121
+ end
122
+ return []
123
+ end
124
+
125
+ def pagination_options(options = nil)
126
+ return [] if options.nil? || options.empty?
127
+ params = []
128
+ if options[:count]
129
+ params << "count=#{options[:count]}"
130
+ end
131
+ if options[:start_index]
132
+ params << "startIndex=#{options[:start_index]}"
133
+ end
134
+ params
135
+ end
136
+
137
+ def filter_options(options = {})
138
+ return [] if options.nil? || options.empty?
139
+ options[:op] ||= "equals"
140
+ to_query_list(options, "filter")
141
+ end
142
+
143
+ def to_query_list(params, pre_fix = '')
144
+ params.collect{ |k,v| "#{pre_fix}#{k.to_s.capitalize}=#{URI.escape(v.to_s)}"}
145
+ end
146
+ end
147
+
148
+ class Person
149
+
150
+ # Encapsulates a person. Each of the portable contact and opensocial contact fields has a rubyfied (underscored) accessor method.
151
+ #
152
+ # @person = @person.display_name
153
+ #
154
+
155
+ def initialize(data={})
156
+ @data = data
157
+ end
158
+
159
+ SINGULAR_FIELDS = [
160
+ # Portable contacts singular fields
161
+ :id, :display_name, :name, :nickname, :published, :updated, :birthday, :anniversary,
162
+ :gender, :note, :preferred_username, :utc_offset, :connected,
163
+
164
+ # OpenSocial singular fields
165
+ :about_me, :body_type, :current_location, :drinker, :ethnicity, :fashion, :happiest_when,
166
+ :humor, :living_arrangement, :looking_for, :profile_song, :profile_video, :relationship_status,
167
+ :religion, :romance, :scared_of, :sexual_orientation, :smoker, :status
168
+ ]
169
+ PLURAL_FIELDS = [
170
+ # Portable contacts plural fields
171
+ :emails, :urls, :phone_numbers, :ims, :photos, :tags, :relationships, :addresses,
172
+ :organizations, :accounts,
173
+
174
+ # OpenSocial plural fields
175
+ :activities, :books, :cars, :children, :food, :heroes, :interests, :job_interests,
176
+ :languages, :languages_spoken, :movies, :music, :pets, :political_views, :quotes,
177
+ :sports, :turn_offs, :turn_ons, :tv_shows
178
+ ]
179
+
180
+ ENTRY_FIELDS = SINGULAR_FIELDS + PLURAL_FIELDS
181
+
182
+ def self.define_getter(field)
183
+ class_eval <<-END, __FILE__, __LINE__
184
+ def #{field}
185
+ @data['#{field.to_s.camelize(:lower)}']
186
+ end
187
+ END
188
+ end
189
+
190
+ ENTRY_FIELDS.each do |f|
191
+ define_getter(f)
192
+ end
193
+
194
+ def [](key)
195
+ @data[key.to_s.camelize(:lower)]
196
+ end
197
+
198
+ # primary email address
199
+ def email
200
+ @email ||= begin
201
+ (emails.detect {|e| e['primary'] == 'true'} || emails.first)["value"] unless emails.blank?
202
+ end
203
+ end
204
+
205
+ protected
206
+
207
+ def method_missing(method, *args)
208
+ if @data.has_key?(method.to_s.camelize(:lower))
209
+ self.class.define_getter(method)
210
+ return send(method)
211
+ end
212
+ super
213
+ end
214
+ end
215
+
216
+ class Collection < Array
217
+ attr_reader :total_entries, :per_page, :start_index
218
+
219
+ def initialize(data)
220
+ super data["entry"].collect{|e| PortableContacts::Person.new(e) }
221
+ @total_entries = data["totalResults"].to_i
222
+ @per_page = data["itemsPerPage"].to_i
223
+ @start_index = data["startIndex"].to_i
224
+ end
225
+
226
+ end
227
+
228
+ end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__),'portable_contacts')
@@ -0,0 +1,64 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
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{muck-portablecontacts}
8
+ s.version = "0.2.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{2011-04-21}
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
+ "LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "lib/portable_contacts.rb",
26
+ "lib/portablecontacts.rb",
27
+ "muck-portablecontacts.gemspec",
28
+ "spec/fixtures/multiple.json",
29
+ "spec/fixtures/single.json",
30
+ "spec/portable_contacts_spec.rb",
31
+ "spec/spec_helper.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/pelle/portablecontacts}
34
+ s.require_paths = ["lib"]
35
+ s.rubyforge_project = %q{portablecontact}
36
+ s.rubygems_version = %q{1.6.0}
37
+ s.summary = %q{Portable Contacts client for Ruby}
38
+ s.test_files = [
39
+ "spec/portable_contacts_spec.rb",
40
+ "spec/spec_helper.rb"
41
+ ]
42
+
43
+ if s.respond_to? :specification_version then
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
48
+ s.add_runtime_dependency(%q<oauth>, [">= 0.3.6"])
49
+ s.add_runtime_dependency(%q<json>, [">= 0"])
50
+ s.add_development_dependency(%q<rspec>, [">= 0"])
51
+ else
52
+ s.add_dependency(%q<activesupport>, [">= 0"])
53
+ s.add_dependency(%q<oauth>, [">= 0.3.6"])
54
+ s.add_dependency(%q<json>, [">= 0"])
55
+ s.add_dependency(%q<rspec>, [">= 0"])
56
+ end
57
+ else
58
+ s.add_dependency(%q<activesupport>, [">= 0"])
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
64
+
@@ -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-work@plaxo.com",
20
+ "type": "work"
21
+ },
22
+ {
23
+ "value": "mhashimoto-home@plaxo.com",
24
+ "type": "home"
25
+ },
26
+ {
27
+ "value": "mhashimoto@plaxo.com",
28
+ "type": "home",
29
+ "primary": "true"
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,232 @@
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
+ it "should handle all parameters at once with string keys" do
99
+ @client.send(:options_for,
100
+ 'fields'=>['name','emails','nickname','id'],
101
+ 'filter'=>{'by'=>'name','value'=>"Bob"},
102
+ 'sort'=>{'by'=>'name','order'=>'descending'},
103
+ '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"
104
+ end
105
+ end
106
+
107
+ describe "parsing" do
108
+
109
+ def parse_json_file(name)
110
+ File.open File.join(File.dirname(__FILE__),'fixtures', name) do |f|
111
+ JSON.parse f.read
112
+ end
113
+ end
114
+
115
+ describe "single entry response" do
116
+ before(:each) do
117
+ @entry = @client.send(:single,parse_json_file('single.json'))
118
+ end
119
+
120
+ it "should contain displayName" do
121
+ @entry.display_name.should=="Mork Hashimoto"
122
+ end
123
+
124
+ it "should contain tags" do
125
+ @entry.tags.should == [
126
+ "plaxo guy",
127
+ "favorite"
128
+ ]
129
+ end
130
+
131
+ it "should emails" do
132
+ @entry.emails.should == [
133
+ {
134
+ "value"=> "mhashimoto-work@plaxo.com",
135
+ "type"=> "work"
136
+ },
137
+ {
138
+ "value"=> "mhashimoto-home@plaxo.com",
139
+ "type"=> "home"
140
+ },
141
+ {
142
+ "value"=> "mhashimoto@plaxo.com",
143
+ "type"=> "home",
144
+ "primary"=> "true"
145
+ }
146
+ ]
147
+ end
148
+
149
+ it "should have email" do
150
+ @entry.email.should=="mhashimoto@plaxo.com"
151
+ end
152
+ end
153
+
154
+ describe "multiple entries" do
155
+ before(:each) do
156
+ @entries = @client.send(:collection,parse_json_file('multiple.json'))
157
+ end
158
+
159
+ it "should have correct start index" do
160
+ @entries.start_index.should == 10
161
+ end
162
+
163
+ it "should have correct per page" do
164
+ @entries.per_page.should == 10
165
+ end
166
+
167
+ it "should have correct total entries" do
168
+ @entries.total_entries.should == 12
169
+ end
170
+
171
+ it "should have 2 enties" do
172
+ @entries.length.should==2
173
+ end
174
+
175
+ describe "first entry" do
176
+
177
+ before(:each) do
178
+ @entry=@entries.first
179
+ end
180
+
181
+ it "should have correct id" do
182
+ @entry.id.should=="123"
183
+ end
184
+
185
+ it "should contain displayName" do
186
+ @entry.display_name.should=="Minimal Contact"
187
+ end
188
+
189
+ end
190
+
191
+ # The same as data in single entry
192
+ describe "last entry" do
193
+
194
+ before(:each) do
195
+ @entry=@entries.last
196
+ end
197
+ it "should contain displayName" do
198
+ @entry.display_name.should=="Mork Hashimoto"
199
+ end
200
+
201
+ it "should contain tags" do
202
+ @entry.tags.should == [
203
+ "plaxo guy",
204
+ "favorite"
205
+ ]
206
+ end
207
+
208
+ it "should emails" do
209
+ @entry.emails.should == [
210
+ {
211
+ "value"=> "mhashimoto-04@plaxo.com",
212
+ "type"=> "work",
213
+ "primary"=> "true"
214
+ },
215
+ {
216
+ "value"=> "mhashimoto-04@plaxo.com",
217
+ "type"=> "home"
218
+ },
219
+ {
220
+ "value"=> "mhashimoto@plaxo.com",
221
+ "type"=> "home"
222
+ }
223
+ ]
224
+ end
225
+
226
+ it "should have email" do
227
+ @entry.email.should=="mhashimoto-04@plaxo.com"
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,11 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rubygems'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ require 'portablecontacts'
8
+
9
+ Spec::Runner.configure do |config|
10
+
11
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: muck-portablecontacts
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Pelle Braendgaard
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-21 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activesupport
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: oauth
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 31
44
+ segments:
45
+ - 0
46
+ - 3
47
+ - 6
48
+ version: 0.3.6
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: json
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rspec
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ description: A client library for the portable contacts standard
80
+ email: pelleb@gmail.com
81
+ executables: []
82
+
83
+ extensions: []
84
+
85
+ extra_rdoc_files:
86
+ - LICENSE
87
+ - README.rdoc
88
+ files:
89
+ - .document
90
+ - LICENSE
91
+ - README.rdoc
92
+ - Rakefile
93
+ - VERSION
94
+ - lib/portable_contacts.rb
95
+ - lib/portablecontacts.rb
96
+ - muck-portablecontacts.gemspec
97
+ - spec/fixtures/multiple.json
98
+ - spec/fixtures/single.json
99
+ - spec/portable_contacts_spec.rb
100
+ - spec/spec_helper.rb
101
+ has_rdoc: true
102
+ homepage: http://github.com/pelle/portablecontacts
103
+ licenses: []
104
+
105
+ post_install_message:
106
+ rdoc_options: []
107
+
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 3
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ requirements: []
129
+
130
+ rubyforge_project: portablecontact
131
+ rubygems_version: 1.6.0
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: Portable Contacts client for Ruby
135
+ test_files:
136
+ - spec/portable_contacts_spec.rb
137
+ - spec/spec_helper.rb