google_apps 0.4.6 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -13,18 +13,43 @@ module GoogleApps
13
13
  include LibXML
14
14
 
15
15
  HASH_FUNCTION = "SHA-1"
16
- DOCUMENTS = %w(user export group group_member message_attributes public_key)
17
16
 
18
17
  NAMESPACES = {
19
18
  atom: 'http://www.w3.org/2005/Atom',
20
19
  apps: 'http://schemas.google.com/apps/2006',
21
- gd: 'http://schemas.google.com/g/2005'
20
+ gd: 'http://schemas.google.com/g/2005',
21
+ openSearch: 'http://a9.com/-/spec/opensearchrss/1.0/'
22
22
  }
23
23
 
24
+ MAPS = {
25
+ user: {
26
+ userName: :login,
27
+ suspended: :suspended,
28
+ familyName: :last_name,
29
+ givenName: :first_name,
30
+ limit: :quota,
31
+ password: :password
32
+ },
33
+ nickname: {
34
+ name: :nickname,
35
+ userName: :user
36
+ }
37
+ }
38
+
39
+ CATEGORY = {
40
+ user: [['scheme', 'http://schemas.google.com/g/2005#kind'], ['term', 'http://schemas.google.com/apps/2006#user']],
41
+ nickname: [['scheme', 'http://schemas.google.com/g/2005#kind'], ['term', 'http://schemas.google.com/apps/2006#nickname']],
42
+ group: [['scheme', 'http://schemas.google.com/g/2005#kind'], ['term', 'http://schemas.google.com/apps/2006#group']]
43
+ }
44
+
45
+ ENTRY_TAG = ["<atom:entry xmlns:atom=\"#{NAMESPACES[:atom]}\" xmlns:apps=\"#{NAMESPACES[:apps]}\" xmlns:gd=\"#{NAMESPACES[:gd]}\">", '</atom:entry>']
46
+
47
+ DOCUMENTS = %w(user export group group_member message_attributes public_key feed)
48
+
24
49
  # The idea is to make document distribution more dynamic.
25
50
  # Might be pointless but it's here for now.
26
51
  DOCUMENTS.each do |doc|
27
- eval "def #{doc}\n #{doc.camel_up}.new\nend"
52
+ eval "def #{doc}(*args)\n #{doc.camel_up}.new *args\nend" # Needs __file__ and __line__
28
53
  module_function doc.to_sym
29
54
  end
30
55
  end
@@ -0,0 +1,41 @@
1
+ module GoogleApps
2
+ module Atom
3
+ module Document
4
+ # parse takes xml, either a document or a string
5
+ # and returns a parsed document. Since libxml-ruby
6
+ # doesn't build a parse tree dynamically this
7
+ # is needed more than you would think.
8
+ #
9
+ # parse xml
10
+ #
11
+ # parse returns a parsed xml document
12
+ def parse(xml)
13
+ document = make_document(xml)
14
+
15
+ Atom::XML::Parser.document(document).parse
16
+ end
17
+
18
+
19
+ # make_document takes either an xml document or a
20
+ # string and generates an xml document.
21
+ #
22
+ # make_document xml
23
+ #
24
+ # make_document returns an xml document.
25
+ def make_document(xml)
26
+ xml.is_a?(Atom::XML::Document) ? xml : Atom::XML::Document.string(xml)
27
+ end
28
+
29
+
30
+ # new_empty_doc creates an empty LibXML::XML::Document
31
+ #
32
+ # new_empty_doc
33
+ #
34
+ # new_empty_doc returns a LibXML::XML::Document without
35
+ # any nodes.
36
+ def new_empty_doc
37
+ Atom::XML::Document.new
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,6 +1,9 @@
1
1
  module GoogleApps
2
2
  module Atom
3
3
  class Export
4
+ HEADER = 'HEADER_ONLY'
5
+ FULL = 'FULL_MESSAGE'
6
+
4
7
  def initialize
5
8
  @document = Atom::XML::Document.new
6
9
  set_header
@@ -0,0 +1,108 @@
1
+ module GoogleApps
2
+ module Atom
3
+ class Feed
4
+ # TODO: Google's feed responses are inconsistent. Will need special fun time, assholes.
5
+
6
+ include Atom::Node
7
+ include Atom::Document
8
+
9
+ attr_reader :xml, :items, :next_page
10
+
11
+ TYPE_MATCH = /<id.*(user|group|nickname).*?<\/id/
12
+ #TYPE_MATCH = /term.*?\#(\w*?)/
13
+
14
+ def initialize(xml)
15
+ @xml = parse(xml)
16
+ @items = entries_from document: @xml, type: @xml.to_s.match(TYPE_MATCH).captures[0], entry_tag: 'entry'
17
+ end
18
+
19
+ # TODO: Need to make sure this works for feeds other than user.
20
+ def entries_from(properties)
21
+ type = properties[:type].to_sym
22
+
23
+ properties[:document].root.inject([]) do |results, entry|
24
+ if entry.name == properties[:entry_tag]
25
+ results << new_doc(type, node_to_ary(entry), ['apps:', 'atom:', 'gd:'])
26
+ end
27
+ set_next_page(entry) if entry.name == 'link' and entry.attributes[:rel] == 'next'
28
+ results
29
+ end
30
+ end
31
+
32
+
33
+ def set_next_page(node)
34
+ @next_page = node.attributes[:href]
35
+ end
36
+
37
+
38
+ # node_to_ary converts a Atom::XML::Node to an array.
39
+ #
40
+ # node_to_ary node
41
+ #
42
+ # node_to_ary returns the string representation of the
43
+ # given node split on \n.
44
+ def node_to_ary(node)
45
+ node.to_s.split("\n")
46
+ end
47
+
48
+
49
+ # new_doc creates a new Atom document from the data
50
+ # provided in the feed. new_doc takes a type, an
51
+ # array of content to be placed into the document
52
+ # as well as an array of filters.
53
+ #
54
+ # new_doc 'user', content_array, ['apps:']
55
+ #
56
+ # new_doc returns an GoogleApps::Atom document of the
57
+ # specified type.
58
+ def new_doc(type, content_array, filters)
59
+ content_array = filters.inject([]) do |content, filter|
60
+ content << grab_elements(content_array, filter)
61
+ content
62
+ end
63
+
64
+ add_category content_array, type
65
+
66
+ Atom.send type, entry_wrap(content_array.flatten).join("\n")
67
+ end
68
+
69
+
70
+ # add_category adds the proper atom:category node to the
71
+ # content_array
72
+ #
73
+ # add_category content_array, 'user'
74
+ #
75
+ # add_category returns the modified content_array
76
+ def add_category(content_array, type)
77
+ content_array.unshift(create_node(type: 'atom:category', attrs: Atom::CATEGORY[type.to_sym]).to_s)
78
+ end
79
+
80
+
81
+ # grab_elements applies the specified filter to the
82
+ # provided array. Google's feed provides a lot of data
83
+ # that we don't need in an entry document.
84
+ #
85
+ # grab_elements content_array, 'apps:'
86
+ #
87
+ # grab_elements returns an array of items from content_array
88
+ # that match the given filter.
89
+ def grab_elements(content_array, filter)
90
+ content_array.grep(Regexp.new filter)
91
+ end
92
+
93
+
94
+ # entry_wrap adds atom:entry opening and closing tags
95
+ # to the provided content_array and the beginning and
96
+ # end.
97
+ #
98
+ # entry_wrap content_array
99
+ #
100
+ # entry_wrap returns an array with an opening atom:entry
101
+ # element prepended to the front and a closing atom:entry
102
+ # tag appended to the end.
103
+ def entry_wrap(content_array)
104
+ content_array.unshift(Atom::ENTRY_TAG[0]).push(Atom::ENTRY_TAG[1])
105
+ end
106
+ end
107
+ end
108
+ end
@@ -1,13 +1,20 @@
1
1
  module GoogleApps
2
2
  module Atom
3
3
  class Group
4
+ include Atom::Node
5
+ include Atom::Document
6
+
4
7
  #ATTRIBUTES = %w(id name description perms).map(&:to_sym)
5
8
 
6
- def initialize
7
- @document = Atom::XML::Document.new
8
- add_header
9
+ def initialize(xml = nil)
10
+ if xml
11
+ @document = parse(xml)
12
+ else
13
+ @document = Atom::XML::Document.new
14
+ add_header
15
+ end
9
16
  end
10
-
17
+
11
18
  # new_group populates the Group XML document with
12
19
  # the provided values. new_group accepts a hash
13
20
  # with the following keys: id, name, description
@@ -36,7 +43,7 @@ module GoogleApps
36
43
  prop = Atom::XML::Node.new('apps:property')
37
44
  prop_name(prop, key)
38
45
  prop.attributes['value'] = group_values[key]
39
- @document.root << prop
46
+ @document.root << prop
40
47
  end
41
48
 
42
49
  @document.root
@@ -59,6 +66,32 @@ module GoogleApps
59
66
  Atom::XML::Namespace.new(@document.root, 'gd', 'http://schemas.google.com/g/2005')
60
67
  end
61
68
 
69
+
70
+ def find_values
71
+ map = Atom::MAPS[:user]
72
+
73
+ @document.root.each do |entry|
74
+ unless entry.name.match 'gd' or entry.name.match 'atom'
75
+ entry.attributes.each do |attribute|
76
+ instance_variable_set "@#{map[attribute.name.to_sym]}", check_value(attribute.value)
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+
83
+ def check_value(value)
84
+ case value
85
+ when 'true'
86
+ true
87
+ when 'false'
88
+ false
89
+ else
90
+ value
91
+ end
92
+ end
93
+
94
+
62
95
  # prop_name takes a LibXML::XML::Node object and
63
96
  # sets the name attribute based on the provided
64
97
  # key.
@@ -1,7 +1,9 @@
1
1
  module GoogleApps
2
2
  module Atom
3
3
  class GroupMember
4
- include GoogleApps::Atom::Node
4
+ include Atom::Node
5
+ include Atom::Document
6
+
5
7
  attr_accessor :member
6
8
 
7
9
  def initialize
@@ -1,7 +1,9 @@
1
1
  module GoogleApps
2
2
  module Atom
3
3
  class Nickname
4
- include GoogleApps::Atom::Node
4
+ include Atom::Node
5
+ include Atom::Document
6
+
5
7
  attr_reader :nickname, :user, :document
6
8
 
7
9
  ELEMENTS = { nick: ['apps:nickname', 'name'], user: ['apps:login', 'userName'] }
@@ -48,12 +50,7 @@ module GoogleApps
48
50
  # header returns an atom:entry node with the appropriate
49
51
  # namespaces for a GoogleApps nickname document
50
52
  def header
51
- node = Atom::XML::Node.new('atom:entry')
52
-
53
- Atom::XML::Namespace.new(node, 'atom', 'http://www.w3.org/2005/Atom')
54
- Atom::XML::Namespace.new(node, 'apps', 'http://schemas.google.com/apps/2006')
55
-
56
- node
53
+ add_namespaces create_node(type: 'atom:entry'), atom: 'http://www.w3.org/2005/Atom', apps: 'http://schemas.google.com/apps/2006'
57
54
  end
58
55
 
59
56
 
@@ -61,11 +58,7 @@ module GoogleApps
61
58
  # appropriate attributes for a GoogleApps nickname
62
59
  # document.
63
60
  def category
64
- node = Atom::XML::Node.new('atom:category')
65
- node.attributes['scheme'] = 'http://schemas.google.com/g/2005#kind'
66
- node.attributes['term'] = 'http://schemas.google.com/apps/2006#nickname'
67
-
68
- node
61
+ create_node type: 'atom:category', attrs: Atom::CATEGORY[:nickname]
69
62
  end
70
63
 
71
64
 
@@ -20,6 +20,22 @@ module GoogleApps
20
20
  end
21
21
 
22
22
 
23
+ # add_namespaces adds the specified namespaces to the
24
+ # specified node. namespaces should be a hash of name,
25
+ # value pairs.
26
+ #
27
+ # add_namespaces node, atom: 'http://www.w3.org/2005/Atom', apps: 'http://schemas.google.com/apps/2006'
28
+ #
29
+ # add_namespaces returns the node with namespaces
30
+ def add_namespaces(node, namespaces)
31
+ namespaces.each_pair do |name, value|
32
+ Atom::XML::Namespace.new node, name.to_s, value
33
+ end
34
+
35
+ node
36
+ end
37
+
38
+
23
39
  # add_attributes adds the specified attributes to the
24
40
  # given node. It takes a LibXML::XML::Node and an
25
41
  # array of name, value attribute pairs.
@@ -50,10 +66,22 @@ module GoogleApps
50
66
  def find_and_update(document, xpath, attributes)
51
67
  document.find(xpath).each do |node|
52
68
  attributes.each_key do |attrib|
53
- node.attributes[attrib.to_s] = attributes[attrib][1] if node.attributes[attrib.to_s] == attributes[attrib][0]
69
+ node.attributes[attrib.to_s] = attributes[attrib][1] if node.attributes[attrib.to_s].to_s == attributes[attrib][0].to_s
54
70
  end
55
71
  end
56
72
  end
73
+
74
+
75
+ # get_content returns the content of the specified node.
76
+ # If multiple nodes match the xpath value get_content
77
+ # will return the content of the first occurance.
78
+ #
79
+ # get_content document, '//title'
80
+ #
81
+ # get_content returns the content of the node as a string.
82
+ def get_content(document, xpath)
83
+ document.find(xpath).first.content
84
+ end
57
85
  end
58
86
  end
59
87
  end
@@ -1,89 +1,163 @@
1
1
  module GoogleApps
2
2
  module Atom
3
+ # TODO: Move User attribute map to user class
4
+ # TODO: Update attribute map to include @ for instance variables
3
5
  class User
4
- attr_reader :document
5
-
6
- def initialize
7
- new_doc
8
- add_header
6
+ include Atom::Node
7
+ include Atom::Document
8
+
9
+ attr_reader :document, :login, :suspended, :first_name, :last_name, :quota, :password
10
+
11
+ def initialize(xml = nil)
12
+ if xml
13
+ @document = parse(xml)
14
+ find_values
15
+ else
16
+ @document = new_empty_doc
17
+ add_header
18
+ end
9
19
  end
10
20
 
11
- # new_user adds the nodes necessary to create a new
12
- # user in Google Apps. new_user requires a username,
13
- # first name, last name and password. You can also
14
- # provide an optional quota argument, this will override
15
- # the default quota in Google Apps.
21
+
22
+ # set adds the values for the given attributes to the
23
+ # current document. populates takes a hash of attribute,
24
+ # value pairs.
16
25
  #
17
- # new_user 'username', 'first_name', 'last_name', 'password', 1024
26
+ # set login: 'Zuddile', password: 'old shoes'
27
+ def set(attributes)
28
+ attributes.keys.each do |key|
29
+ self.send("#{key}=", attributes[key])
30
+ end
31
+ end
32
+
33
+
34
+ # add_node creates the specified node in the user document. It
35
+ # takes a type/name and an array of attribute, value pairs as
36
+ # arguments. It also parses the new document and saves the
37
+ # copy in @document
18
38
  #
19
- # new_user returns the full XML document.
20
- def new_user(user_name, first, last, password, quota=nil)
21
- set_values suspended: 'false', username: user_name, password: password, first_name: first, last_name: last, quota: quota
22
- end
39
+ # add_node 'apps:login', [['userName', 'Zanzabar']]
40
+ #
41
+ # add_node returns a parsed copy of the new document.
42
+ def add_node(type, attrs) # TODO: Should take a target argument rather than only appending to @document.root
43
+ @document.root << create_node(type: type, attrs: attrs)
44
+
45
+ @document = parse @document
46
+ end
47
+
48
+
49
+ # update_node updates an existing node in the document. It takes
50
+ # the type/name, attribute name and the new value as arguments
51
+ #
52
+ # update_node 'apps:login', :userName, true
53
+ def update_node(type, attribute, value)
54
+ find_and_update @document, "//#{type}", { attribute => [instance_variable_get("@#{Atom::MAPS[:user][attribute]}").to_s, value.to_s]}
55
+ end
56
+
57
+
58
+ # TODO: Move this method.
59
+ def node(name)
60
+ @document.find_first("//#{name}")
61
+ end
62
+
23
63
 
24
- # TODO: Document
25
- def set_values(values = {})
26
- # Don't want to create login_node if nothing has been specified.
27
- @document.root << login_node(values[:suspended], values[:username], values[:password])
28
- @document.root << quota_node(values[:quota]) if values[:quota]
29
- @document.root << name_node(values[:first_name], values[:last_name]) if values[:first_name] or values[:last_name]
64
+ # NOTE: setters should work even if target node exists but has no suspended property. Unless libxml-ruby changes it's default for the attributes hash on a node.
30
65
 
31
- @document
66
+
67
+ # suspended= sets the suspended value for the account
68
+ #
69
+ # suspended = true
70
+ #
71
+ # suspended= returns the value that has been set
72
+ def suspended=(value)
73
+ node('apps:login') ? update_node('apps:login', :suspended, value) : add_node('apps:login', [['suspended', value.to_s]])
74
+
75
+ @suspended = value
32
76
  end
33
77
 
34
- # login_node adds an apps:login attribute to @document.
35
- # login_node takes a username and password as arguments
36
- # it is also possible to specify that the account be
37
- # suspended.
78
+
79
+ # login= sets the login/account name for this entry
38
80
  #
39
- # login_node suspended, 'username', 'password'
81
+ # login = 'Zanzabar'
40
82
  #
41
- # login_node returns an 'apps:login' LibXML::XML::Node
42
- def login_node(suspended = "false", user_name = nil, password = nil)
43
- login = Atom::XML::Node.new('apps:login')
44
- login['userName'] = user_name unless user_name.nil?
45
- login['password'] = OpenSSL::Digest::SHA1.hexdigest password unless password.nil?
46
- login['hashFunctionName'] = Atom::HASH_FUNCTION unless password.nil?
47
- suspended.nil? ? login['suspended'] = 'false' : login['suspended'] = suspended
83
+ # login= returns the value that has been set
84
+ def login=(login)
85
+ node('apps:login') ? update_node('apps:login', :userName, login) : add_node('apps:login', [['userName', login]])
48
86
 
49
- login
87
+ @login = login
50
88
  end
51
89
 
52
90
 
53
- # quota_node adds an apps:quota attribute to @document.
54
- # quota_node takes an integer value as an argument. This
55
- # argument translates to the number of megabytes available
56
- # on the Google side.
91
+ # first_name= sets the first name for this user entry
57
92
  #
58
- # quota_node 1024
93
+ # first_name = 'Lou'
59
94
  #
60
- # quota_node returns an 'apps:quota' LibXML::XML::Node
61
- def quota_node(limit)
62
- quota = Atom::XML::Node.new('apps:quota')
63
- quota['limit'] = limit.to_s
95
+ # first_name returns the value that has been set
96
+ def first_name=(name)
97
+ node('apps:name') ? update_node('apps:name', :givenName, name) : add_node('apps:name', [['givenName', name]])
64
98
 
65
- quota
66
- end
99
+ @first_name = name
100
+ end
67
101
 
68
- # name_node adds an apps:name attribute to @document.
69
- # name_node takes the first and last names as arguments.
102
+
103
+ # last_name= sets the last name for this user entry
70
104
  #
71
- # name_node 'first name', 'last name'
105
+ # last_name = 'Svensen'
72
106
  #
73
- # name_node returns an apps:name LibXML::XML::Node
74
- def name_node(first = nil, last = nil)
75
- name = Atom::XML::Node.new('apps:name')
76
- name['familyName'] = last if last
77
- name['givenName'] = first if first
107
+ # last_name= returns the value that has been set
108
+ def last_name=(name)
109
+ node('apps:name') ? update_node('apps:name', :familyName, name) : add_node('apps:name', [['familyName', name]])
110
+
111
+ @last_name = name
112
+ end
113
+
114
+
115
+ # quota= sets the quota for this user entry
116
+ #
117
+ # quota = 123456
118
+ #
119
+ # quota= returns the value that has been set
120
+ def quota=(limit)
121
+ node('apps:quota') ? update_node('apps:quota', :limit, limit) : add_node('apps:quota', [['limit', limit.to_s]])
122
+
123
+ @quota = limit
124
+ end
125
+
126
+
127
+ # password= sets the password and hashFunctionName attributes
128
+ # in the apps:login node. It takes a plaintext string as it's
129
+ # only argument.
130
+ #
131
+ # password = 'new password'
132
+ #
133
+ # password= returns the value that has been set
134
+ def password=(password)
135
+ hashed = hash_password(password)
136
+
137
+ node('apps:login') ? update_node('apps:login', :password, hashed) : add_node('apps:login', [['password', hashed]])
138
+
139
+ add_attributes node('apps:login'), [['hashFunctionName', Atom::HASH_FUNCTION]]
140
+
141
+ @password = hashed
142
+ end
143
+
144
+
145
+ # hash_password hashes the provided password
146
+ #
147
+ # hash_password 'new password'
148
+ #
149
+ # hash_password returns an SHA1 digest of the password
150
+ def hash_password(password)
151
+ OpenSSL::Digest::SHA1.hexdigest password
152
+ end
78
153
 
79
- name
80
- end
81
154
 
82
155
  # to_s returns @document as a string
83
156
  def to_s
84
157
  @document.to_s
85
158
  end
86
159
 
160
+
87
161
  private
88
162
 
89
163
  # new_doc re-initializes the XML document.
@@ -91,6 +165,35 @@ module GoogleApps
91
165
  @document = Atom::XML::Document.new
92
166
  end
93
167
 
168
+
169
+ # TODO: This needs to target the proper nodes.
170
+ # TODO: This needs to treat 'true' and 'false' properly
171
+ def find_values
172
+ map = Atom::MAPS[:user]
173
+
174
+ @document.root.each do |entry|
175
+ # Something in the feedLink entries causes a segfault.
176
+ unless entry.name.match 'gd' or entry.name.match 'atom'
177
+ entry.attributes.each do |attribute|
178
+ instance_variable_set "@#{map[attribute.name.to_sym]}", check_value(attribute.value)
179
+ end
180
+ end
181
+ end
182
+ end
183
+
184
+
185
+ def check_value(value)
186
+ case value
187
+ when 'true'
188
+ true
189
+ when 'false'
190
+ false
191
+ else
192
+ value
193
+ end
194
+ end
195
+
196
+
94
197
  def add_header
95
198
  @document.root = Atom::XML::Node.new('atom:entry')
96
199
 
@@ -5,10 +5,14 @@ require 'rexml/document'
5
5
 
6
6
  module GoogleApps
7
7
  class Transport
8
- attr_reader :request, :response, :domain
8
+ attr_reader :request, :response, :domain, :feeds
9
9
  attr_accessor :auth, :user, :group, :nickname, :export
10
10
 
11
11
  BOUNDARY = "=AaB03xDFHT8xgg"
12
+ PAGE_SIZE = {
13
+ user: 100,
14
+ group: 200
15
+ }
12
16
 
13
17
  def initialize(domain, targets = {})
14
18
  @auth = targets[:auth] || "https://www.google.com/accounts/ClientLogin"
@@ -22,6 +26,7 @@ module GoogleApps
22
26
  @token = nil
23
27
  @response = nil
24
28
  @request = nil
29
+ @feeds = []
25
30
  end
26
31
 
27
32
 
@@ -109,15 +114,41 @@ module GoogleApps
109
114
  # get returns the HTTP response received from Google.
110
115
  def get(endpoint, id = nil)
111
116
  # TODO: Need to handle <link rel='next' for pagination if wanting all users
112
- id ? uri = URI(endpoint + "/#{id}") : uri = URI(endpoint)
113
- #uri = URI(instance_variable_get("@#{endpoint.to_s}") + "/#{id}")
114
- @request = Net::HTTP::Get.new(uri.path)
117
+ id ? uri = URI(endpoint + build_id(id)) : uri = URI(endpoint)
118
+ @request = Net::HTTP::Get.new(uri.request_uri)
115
119
  set_headers :user
116
120
 
117
121
  @response = request uri
118
122
  end
119
123
 
120
124
 
125
+ # get_users retrieves as many users as specified from the
126
+ # domain. If no starting point is given it will grab all the
127
+ # users in the domain. If a starting point is specified all
128
+ # users from that point on (alphabetically) will be returned.
129
+ #
130
+ # get_users start: 'lholcomb2'
131
+ #
132
+ # get_users returns the final response from google.
133
+ def get_users(options = {})
134
+ # TODO: Limit isn't working right. It stops the retrieval but not as soon as it should.
135
+ @feeds, page = [], 0
136
+
137
+ options[:limit] ? limit = options[:limit] : limit = 1000000
138
+ options[:start] ? get(@user + "?startUsername=#{options[:start]}") : get(@user)
139
+
140
+ add_feed
141
+
142
+ while (next_page = get_next(@feeds.last.xml)) and (page * PAGE_SIZE[:user] < limit)
143
+ get @feeds.last.next_page
144
+ add_feed
145
+ page += 1
146
+ end
147
+
148
+ @response
149
+ end
150
+
151
+
121
152
  # add_member_to adds a member to a group in the domain.
122
153
  # It takes a group_id and a GoogleApps::Atom::GroupMember
123
154
  # document as arguments.
@@ -246,6 +277,14 @@ module GoogleApps
246
277
  end
247
278
 
248
279
 
280
+ # build_id checks the id string. If it is formatted
281
+ # as a query string it is returned as is. If not
282
+ # a / is prepended to the id string.
283
+ def build_id(id)
284
+ id =~ /^\?/ ? id : "/#{id}"
285
+ end
286
+
287
+
249
288
  # Grab the auth token from the response body
250
289
  def set_auth_token
251
290
  @response.body.split("\n").grep(/auth=(.*)/i)
@@ -254,6 +293,12 @@ module GoogleApps
254
293
  end
255
294
 
256
295
 
296
+ # add_feed adds a feed to the @feeds array.
297
+ def add_feed
298
+ @feeds << GoogleApps::Atom.feed(@response.body)
299
+ end
300
+
301
+
257
302
  def request(uri)
258
303
  # TODO: Clashes with @request reader
259
304
  Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
data/lib/google_apps.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'google_apps/transport'
2
2
  require 'google_apps/atom/atom'
3
3
  require 'google_apps/atom/node'
4
+ require 'google_apps/atom/document'
5
+ require 'google_apps/atom/feed'
4
6
  require 'google_apps/atom/user'
5
7
  require 'google_apps/atom/group'
6
8
  require 'google_apps/atom/public_key'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google_apps
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
4
+ version: 0.4.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-24 00:00:00.000000000 Z
12
+ date: 2012-07-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: libxml-ruby
@@ -34,7 +34,9 @@ extensions: []
34
34
  extra_rdoc_files: []
35
35
  files:
36
36
  - lib/google_apps/atom/atom.rb
37
+ - lib/google_apps/atom/document.rb
37
38
  - lib/google_apps/atom/export.rb
39
+ - lib/google_apps/atom/feed.rb
38
40
  - lib/google_apps/atom/group.rb
39
41
  - lib/google_apps/atom/group_member.rb
40
42
  - lib/google_apps/atom/message.rb