google_r 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,58 @@
1
+ require 'ostruct'
2
+
3
+ class GoogleR::Calendar
4
+ attr_accessor :google_id, :etag, :summary, :description, :time_zone
5
+
6
+ def self.url
7
+ "https://www.googleapis.com"
8
+ end
9
+
10
+ def self.api_headers
11
+ {
12
+ 'GData-Version' => '3.0',
13
+ 'Content-Type' => 'application/json',
14
+ }
15
+ end
16
+
17
+ def self.path
18
+ "/calendar/v3/users/me/calendarList"
19
+ end
20
+
21
+ def path
22
+ if new?
23
+ "/calendar/v3/calendars"
24
+ else
25
+ "/calendar/v3/calendars/#{google_id}"
26
+ end
27
+ end
28
+
29
+ def self.from_json(json, *attrs)
30
+ if json["kind"] == "calendar#calendar" || json["kind"] == "calendar#calendarListEntry"
31
+ calendar = self.new
32
+ calendar.google_id = json["id"]
33
+ calendar.etag = json["etag"]
34
+ calendar.summary = json["summary"]
35
+ calendar.description = json["description"]
36
+ calendar.time_zone = json["timeZone"]
37
+ calendar
38
+ else
39
+ raise "Not implemented:\n#{json.inspect}"
40
+ end
41
+ end
42
+
43
+ def to_google(yajl_opts = {})
44
+ hash = {
45
+ "kind" => "calendar#calendar",
46
+ }
47
+ hash["etag"] = etag if etag
48
+ hash["id"] = google_id if google_id
49
+ hash["summary"] = summary if summary
50
+ hash["description"] = description if description
51
+ hash["timeZone"] = time_zone if time_zone
52
+ Yajl::Encoder.encode(hash, yajl_opts)
53
+ end
54
+
55
+ def new?
56
+ self.google_id.nil?
57
+ end
58
+ end
@@ -0,0 +1,241 @@
1
+ require 'ostruct'
2
+
3
+ class GoogleR::Contact
4
+ Email = Struct.new(:address, :display_name, :label, :rel, :primary)
5
+ Phone = Struct.new(:rel, :text)
6
+ Organization = Struct.new(:name, :title, :rel)
7
+ Address = Struct.new(:street, :neighborhood, :pobox, :postcode, :city, :region, :country, :rel)
8
+ Website = Struct.new(:href, :rel)
9
+
10
+ attr_reader :emails, :phones, :organizations, :addresses, :groups, :websites
11
+ attr_accessor :given_name, :additional_name, :name_prefix, :name_suffix, :family_name,
12
+ :google_id, :etag, :content, :updated, :user_fields, :nickname
13
+
14
+ def initialize
15
+ @emails = []
16
+ @phones = []
17
+ @organizations = []
18
+ @addresses = []
19
+ @groups = []
20
+ @websites = []
21
+ @user_fields = {}
22
+ end
23
+
24
+ def self.url
25
+ "https://www.google.com"
26
+ end
27
+
28
+ def self.path
29
+ "/m8/feeds/contacts/default/full/"
30
+ end
31
+
32
+ def self.api_headers
33
+ {
34
+ 'GData-Version' => '3.0',
35
+ 'Content-Type' => 'application/atom+xml',
36
+ }
37
+ end
38
+
39
+ def path
40
+ if new?
41
+ self.class.path
42
+ else
43
+ self.class.path + google_id.split("/")[-1]
44
+ end
45
+ end
46
+
47
+ def full_name
48
+ [name_prefix, given_name, additional_name, family_name, name_suffix].compact.join(" ")
49
+ end
50
+
51
+ def to_google
52
+ builder = Nokogiri::XML::Builder.new(:encoding => "UTF-8") do |xml|
53
+ root_attrs = {
54
+ 'xmlns:atom' => 'http://www.w3.org/2005/Atom',
55
+ 'xmlns:gd' => 'http://schemas.google.com/g/2005',
56
+ 'xmlns:gContact' => 'http://schemas.google.com/contact/2008',
57
+ }
58
+ root_attrs["gd:etag"] = self.etag unless new?
59
+ xml.entry(root_attrs) do
60
+ unless new?
61
+ xml.id_ self.google_id
62
+ end
63
+
64
+ xml.updated self.updated.strftime("%Y-%m-%dT%H:%M:%S.%LZ") unless self.updated.nil?
65
+
66
+ if self.full_name != ''
67
+ xml['gd'].name do
68
+ xml['gd'].givenName self.given_name unless self.given_name.nil?
69
+ xml['gd'].additionalName self.additional_name unless self.additional_name.nil?
70
+ xml['gd'].familyName self.family_name unless self.family_name.nil?
71
+ xml['gd'].namePrefix self.name_prefix unless self.name_prefix.nil?
72
+ xml['gd'].nameSuffix self.name_suffix unless self.name_suffix.nil?
73
+ end
74
+ end
75
+
76
+ xml['atom'].content({'type' => 'text'}, self.content) unless self.content.nil?
77
+ xml['gContact'].nickname self.nickname unless self.nickname.nil?
78
+
79
+ phones.each do |phone|
80
+ xml['gd'].phoneNumber({'rel' => phone.rel}, phone.text)
81
+ end
82
+
83
+ emails.each do |email|
84
+ attrs = {'address' => email.address}
85
+ attrs['rel'] = email.rel if email.rel
86
+ attrs['label'] = email.label if email.label
87
+ attrs['primary'] = email.primary
88
+ xml['gd'].email(attrs)
89
+ end
90
+
91
+ organizations.each do |org|
92
+ xml['gd'].organization({:rel => org.rel}) do
93
+ xml['gd'].orgName org.name
94
+ xml['gd'].orgTitle org.title
95
+ end
96
+ end
97
+
98
+ addresses.each do |address|
99
+ xml['gd'].structuredPostalAddress({'rel' => address.rel}) do
100
+ xml['gd'].street address.street unless address.street.nil?
101
+ xml['gd'].neighborhood address.neighborhood unless address.neighborhood.nil?
102
+ xml['gd'].pobox address.pobox unless address.pobox.nil?
103
+ xml['gd'].postcode address.postcode unless address.postcode.nil?
104
+ xml['gd'].city address.city unless address.city.nil?
105
+ xml['gd'].region address.region unless address.region.nil?
106
+ xml['gd'].country address.country unless address.country.nil?
107
+ end
108
+ end
109
+
110
+ user_fields.each do |key, value|
111
+ xml['gContact'].userDefinedField({'key' => key, 'value' => value})
112
+ end
113
+
114
+ websites.each do |website|
115
+ xml['gContact'].website({'href' => website.href, 'rel' => website.rel})
116
+ end
117
+
118
+ groups.each do |group|
119
+ xml['gContact'].groupMembershipInfo({'href' => group.google_id, 'deleted' => 'false'})
120
+ end
121
+
122
+ xml.parent.namespace = xml.parent.namespace_definitions.find { |ns| ns.prefix == "atom" }
123
+ end
124
+ end
125
+ builder.to_xml
126
+ end
127
+
128
+ def add_email(email)
129
+ @emails << email
130
+ end
131
+
132
+ def add_phone(phone)
133
+ @phones << phone
134
+ end
135
+
136
+ def add_organization(organization)
137
+ @organizations << organization
138
+ end
139
+
140
+ def add_address(address)
141
+ @addresses << address
142
+ end
143
+
144
+ def add_group(group)
145
+ @groups << group
146
+ end
147
+
148
+ def add_website(website)
149
+ @websites << website
150
+ end
151
+
152
+ def self.from_xml(doc, *attrs)
153
+ is_collection = doc.search("totalResults").size > 0
154
+ return doc.search("entry").map { |e| from_xml(e) } if is_collection
155
+
156
+ contact = GoogleR::Contact.new
157
+
158
+ google_id = doc.search("id")
159
+
160
+ if google_id.empty?
161
+ contact.etag = contact.google_id = nil
162
+ else
163
+ contact.etag = doc["etag"]
164
+ contact.google_id = google_id.inner_text
165
+ end
166
+
167
+ doc.search("email").each do |email|
168
+ contact.add_email(GoogleR::Contact::Email.new(email[:address], email[:display_name], email[:label], email[:rel], email[:primary] == "true"))
169
+ end
170
+
171
+ doc.search("phoneNumber").each do |phone|
172
+ contact.add_phone(GoogleR::Contact::Phone.new(phone[:rel], phone.inner_text))
173
+ end
174
+
175
+ doc.search("organization").each do |org|
176
+ name = org.search("orgName").inner_text
177
+ title = org.search("orgTitle").inner_text
178
+ rel = org[:rel]
179
+ contact.add_organization(GoogleR::Contact::Organization.new(name, title, rel))
180
+ end
181
+
182
+ doc.search("structuredPostalAddress").each do |address|
183
+ rel = address[:rel]
184
+ street = address.search("street").inner_text
185
+ neighborhood = address.search("neighborhood").inner_text
186
+ pobox = address.search("pobox").inner_text
187
+ postcode = address.search("postcode").inner_text
188
+ city = address.search("city").inner_text
189
+ region = address.search("region").inner_text
190
+ country = address.search("country").inner_text
191
+ contact.add_address(GoogleR::Contact::Address.new(street, neighborhood, pobox, postcode, city, region, country, rel))
192
+ end
193
+
194
+ doc.search("userDefinedField").each do |field|
195
+ contact.user_fields[field[:key]] = field[:value]
196
+ end
197
+
198
+ doc.search("groupMembershipInfo").each do |entry|
199
+ group = GoogleR::Group.new
200
+ group.google_id = entry[:href]
201
+ contact.add_group(group)
202
+ end
203
+
204
+ doc.search("website").each do |entry|
205
+ website = GoogleR::Contact::Website.new
206
+ website.href = entry[:href]
207
+ website.rel = entry[:rel]
208
+ contact.add_website(website)
209
+ end
210
+
211
+ name_prefix = doc.search("name/namePrefix")
212
+ contact.name_prefix = name_prefix.inner_text unless name_prefix.empty?
213
+
214
+ given_name = doc.search("name/givenName")
215
+ contact.given_name = given_name.inner_text unless given_name.empty?
216
+
217
+ additional_name = doc.search("name/additionalName")
218
+ contact.additional_name = additional_name.inner_text unless additional_name.empty?
219
+
220
+ family_name = doc.search("name/familyName")
221
+ contact.family_name = family_name.inner_text unless family_name.empty?
222
+
223
+ name_suffix = doc.search("name/nameSuffix")
224
+ contact.name_suffix = name_suffix.inner_text unless name_suffix.empty?
225
+
226
+ content = doc.search("content")
227
+ contact.content = content.inner_text unless content.empty?
228
+
229
+ updated = doc.search("updated")
230
+ contact.updated = Time.parse(updated.inner_text) unless updated.empty?
231
+
232
+ nickname = doc.search("nickname")
233
+ contact.nickname = nickname.inner_text unless nickname.empty?
234
+
235
+ contact
236
+ end
237
+
238
+ def new?
239
+ self.google_id.nil?
240
+ end
241
+ end
@@ -0,0 +1,99 @@
1
+ require 'ostruct'
2
+
3
+ class GoogleR::Event
4
+ attr_accessor :google_id, :etag, :start_time, :end_time, :calendar, :description, :summary,
5
+ :visibility, :updated, :created, :status, :start_time_zone, :end_time_zone
6
+
7
+ def initialize(calendar, opts = {})
8
+ self.calendar = calendar
9
+ self.visibility = opts[:visibility] || "private"
10
+ end
11
+
12
+ def self.url
13
+ "https://www.googleapis.com"
14
+ end
15
+
16
+ def self.api_headers
17
+ {
18
+ 'GData-Version' => '3.0',
19
+ 'Content-Type' => 'application/json',
20
+ }
21
+ end
22
+
23
+ def path
24
+ if new?
25
+ "/calendar/v3/calendars/#{calendar.google_id}/events"
26
+ else
27
+ "/calendar/v3/calendars/#{calendar.google_id}/events/#{google_id}"
28
+ end
29
+ end
30
+
31
+ def self.path(calendar_google_id)
32
+ "/calendar/v3/calendars/#{calendar_google_id}/events"
33
+ end
34
+
35
+ def self.from_json(json, *attrs)
36
+ calendar = attrs[0].calendar
37
+
38
+ if json["kind"] == "calendar#events"
39
+ (json["items"] || []).map { |e| from_json(e, *attrs) }
40
+ elsif json["kind"] == "calendar#event"
41
+ event = self.new(calendar)
42
+ event.google_id = json["id"]
43
+ event.etag = json["etag"]
44
+ event.description = json["description"]
45
+ event.summary = json["summary"]
46
+ event.visibility = json["visibility"]
47
+ event.status = json["status"]
48
+
49
+ start_time = json["start"]["dateTime"] || json["start"]["date"]
50
+ if start_time
51
+ event.start_time = Time.parse(start_time)
52
+ event.start_time_zone = json["start"]["timeZone"]
53
+ end
54
+
55
+ end_time = json["end"]["dateTime"] || json["end"]["date"]
56
+ if end_time
57
+ event.end_time = Time.parse(end_time)
58
+ event.end_time_zone = json["end"]["timeZone"]
59
+ end
60
+
61
+ event.updated = Time.parse(json["updated"]) if json['updated']
62
+ event.created = Time.parse(json["created"]) if json['created']
63
+ event
64
+ else
65
+ raise "Not implemented:\n#{json.inspect}"
66
+ end
67
+ end
68
+
69
+ def to_google(yajl_opts = {})
70
+ hash = {
71
+ "kind" => "calendar#event",
72
+ }
73
+ hash["etag"] = etag if etag
74
+ hash["id"] = google_id if google_id
75
+ hash["description"] = description if description
76
+ hash["summary"] = summary if summary
77
+ start = {}
78
+ start["dateTime"] = format_time(start_time) if start_time
79
+ start["date"] = nil if start_time
80
+ start["timeZone"] = start_time_zone if start_time_zone
81
+ finish = {}
82
+ finish["dateTime"] = format_time(end_time) if end_time
83
+ finish["date"] = nil if end_time
84
+ finish["timeZone"] = end_time_zone if end_time_zone
85
+ hash["start"] = start
86
+ hash["end"] = finish
87
+ hash["visibility"] = visibility if visibility
88
+ hash["status"] = status if status
89
+ Yajl::Encoder.encode(hash, yajl_opts)
90
+ end
91
+
92
+ def new?
93
+ self.google_id.nil?
94
+ end
95
+
96
+ def format_time(time)
97
+ time.strftime("%Y-%m-%dT%H:%M:%S%:z")
98
+ end
99
+ end
@@ -0,0 +1,16 @@
1
+ class GoogleR
2
+ class Error < Exception
3
+ attr_reader :status, :response
4
+
5
+ def initialize(status, response)
6
+ @status, @response = status, response
7
+ end
8
+
9
+ def message
10
+ [
11
+ "Response code: #{@status}",
12
+ "Response body: #{@response}",
13
+ ].join("\n")
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,88 @@
1
+ require 'ostruct'
2
+
3
+ class GoogleR::Group
4
+ Property = Struct.new(:name, :info)
5
+
6
+ attr_accessor :property, :etag, :google_id, :title, :updated
7
+
8
+ def self.url
9
+ "https://www.google.com"
10
+ end
11
+
12
+ def self.path
13
+ "/m8/feeds/groups/default/full/"
14
+ end
15
+
16
+ def self.api_headers
17
+ {
18
+ 'GData-Version' => '3.0',
19
+ 'Content-Type' => 'application/atom+xml',
20
+ }
21
+ end
22
+
23
+ def path
24
+ if new?
25
+ self.class.path
26
+ else
27
+ self.class.path + google_id.split("/")[-1]
28
+ end
29
+ end
30
+
31
+ def to_google
32
+ builder = Nokogiri::XML::Builder.new(:encoding => "UTF-8") do |xml|
33
+ root_attrs = {
34
+ 'xmlns:atom' => 'http://www.w3.org/2005/Atom',
35
+ 'xmlns:gd' => 'http://schemas.google.com/g/2005',
36
+ }
37
+ root_attrs["gd:etag"] = self.etag unless new?
38
+ xml.entry(root_attrs) do
39
+ xml.id_ self.google_id unless new?
40
+ xml.updated self.updated.strftime("%Y-%m-%dT%H:%M:%S.%LZ") unless self.updated.nil?
41
+
42
+ xml['atom'].title({:type => "text"}, self.title) unless self.title.nil?
43
+ if self.property
44
+ xml['gd'].extendedProperty({:name => self.property.name}) do
45
+ xml.info self.property.info unless self.property.info.nil?
46
+ end
47
+ end
48
+ xml.parent.namespace = xml.parent.namespace_definitions.find { |ns| ns.prefix == "atom" }
49
+ end
50
+ end
51
+ builder.to_xml
52
+ end
53
+
54
+ def self.from_xml(doc, *attrs)
55
+ is_collection = doc.search("totalResults").size > 0
56
+ return doc.search("entry").map { |e| from_xml(e) } if is_collection
57
+
58
+ group = self.new
59
+
60
+ google_id = doc.search("id")
61
+ if google_id.empty?
62
+ group.etag = group.google_id = nil
63
+ else
64
+ group.etag = doc["etag"]
65
+ group.google_id = google_id.inner_text
66
+ end
67
+
68
+ title = doc.search("title")
69
+ group.title = title.inner_text unless title.size == 0
70
+
71
+ updated = doc.search("updated")
72
+ group.updated = Time.parse(updated.inner_text) unless updated.empty?
73
+
74
+ extended = doc.search("extendedProperty")
75
+ if extended.size != 0
76
+ info = extended.search("info")
77
+ info = info.size == 0 ? nil : info.inner_text
78
+ property = GoogleR::Group::Property.new(extended[0][:name], info)
79
+ group.property = property
80
+ end
81
+
82
+ group
83
+ end
84
+
85
+ def new?
86
+ self.google_id.nil?
87
+ end
88
+ end