googlecontacts 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +10 -0
- data/LICENSE +20 -0
- data/README.md +41 -0
- data/README.rdoc +17 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/google_contacts.rb +15 -0
- data/lib/google_contacts/auth.rb +28 -0
- data/lib/google_contacts/base.rb +163 -0
- data/lib/google_contacts/contact.rb +32 -0
- data/lib/google_contacts/group.rb +9 -0
- data/lib/google_contacts/proxies/array.rb +46 -0
- data/lib/google_contacts/proxies/emails.rb +125 -0
- data/lib/google_contacts/proxies/hash.rb +41 -0
- data/lib/google_contacts/wrapper.rb +126 -0
- data/spec/assets/contacts_full.xml +60 -0
- data/spec/assets/groups_full.xml +58 -0
- data/spec/base_spec.rb +78 -0
- data/spec/contact_spec.rb +80 -0
- data/spec/group_spec.rb +18 -0
- data/spec/proxies/array_spec.rb +105 -0
- data/spec/proxies/emails_spec.rb +161 -0
- data/spec/proxies/hash_spec.rb +78 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/wrapper_spec.rb +75 -0
- metadata +129 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
module GoogleContacts
|
2
|
+
module Proxies
|
3
|
+
class Emails < BlankSlate
|
4
|
+
def initialize(parent)
|
5
|
+
@parent = parent
|
6
|
+
reinitialize
|
7
|
+
end
|
8
|
+
|
9
|
+
def reinitialize
|
10
|
+
@current = ::Hash[*@parent.xml.xpath("./gd:email").map do |entry|
|
11
|
+
email = Email.new(self, entry.attributes)
|
12
|
+
[email.address, email]
|
13
|
+
end.flatten]
|
14
|
+
|
15
|
+
# create a deep copy
|
16
|
+
@new = ::Hash[*@current.map do |k,v|
|
17
|
+
[k.dup, v.dup]
|
18
|
+
end.flatten]
|
19
|
+
end
|
20
|
+
|
21
|
+
def changed?
|
22
|
+
@current != @new
|
23
|
+
end
|
24
|
+
|
25
|
+
def primary!(address)
|
26
|
+
@new.each do |key, email|
|
27
|
+
if key == address
|
28
|
+
email.primary = true
|
29
|
+
else
|
30
|
+
email.delete(:primary)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def <<(address)
|
36
|
+
raise "Duplicate address" if @new[address]
|
37
|
+
add(address)
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](address)
|
41
|
+
@new[address] || add(address)
|
42
|
+
end
|
43
|
+
|
44
|
+
def synchronize
|
45
|
+
@parent.remove_xml("./gd:email")
|
46
|
+
@new.each_pair do |address, email|
|
47
|
+
@parent.insert_xml('gd:email', email)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def add(address)
|
53
|
+
set_primary = @new.empty?
|
54
|
+
@new[address] = Email.new(self, { :address => address })
|
55
|
+
@new[address].primary = true if set_primary
|
56
|
+
@new[address]
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_missing(sym, *args, &blk)
|
60
|
+
if [:size, :delete].include?(sym)
|
61
|
+
@new.send(sym, *args, &blk)
|
62
|
+
else
|
63
|
+
super
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Email < ::HashWithIndifferentAccess
|
68
|
+
DEFAULTS = {
|
69
|
+
:rel => 'http://schemas.google.com/g/2005#home'
|
70
|
+
}.freeze
|
71
|
+
|
72
|
+
alias_attribute :name, :displayName
|
73
|
+
|
74
|
+
def initialize(parent, attributes = {})
|
75
|
+
super(DEFAULTS)
|
76
|
+
@parent = parent
|
77
|
+
|
78
|
+
attributes.each do |key, value|
|
79
|
+
send("#{key}=", value)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def primary!
|
84
|
+
@parent.primary! self[:address]
|
85
|
+
end
|
86
|
+
|
87
|
+
def rel=(arg)
|
88
|
+
delete(:label)
|
89
|
+
method_missing("rel=", arg)
|
90
|
+
end
|
91
|
+
|
92
|
+
def label=(arg)
|
93
|
+
delete(:rel)
|
94
|
+
method_missing("label=", arg)
|
95
|
+
end
|
96
|
+
|
97
|
+
def []=(key, value)
|
98
|
+
if "#{key}" == 'address' && self[key]
|
99
|
+
raise "Cannot modify email address"
|
100
|
+
end
|
101
|
+
super(key, value.to_s)
|
102
|
+
end
|
103
|
+
|
104
|
+
def dup
|
105
|
+
self.class.new(@parent, self)
|
106
|
+
end
|
107
|
+
|
108
|
+
def method_missing(sym, *args, &blk)
|
109
|
+
if sym.to_s =~ /^(\w+)(=|\?)?$/
|
110
|
+
case $2
|
111
|
+
when '='
|
112
|
+
send(:[]=, $1, *args)
|
113
|
+
when '?'
|
114
|
+
send(:[], $1) == 'true'
|
115
|
+
else
|
116
|
+
send(:[], $1)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
super
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end # class Email
|
123
|
+
end # class Emails
|
124
|
+
end # module Proxies
|
125
|
+
end # module GoogleContacts
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module GoogleContacts
|
2
|
+
module Proxies
|
3
|
+
class Hash < BlankSlate
|
4
|
+
def initialize(parent, options)
|
5
|
+
@parent = parent
|
6
|
+
@tag = options[:tag]
|
7
|
+
@key = options[:key]
|
8
|
+
@value = options[:value]
|
9
|
+
|
10
|
+
reinitialize
|
11
|
+
end
|
12
|
+
|
13
|
+
def reinitialize
|
14
|
+
@current = HashWithIndifferentAccess.new
|
15
|
+
@parent.xml.xpath("./#{@tag}").map do |entry|
|
16
|
+
@current[entry[@key]] = entry[@value]
|
17
|
+
end
|
18
|
+
|
19
|
+
# create a deep copy
|
20
|
+
@new = HashWithIndifferentAccess.new
|
21
|
+
@current.each { |k,v| @new[k.dup] = v.dup }
|
22
|
+
end
|
23
|
+
|
24
|
+
def changed?
|
25
|
+
@current != @new
|
26
|
+
end
|
27
|
+
|
28
|
+
def synchronize
|
29
|
+
@parent.remove_xml("./#{@tag}")
|
30
|
+
@new.each_pair do |key, value|
|
31
|
+
@parent.insert_xml(@tag, @key => key, @value => value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def method_missing(sym, *args, &blk)
|
37
|
+
@new.send(sym, *args, &blk)
|
38
|
+
end
|
39
|
+
end # class Hash
|
40
|
+
end # module Proxies
|
41
|
+
end # module GoogleContacts
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module GoogleContacts
|
2
|
+
class Wrapper
|
3
|
+
attr_reader :consumer
|
4
|
+
|
5
|
+
CONTACTS_BATCH = "http://www.google.com/m8/feeds/contacts/default/full/batch".freeze
|
6
|
+
GROUPS_BATCH = "http://www.google.com/m8/feeds/groups/default/full/batch".freeze
|
7
|
+
|
8
|
+
# Proxies for crud
|
9
|
+
attr_reader :contacts
|
10
|
+
attr_reader :groups
|
11
|
+
|
12
|
+
def initialize(consumer)
|
13
|
+
@consumer = consumer
|
14
|
+
|
15
|
+
@contacts = CollectionProxy.new(self, 'contacts')
|
16
|
+
@groups = CollectionProxy.new(self, 'groups')
|
17
|
+
end
|
18
|
+
|
19
|
+
def get(url, options = {})
|
20
|
+
query = options.map { |k,v| "#{k}=#{v}" }.join('&')
|
21
|
+
url += "?#{query}" if query.size > 0
|
22
|
+
|
23
|
+
body = consumer.get(url).body
|
24
|
+
Nokogiri::XML.parse body
|
25
|
+
end
|
26
|
+
|
27
|
+
def post(url, body)
|
28
|
+
consumer.post(url, body, 'Content-Type' => 'application/atom+xml')
|
29
|
+
end
|
30
|
+
|
31
|
+
def batch(options = {}, &blk)
|
32
|
+
raise "Nesting of calls to batch is not allowed" if @batching
|
33
|
+
@batching = true
|
34
|
+
@batch ||= []
|
35
|
+
|
36
|
+
yield(blk)
|
37
|
+
@batching = false
|
38
|
+
|
39
|
+
# create documents to be flushed
|
40
|
+
documents = @batch.each_slice(100).map do |chunk|
|
41
|
+
batch_document(chunk)
|
42
|
+
end
|
43
|
+
@batch.clear
|
44
|
+
|
45
|
+
if options[:return_documents]
|
46
|
+
documents
|
47
|
+
else
|
48
|
+
documents.each do |doc|
|
49
|
+
flush_batch(doc)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def find(what, options = {}, &blk)
|
55
|
+
xml = get("http://www.google.com/m8/feeds/#{what}/default/full", options)
|
56
|
+
xml.xpath('/xmlns:feed/xmlns:entry').map(&blk)
|
57
|
+
end
|
58
|
+
|
59
|
+
def save(instance)
|
60
|
+
entry = instance.entry_for_batch(instance.new? ? :insert : :update)
|
61
|
+
append_to_batch(entry)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def append_to_batch(entry)
|
67
|
+
if @batching
|
68
|
+
if @batch.present?
|
69
|
+
# puts @batch.last.a
|
70
|
+
# puts @batch.last.at('./category').inspect
|
71
|
+
batch_term = @batch.last.at('./category')['term']
|
72
|
+
entry_term = entry.at('./category')['term']
|
73
|
+
raise "Cannot mix Contact and Group in one batch" if batch_term != entry_term
|
74
|
+
end
|
75
|
+
|
76
|
+
@batch << entry
|
77
|
+
else
|
78
|
+
batch do
|
79
|
+
@batch << entry
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Use the <category/> tag of the first entry to find out
|
85
|
+
# which type we're flushing
|
86
|
+
def flush_batch(document)
|
87
|
+
url = case document.at('./xmlns:entry[1]/xmlns:category')['term']
|
88
|
+
when /#contact$/i
|
89
|
+
CONTACTS_BATCH
|
90
|
+
when /#group$/i
|
91
|
+
GROUPS_BATCH
|
92
|
+
else
|
93
|
+
raise "Unable to determine type for batch"
|
94
|
+
end
|
95
|
+
post(url, document.to_xml)
|
96
|
+
end
|
97
|
+
|
98
|
+
def batch_document(*operations)
|
99
|
+
batch_feed = Base.feed_for_batch
|
100
|
+
operations.flatten.each do |operation|
|
101
|
+
batch_feed << operation
|
102
|
+
end
|
103
|
+
batch_feed
|
104
|
+
end
|
105
|
+
|
106
|
+
class CollectionProxy
|
107
|
+
def initialize(wrapper, collection)
|
108
|
+
@wrapper = wrapper
|
109
|
+
@collection = collection
|
110
|
+
@klass = collection.singularize.camelcase.constantize
|
111
|
+
end
|
112
|
+
|
113
|
+
# :what - all, ID, whatever, currently unused
|
114
|
+
def find(what, options = {})
|
115
|
+
@wrapper.find(@collection, options) do |entry|
|
116
|
+
@klass.new(@wrapper, entry)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def build(attributes = {})
|
121
|
+
@klass.new(@wrapper)
|
122
|
+
end
|
123
|
+
|
124
|
+
end # class CollectionProxy
|
125
|
+
end # class Wrapper
|
126
|
+
end # module GoogleContacts
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<feed xmlns='http://www.w3.org/2005/Atom'
|
2
|
+
xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/'
|
3
|
+
xmlns:gContact='http://schemas.google.com/contact/2008'
|
4
|
+
xmlns:batch='http://schemas.google.com/gdata/batch'
|
5
|
+
xmlns:gd='http://schemas.google.com/g/2005'
|
6
|
+
gd:etag='W/"CUMBRHo_fip7ImA9WxRbGU0."'>
|
7
|
+
<id>liz@gmail.com</id>
|
8
|
+
<updated>2008-12-10T10:04:15.446Z</updated>
|
9
|
+
<category scheme='http://schemas.google.com/g/2005#kind'
|
10
|
+
term='http://schemas.google.com/contact/2008#contact' />
|
11
|
+
<title>Elizabeth Bennet's Contacts</title>
|
12
|
+
<link rel='http://schemas.google.com/g/2005#feed'
|
13
|
+
type='application/atom+xml'
|
14
|
+
href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full' />
|
15
|
+
<link rel='http://schemas.google.com/g/2005#post'
|
16
|
+
type='application/atom+xml'
|
17
|
+
href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full' />
|
18
|
+
<link rel='http://schemas.google.com/g/2005#batch'
|
19
|
+
type='application/atom+xml'
|
20
|
+
href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/batch' />
|
21
|
+
<link rel='self' type='application/atom+xml'
|
22
|
+
href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full?max-results=25' />
|
23
|
+
<author>
|
24
|
+
<name>Elizabeth Bennet</name>
|
25
|
+
<email>liz@gmail.com</email>
|
26
|
+
</author>
|
27
|
+
<generator version='1.0' uri='http://www.google.com/m8/feeds'>
|
28
|
+
Contacts
|
29
|
+
</generator>
|
30
|
+
<openSearch:totalResults>1</openSearch:totalResults>
|
31
|
+
<openSearch:startIndex>1</openSearch:startIndex>
|
32
|
+
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
|
33
|
+
<entry gd:etag='"Qn04eTVSLyp7ImA9WxRbGEUORAQ."'>
|
34
|
+
<id>
|
35
|
+
http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base/c9012de
|
36
|
+
</id>
|
37
|
+
<updated>2008-12-10T04:45:03.331Z</updated>
|
38
|
+
<app:edited xmlns:app='http://www.w3.org/2007/app'>2008-12-10T04:45:03.331Z</app:edited>
|
39
|
+
<category scheme='http://schemas.google.com/g/2005#kind'
|
40
|
+
term='http://schemas.google.com/contact/2008#contact' />
|
41
|
+
<title>Fitzwilliam Darcy</title>
|
42
|
+
<gd:name>
|
43
|
+
<gd:fullName>Fitzwilliam Darcy</gd:fullName>
|
44
|
+
</gd:name>
|
45
|
+
<link rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*'
|
46
|
+
href='http://www.google.com/m8/feeds/photos/media/liz%40gmail.com/c9012de'
|
47
|
+
gd:etag='"KTlcZWs1bCp7ImBBPV43VUV4LXEZCXERZAc."' />
|
48
|
+
<link rel='self' type='application/atom+xml'
|
49
|
+
href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/c9012de' />
|
50
|
+
<link rel='edit' type='application/atom+xml'
|
51
|
+
href='http://www.google.com/m8/feeds/contacts/liz%40gmail.com/full/c9012de' />
|
52
|
+
<gd:phoneNumber rel='http://schemas.google.com/g/2005#home'
|
53
|
+
primary='true'>
|
54
|
+
456
|
55
|
+
</gd:phoneNumber>
|
56
|
+
<gd:extendedProperty name='pet' value='hamster' />
|
57
|
+
<gContact:groupMembershipInfo deleted='false'
|
58
|
+
href='http://www.google.com/m8/feeds/groups/liz%40gmail.com/base/270f' />
|
59
|
+
</entry>
|
60
|
+
</feed>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<feed xmlns='http://www.w3.org/2005/Atom'
|
2
|
+
xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/'
|
3
|
+
xmlns:gContact='http://schemas.google.com/contact/2008'
|
4
|
+
xmlns:batch='http://schemas.google.com/gdata/batch'
|
5
|
+
xmlns:gd='http://schemas.google.com/g/2005'
|
6
|
+
gd:etag='W/"D08MQnc-fSp7ImA9WxRbGU0."'>
|
7
|
+
<id>jo@gmail.com</id>
|
8
|
+
<updated>2008-12-10T10:44:43.955Z</updated>
|
9
|
+
<category scheme='http://schemas.google.com/g/2005#kind'
|
10
|
+
term='http://schemas.google.com/contact/2008#group' />
|
11
|
+
<title>Jo March's Contact Groups</title>
|
12
|
+
<link rel='alternate' type='text/html'
|
13
|
+
href='http://www.google.com/' />
|
14
|
+
<link rel='http://schemas.google.com/g/2005#feed'
|
15
|
+
type='application/atom+xml'
|
16
|
+
href='http://www.google.com/m8/feeds/groups/jo%40gmail.com/thin' />
|
17
|
+
<link rel='http://schemas.google.com/g/2005#post'
|
18
|
+
type='application/atom+xml'
|
19
|
+
href='http://www.google.com/m8/feeds/groups/jo%40gmail.com/thin' />
|
20
|
+
<link rel='http://schemas.google.com/g/2005#batch'
|
21
|
+
type='application/atom+xml'
|
22
|
+
href='http://www.google.com/m8/feeds/groups/jo%40gmail.com/thin/batch' />
|
23
|
+
<link rel='self'
|
24
|
+
type='application/atom+xml'
|
25
|
+
href='http://www.google.com/m8/feeds/groups/jo%40gmail.com/thin?max-results=25' />
|
26
|
+
<author>
|
27
|
+
<name>Jo March</name>
|
28
|
+
<email>jo@gmail.com</email>
|
29
|
+
</author>
|
30
|
+
<generator version='1.0'
|
31
|
+
uri='http://www.google.com/m8/feeds'>Contacts</generator>
|
32
|
+
<openSearch:totalResults>5</openSearch:totalResults>
|
33
|
+
<openSearch:startIndex>1</openSearch:startIndex>
|
34
|
+
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
|
35
|
+
<entry gd:etag='"YDwqeyI."'>
|
36
|
+
<id>http://www.google.com/m8/feeds/groups/jo%40gmail.com/base/6</id>
|
37
|
+
<updated>1970-01-01T00:00:00.000Z</updated>
|
38
|
+
<category scheme='http://schemas.google.com/g/2005#kind'
|
39
|
+
term='http://schemas.google.com/contact/2008#group' />
|
40
|
+
<title>System Group: My Contacts</title>
|
41
|
+
<content>System Group: My Contacts</content>
|
42
|
+
<link rel='self' type='application/atom+xml'
|
43
|
+
href='http://www.google.com/m8/feeds/groups/jo%40gmail.com/thin/6' />
|
44
|
+
<gContact:systemGroup id='Contacts' />
|
45
|
+
</entry>
|
46
|
+
<entry gd:etag='"Rn05fDVSLyp7ImA9WxRbGEUORQM."'>
|
47
|
+
<id>http://www.google.com/m8/feeds/groups/jo%40gmail.com/base/68f415478ba1aa69</id>
|
48
|
+
<updated>2008-12-10T04:44:37.324Z</updated>
|
49
|
+
<category scheme='http://schemas.google.com/g/2005#kind'
|
50
|
+
term='http://schemas.google.com/contact/2008#group' />
|
51
|
+
<title>joggers</title>
|
52
|
+
<content>joggers</content>
|
53
|
+
<link rel='self' type='application/atom+xml'
|
54
|
+
href='http://www.google.com/m8/feeds/groups/jo%40gmail.com/thin/68f415478ba1aa69' />
|
55
|
+
<link rel='edit' type='application/atom+xml'
|
56
|
+
href='http://www.google.com/m8/feeds/groups/jo%40gmail.com/thin/68f415478ba1aa69' />
|
57
|
+
</entry>
|
58
|
+
</feed>
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class BaseTester < Base
|
4
|
+
CATEGORY_TERM = "i'm not used here"
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Base do
|
8
|
+
it "should not be possible to create" do
|
9
|
+
lambda {
|
10
|
+
Base.new(wrapper, 'some xml')
|
11
|
+
}.should raise_error(/cannot create instance/i)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "with an XML document" do
|
15
|
+
before(:each) do
|
16
|
+
@t = BaseTester.new(wrapper)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should default namespace to document default" do
|
20
|
+
node = @t.insert_xml 'tag'
|
21
|
+
node.namespace.href.should == 'http://www.w3.org/2005/Atom'
|
22
|
+
@t.xpath('xmlns:tag').should have(1).node
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should set namespace when specified in tag" do
|
26
|
+
node = @t.insert_xml 'gd:extendedProperty'
|
27
|
+
node.namespace.href.should == 'http://schemas.google.com/g/2005'
|
28
|
+
@t.xpath('gd:extendedProperty').should have(1).node
|
29
|
+
|
30
|
+
node = @t.insert_xml 'gContact:birthday'
|
31
|
+
node.namespace.href.should == 'http://schemas.google.com/contact/2008'
|
32
|
+
@t.xpath('gContact:birthday').should have(1).node
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should raise on unknown namespace" do
|
36
|
+
lambda {
|
37
|
+
@t.insert_xml 'unknown:foo'
|
38
|
+
}.should raise_error(/unknown namespace/i)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should also set attributes if given" do
|
42
|
+
node = @t.insert_xml 'tag', :foo => 'bar'
|
43
|
+
node['foo'].should == 'bar'
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should allow removing xml" do
|
47
|
+
@t.insert_xml 'gd:extendedProperty'
|
48
|
+
@t.xpath('./gd:extendedProperty').should have(1).node
|
49
|
+
|
50
|
+
@t.remove_xml 'gd:extendedProperty'
|
51
|
+
@t.xpath('./gd:extendedProperty').should have(:no).nodes
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "prepare for batch operation" do
|
56
|
+
before(:all) do
|
57
|
+
@t = BaseTester.new(wrapper, parsed_asset('contacts_full').at('feed > entry'))
|
58
|
+
@batch = @t.entry_for_batch(:update)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not share the same document" do
|
62
|
+
@batch.document.should_not == @t.xml.document
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should create a duplicate node without link tags" do
|
66
|
+
@batch.xpath('./xmlns:link').should be_empty
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should remove the updated tag (not useful when updating)" do
|
70
|
+
@batch.xpath('./xmlns:updated').should be_empty
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should be possible to combine feed_for_batch and entry_for_batch" do
|
74
|
+
feed = BaseTester.feed_for_batch
|
75
|
+
feed << @t.entry_for_batch(:update)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|