atom-tools 2.0.3 → 2.0.4

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/Rakefile CHANGED
@@ -7,81 +7,74 @@ require "spec/rake/spectask"
7
7
  require "rake/clean"
8
8
 
9
9
  NAME = "atom-tools"
10
- VERS = "2.0.3"
11
-
12
- # the following from markaby-0.5's tools/rakehelp
13
- def setup_tests
14
- Rake::TestTask.new do |t|
15
- t.libs << "test"
16
- t.test_files = FileList['test/test*.rb']
17
- t.verbose = true
18
- end
19
- end
10
+ VERS = "2.0.4"
11
+
12
+ task :default => [:spec]
13
+
14
+ # For historical reasons, atom-tools has both rspec specs and test/unit tests.
15
+ # This is silly (and there's a lot of duplication), but I have better things to
16
+ # do than rewrite the tests.
17
+ #
18
+ # Ideally all the tests should be runnable with one command, but for now you
19
+ # have to run "rake test" and "rake spec"
20
20
 
21
- def setup_rdoc files
22
- Rake::RDocTask.new do |rdoc|
23
- rdoc.title = NAME + " documentation"
24
- rdoc.rdoc_dir = 'doc'
25
- rdoc.options << '--line-numbers'
26
- rdoc.options << '--inline-source'
27
- rdoc.rdoc_files.add(files)
28
- end
21
+ # spec task
22
+ desc 'Run all specs (see also "test" task)'
23
+ Spec::Rake::SpecTask.new('spec')
24
+
25
+ # test task
26
+ Rake::TestTask.new do |t|
27
+ t.libs << "test"
28
+ t.test_files = FileList['test/test*.rb']
29
+ t.verbose = true
29
30
  end
30
31
 
31
- def setup_gem(pkg_name, pkg_version, author, summary, dependencies, test_file)
32
- pkg_version = pkg_version
33
- pkg_name = pkg_name
34
- pkg_file_name = "#{pkg_name}-#{pkg_version}"
35
-
36
- spec = Gem::Specification.new do |s|
37
- s.name = pkg_name
38
- s.version = pkg_version
39
- s.platform = Gem::Platform::RUBY
40
- s.author = author
41
- s.email = 'whateley@gmail.com'
42
- s.homepage = 'http://code.necronomicorp.com/atom-tools'
43
- s.rubyforge_project = 'ibes'
44
- s.summary = summary
45
- s.test_file = test_file
46
- s.has_rdoc = true
47
- s.extra_rdoc_files = [ "README" ]
48
- dependencies.each do |dep|
49
- s.add_dependency(*dep)
50
- end
51
- s.files = %w(COPYING README Rakefile setup.rb) +
52
- Dir.glob("{bin,doc,test,lib}/**/*") +
53
- Dir.glob("ext/**/*.{h,c,rb}") +
54
- Dir.glob("examples/**/*.rb") +
55
- Dir.glob("tools/*.rb")
56
-
57
- s.require_path = "lib"
58
- s.extensions = FileList["ext/**/extconf.rb"].to_a
59
-
60
- s.bindir = "bin"
61
- end
62
-
63
- Rake::GemPackageTask.new(spec) do |p|
64
- p.gem_spec = spec
65
- p.need_tar = true
66
- end
67
-
68
- task :install do
69
- sh %{rake package}
70
- sh %{gem install pkg/#{pkg_name}-#{pkg_version}}
71
- end
32
+ Rake::RDocTask.new do |rdoc|
33
+ rdoc.title = 'atom-tools documentation'
34
+ rdoc.main = 'README'
35
+ rdoc.rdoc_files.include 'README', 'lib/**/*.rb'
36
+ rdoc.rdoc_dir = 'doc'
72
37
  end
73
38
 
74
- task :default => [:spec]
75
- desc 'Run all specs and generate report for spec results and code coverage'
76
- Spec::Rake::SpecTask.new('spec') do |t|
77
- t.spec_opts = ["--format", "html:report.html", '--diff']
78
- t.fail_on_error = false
79
- t.rcov = true
39
+ spec = Gem::Specification.new do |s|
40
+ s.name = NAME
41
+ s.version = VERS
42
+ s.platform = Gem::Platform::RUBY
43
+ s.author = "Brendan Taylor"
44
+ s.email = 'whateley@gmail.com'
45
+ s.homepage = 'http://github.com/bct/atom-tools/wikis'
46
+
47
+ s.rubyforge_project = 'ibes'
48
+
49
+ s.summary = 'Tools for working with Atom Entries, Feeds and Collections.'
50
+ s.description = 'atom-tools is an all-in-one Atom library. It parses and builds Atom (RFC 4287) entries and feeds, and manipulates Atom Publishing Protocol (RFC 5023) Collections.
51
+
52
+ It also comes with a set of commandline utilities for working with AtomPub Collections.
53
+
54
+ It is not the fastest Ruby Atom library, but it is comprehensive and makes handling extensions to the Atom format very easy.'
55
+
56
+ s.test_file = "test/runtests.rb" # TODO: should have the spec here instead?
57
+ s.has_rdoc = true
58
+ s.extra_rdoc_files = [ "README" ]
59
+
60
+ s.files = %w(COPYING README Rakefile setup.rb) +
61
+ Dir.glob("{bin,doc,test,spec,lib}/**/*") +
62
+ Dir.glob("ext/**/*.{h,c,rb}") +
63
+ Dir.glob("examples/**/*.rb") +
64
+ Dir.glob("tools/*.rb")
65
+
66
+ s.require_path = "lib"
67
+ s.extensions = FileList["ext/**/extconf.rb"].to_a
68
+
69
+ s.bindir = "bin"
80
70
  end
81
71
 
82
- setup_tests
83
- setup_rdoc ['README', 'lib/**/*.rb']
72
+ Rake::GemPackageTask.new(spec) do |p|
73
+ p.gem_spec = spec
74
+ p.need_tar = true
75
+ end
84
76
 
85
- summary = "Tools for working with Atom Entries, Feeds and Collections"
86
- test_file = "test/runtests.rb"
87
- setup_gem(NAME, VERS, "Brendan Taylor", summary, [], test_file)
77
+ task :install do
78
+ sh %{rake package}
79
+ sh %{gem install pkg/#{NAME}-#{VERS}}
80
+ end
@@ -8,6 +8,7 @@ Usage: atom-post [options] destination [file]
8
8
  'file' is the path to a file to POST (default is stdin)
9
9
  =end
10
10
 
11
+ require 'rubygems'
11
12
  require 'atom/tools'
12
13
  include Atom::Tools
13
14
 
@@ -507,7 +507,7 @@ module Atom # :nodoc:
507
507
  @attrs
508
508
  end
509
509
 
510
- self.class.initters do |init|
510
+ self.class.run_initters do |init|
511
511
  self.instance_eval &init
512
512
  end
513
513
 
@@ -521,9 +521,8 @@ module Atom # :nodoc:
521
521
  @on_init << block
522
522
  end
523
523
 
524
- def self.initters &block
525
- @on_init ||= []
526
- @on_init.each &block
524
+ def self.run_initters &block
525
+ @on_init.each(&block) if @on_init
527
526
  end
528
527
 
529
528
  # appends an element named 'name' in namespace 'ns' to 'root'
@@ -644,4 +643,43 @@ module Atom # :nodoc:
644
643
  class Contributor < Atom::Person
645
644
  is_atom_element :contributor
646
645
  end
646
+
647
+ module HasLinks
648
+ def HasLinks.included(klass)
649
+ klass.atom_elements :link, :links, Atom::Link
650
+ end
651
+
652
+ def find_link(criteria)
653
+ self.links.find do |l|
654
+ criteria.all? { |k,v| l.send(k) == v }
655
+ end
656
+ end
657
+ end
658
+
659
+ module HasCategories
660
+ def HasCategories.included(klass)
661
+ klass.atom_elements :category, :categories, Atom::Category
662
+ end
663
+
664
+ # categorize the entry with each of an array or a space-separated
665
+ # string
666
+ def tag_with(tags, delimiter = ' ')
667
+ return if not tags or tags.empty?
668
+
669
+ tag_list = unless tags.is_a?(String)
670
+ tags
671
+ else
672
+ tags = tags.split(delimiter)
673
+ tags.map! { |t| t.strip }
674
+ tags.reject! { |t| t.empty? }
675
+ tags.uniq
676
+ end
677
+
678
+ tag_list.each do |tag|
679
+ unless categories.any? { |c| c.term == tag }
680
+ categories.new :term => tag
681
+ end
682
+ end
683
+ end
684
+ end
647
685
  end
@@ -3,7 +3,21 @@ require "rexml/document"
3
3
  require "atom/element"
4
4
  require "atom/text"
5
5
 
6
+ require 'atom/feed'
7
+
6
8
  module Atom
9
+ # this is just a forward declaration since atom/entry includes atom/feed and vice-versa.
10
+ class Feed < Atom::Element # :nodoc:
11
+ end
12
+
13
+ class Source < Atom::Feed
14
+ is_atom_element :source
15
+
16
+ # TODO: this shouldn't be necessary, but on_init doesn't get inherited the
17
+ # way I would like it to.
18
+ @on_init = Atom::Feed.instance_variable_get '@on_init'
19
+ end
20
+
7
21
  class Control < Atom::Element
8
22
  attr_accessor :draft
9
23
 
@@ -21,45 +35,6 @@ module Atom
21
35
  end
22
36
  end
23
37
 
24
- module HasCategories
25
- def HasCategories.included(klass)
26
- klass.atom_elements :category, :categories, Atom::Category
27
- end
28
-
29
- # categorize the entry with each of an array or a space-separated
30
- # string
31
- def tag_with(tags, delimiter = ' ')
32
- return if not tags or tags.empty?
33
-
34
- tag_list = unless tags.is_a?(String)
35
- tags
36
- else
37
- tags = tags.split(delimiter)
38
- tags.map! { |t| t.strip }
39
- tags.reject! { |t| t.empty? }
40
- tags.uniq
41
- end
42
-
43
- tag_list.each do |tag|
44
- unless categories.any? { |c| c.term == tag }
45
- categories.new :term => tag
46
- end
47
- end
48
- end
49
- end
50
-
51
- module HasLinks
52
- def HasLinks.included(klass)
53
- klass.atom_elements :link, :links, Atom::Link
54
- end
55
-
56
- def find_link(criteria)
57
- self.links.find do |l|
58
- criteria.all? { |k,v| l.send(k) == v }
59
- end
60
- end
61
- end
62
-
63
38
  # An individual entry in a feed. As an Atom::Element, it can be
64
39
  # manipulated using accessors for each of its child elements. You
65
40
  # should be able to set them using an instance of any class that
@@ -94,7 +69,7 @@ module Atom
94
69
 
95
70
  atom_element :rights, Atom::Rights
96
71
 
97
- # element :source, Atom::Feed # XXX complicated, eg. serialization
72
+ atom_element :source, Atom::Source
98
73
 
99
74
  atom_time :published
100
75
  atom_time :updated
@@ -5,6 +5,10 @@ require "atom/entry"
5
5
  require "atom/http"
6
6
 
7
7
  module Atom
8
+ # this is just a forward declaration since atom/entry includes atom/feed and vice-versa.
9
+ class Entry < Atom::Element # :nodoc:
10
+ end
11
+
8
12
  class FeedGone < RuntimeError # :nodoc:
9
13
  end
10
14
 
@@ -17,7 +17,7 @@ class String # :nodoc:
17
17
  end
18
18
 
19
19
  module Atom
20
- TOOLS_VERSION = '2.0.3'
20
+ TOOLS_VERSION = '2.0.4'
21
21
  UA = "atom-tools " + TOOLS_VERSION
22
22
 
23
23
  module DigestAuth
@@ -0,0 +1,353 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ require 'atom/entry'
4
+
5
+ module TestsXML
6
+ def read_entry_xml xpath
7
+ REXML::XPath.first(@entry.to_xml, xpath, { 'atom' => Atom::NS, 'app' => Atom::PP_NS })
8
+ end
9
+ end
10
+
11
+ describe Atom::Entry do
12
+ describe 'when parsing' do
13
+ before(:each) do
14
+ @entry = Atom::Entry.parse(fixtures(:entry))
15
+ @empty_entry = '<entry xmlns="http://www.w3.org/2005/Atom" />'
16
+ end
17
+
18
+ it 'should read & parse input from an IO object' do
19
+ input = mock('IO')
20
+ input.should_receive(:read).and_return(@empty_entry)
21
+ Atom::Entry.parse(input).should be_an_instance_of(Atom::Entry)
22
+ end
23
+
24
+ it 'should read & parse input from a string' do
25
+ input = mock('string')
26
+ input.should_receive(:to_s).and_return(@empty_entry)
27
+ Atom::Entry.parse(input).should be_an_instance_of(Atom::Entry)
28
+ end
29
+
30
+ it 'should raise ParseError when invalid entry' do
31
+ lambda { Atom::Entry.parse('<entry/>') }.should raise_error(Atom::ParseError)
32
+ end
33
+
34
+ it 'should parse title element correctly' do
35
+ @entry.title.should be_is_a(Atom::Text)
36
+ @entry.title['type'].should == 'text'
37
+ @entry.title.to_s.should == 'Atom draft-07 snapshot'
38
+ end
39
+
40
+ it 'should parse id element correctly' do
41
+ @entry.id.should == 'tag:example.org,2003:3.2397'
42
+ end
43
+
44
+ it 'should parse updated element correctly' do
45
+ @entry.updated.should == Time.parse('2005-07-31T12:29:29Z')
46
+ end
47
+
48
+ it 'should parse published element correctly' do
49
+ @entry.published.should == Time.parse('2003-12-13T08:29:29-04:00')
50
+ end
51
+
52
+ it 'should parse app:edited element correctly' do
53
+ @entry.edited.should == Time.parse('2005-07-31T12:29:29Z')
54
+ end
55
+
56
+ it 'should parse app:control/draft element correctly' do
57
+ @entry.draft?.should be_true
58
+ end
59
+
60
+ it 'should parse rights element correctly' do
61
+ @entry.rights.should be_is_a(Atom::Text)
62
+ @entry.rights['type'].should == 'text'
63
+ @entry.rights.to_s.should == 'Copyright (c) 2003, Mark Pilgrim'
64
+ end
65
+
66
+ it 'should parse author element correctly' do
67
+ @entry.authors.length.should == 1
68
+ @entry.authors.first.name.should == 'Mark Pilgrim'
69
+ @entry.authors.first.email.should == 'f8dy@example.com'
70
+ @entry.authors.first.uri.should == 'http://example.org/'
71
+ end
72
+
73
+ it 'should parse contributor element correctly' do
74
+ @entry.contributors.length.should == 2
75
+ @entry.contributors.first.name.should == 'Sam Ruby'
76
+ @entry.contributors[1].name.should == 'Joe Gregorio'
77
+ end
78
+
79
+ it 'should parse content element correctly' do
80
+ @entry.content.should be_an_instance_of(Atom::Content)
81
+ @entry.content['type'].should == 'xhtml'
82
+ @entry.content.base.should == 'http://diveintomark.org/'
83
+ @entry.content.to_s.strip.should == '<p><i>[Update: The Atom draft is finished.]</i></p>'
84
+ end
85
+
86
+ it 'should parse summary element correctly' do
87
+ @entry.summary['type'].should == 'text'
88
+ @entry.summary.to_s.should == 'Some text.'
89
+ end
90
+
91
+ it 'should parse links element correctly' do
92
+ @entry.links.length.should == 2
93
+ alternates = @entry.links.select { |l| l['rel'] == 'alternate' }
94
+ alternates.length.should == 1
95
+ alternates.first['href'].should == 'http://example.org/2005/04/02/atom'
96
+ alternates.first['type'].should == 'text/html'
97
+ @entry.links.last['rel'].should == 'enclosure'
98
+ @entry.links.last['href'].should == 'http://example.org/audio/ph34r_my_podcast.mp3'
99
+ @entry.links.last['type'].should == 'audio/mpeg'
100
+ end
101
+
102
+ it 'should parse category element correctly' do
103
+ @entry.categories.first['term'].should == 'ann'
104
+ @entry.categories.first['scheme'].should == 'http://example.org/cats'
105
+ end
106
+
107
+ it 'should parse source element correctly' do
108
+ @entry.source.title.to_s.should == 'Atom Sample Feed'
109
+ @entry.source.id.should == 'tag:example.org,2003:/'
110
+
111
+ @entry.source.links.length.should == 1
112
+ @entry.source.links.first.rel == 'self'
113
+
114
+ @entry.source.authors.length.should == 0
115
+
116
+ @entry.source.contributors.length.should == 1
117
+ @entry.source.contributors.first.name == 'Mark Pilgrim'
118
+ end
119
+ end
120
+
121
+ describe 'title element' do
122
+ before(:each) do
123
+ @entry = Atom::Entry.new
124
+ end
125
+
126
+ it 'should be nil if not defined' do
127
+ @entry.title.should be_nil
128
+
129
+ @entry.title.to_s.should == ''
130
+ end
131
+
132
+ it 'should accept a simple string' do
133
+ @entry.title = '<clever thing here>'
134
+
135
+ @entry.title.type.should == 'text'
136
+
137
+ @entry.title.to_s.should == '<clever thing here>'
138
+ @entry.title.html.should =~ /^&lt;clever thing/
139
+ @entry.title.to_xml.to_s.should =~ /&lt;clever thing/
140
+ end
141
+
142
+ it 'should accept an HTML string' do
143
+ @entry.title = 'even <em>cleverer</em>'
144
+ @entry.title.type = 'html'
145
+
146
+ @entry.title.type.should == 'html'
147
+
148
+ @entry.title.to_s.should =~ /even <em>clever/
149
+ @entry.title.html.should =~ /even <em>clever/
150
+ @entry.title.to_xml.to_s.should =~ /even &lt;em/
151
+ end
152
+
153
+ it 'should accept an XHTML string' do
154
+ @entry.title = 'the <strong>cleverest</strong>'
155
+ @entry.title.type = 'xhtml'
156
+
157
+ @entry.title.to_xml.to_s.should =~ /w3.org\/1999\/xhtml.>the <strong>/
158
+ @entry.title.html.should =~ /the <strong>cleverest/
159
+ end
160
+
161
+ it 'should reject an ill-formed XHTML string' do
162
+ @entry.title = 'the <strong>cleverest'
163
+ lambda { @entry.title.type = 'xhtml' }.should raise_error(Atom::ParseError)
164
+ end
165
+
166
+ it 'should accept something like Atom::Text' do
167
+ title = Atom::Title.new '<3'
168
+
169
+ @entry.title = title
170
+ @entry.title.type.should == 'text'
171
+
172
+ @entry.title.to_xml.to_s.should =~ /&lt;3/
173
+ end
174
+ end
175
+
176
+ describe 'updated element' do
177
+ before(:each) do
178
+ @entry = Atom::Entry.new
179
+ end
180
+
181
+ it 'should be nil if not defined' do
182
+ @entry.updated.should be_nil
183
+ end
184
+
185
+ it 'should be definable' do
186
+ @entry.updated = '1990-04-07'
187
+ @entry.updated.should == Time.parse('1990-04-07')
188
+ end
189
+
190
+ it 'should be an xsd:DateTime' do
191
+ @entry.updated = '1990-04-07'
192
+ @entry.updated.to_s.should =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
193
+ end
194
+
195
+ it 'should be declarable as updated using #updated!' do
196
+ @entry.updated!
197
+ @entry.updated.should > Time.parse('1990-04-07')
198
+ end
199
+ end
200
+
201
+ describe 'app:edited element' do
202
+ include TestsXML
203
+
204
+ before(:each) do
205
+ @entry = Atom::Entry.new
206
+ end
207
+
208
+ it 'should be nil if not defined' do
209
+ @entry.edited.should be_nil
210
+ end
211
+
212
+ it 'should be definable' do
213
+ @entry.edited = '1990-04-07'
214
+ @entry.edited.should == Time.parse('1990-04-07')
215
+ end
216
+
217
+ it 'should be an xsd:DateTime' do
218
+ @entry.edited = '1990-04-07'
219
+ @entry.edited.to_s.should =~ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
220
+ end
221
+
222
+ it 'should have APP namespace' do
223
+ @entry.edited = '1990-04-07'
224
+ read_entry_xml('app:edited').namespace.should == Atom::PP_NS
225
+ end
226
+
227
+ it 'should be declarable as edited using #edited!' do
228
+ @entry.edited!
229
+ @entry.edited.should > Time.parse('1990-04-07')
230
+ end
231
+ end
232
+
233
+ describe 'category element' do
234
+ before(:each) do
235
+ @entry = Atom::Entry.new
236
+ end
237
+
238
+ it 'should have no category on intializing' do
239
+ @entry.categories.should be_empty
240
+ end
241
+
242
+ it 'should increase total count when adding a new category' do
243
+ @count = @entry.categories.length
244
+ @entry.categories.new['term'] = 'foo'
245
+ @entry.categories.length.should == @count + 1
246
+ end
247
+
248
+ it 'should find category' do
249
+ category = @entry.categories.new
250
+ category['scheme'] = 'http://example.org/categories'
251
+ category['term'] = 'bar'
252
+ @entry.categories.select { |c| c['scheme'] == 'http://example.org/categories' }.should == [category]
253
+ end
254
+
255
+ describe 'when using tags' do
256
+ before(:each) do
257
+ @tags = %w(chunky bacon ruby)
258
+ end
259
+
260
+ it 'should set categories from an array of tags' do
261
+ @entry.tag_with(@tags)
262
+ @entry.categories.length.should == 3
263
+ @tags.each { |tag| @entry.categories.any? { |c| c['term'] == tag }.should be_true }
264
+ end
265
+
266
+ it 'should set categories from a space-sperated string of tags' do
267
+ @entry.tag_with(@tags.join(' '))
268
+ @entry.categories.length.should == 3
269
+ @tags.each { |tag| @entry.categories.any? { |c| c['term'] == tag }.should be_true }
270
+ end
271
+
272
+ it 'should be possible to specify the delimiter when passing tags as a string' do
273
+ @entry.tag_with(@tags.join(','), ',')
274
+ @entry.categories.length.should == 3
275
+ @tags.each { |tag| @entry.categories.any? { |c| c['term'] == tag }.should be_true }
276
+ end
277
+
278
+ it 'should create a category only once' do
279
+ @entry.tag_with(@tags)
280
+ @entry.tag_with(@tags.first)
281
+ @entry.categories.length.should == 3
282
+ end
283
+ end
284
+ end
285
+
286
+ describe 'edit url' do
287
+ before(:each) do
288
+ @entry = Atom::Entry.new
289
+ end
290
+
291
+ it 'should be nil on initializing' do
292
+ @entry.edit_url.should be_nil
293
+ end
294
+
295
+ it 'should be easily definable' do
296
+ @entry.edit_url = 'http://example.org/entries/foo'
297
+ @entry.edit_url.should == 'http://example.org/entries/foo'
298
+ end
299
+
300
+ it 'should not erase other links' do
301
+ link = @entry.links.new :rel => 'related', :href => 'http://example.org'
302
+
303
+ @entry.edit_url = 'http://example.com/entries/foo'
304
+ @entry.links.length.should == 2
305
+ @entry.links.should include(link)
306
+ end
307
+
308
+ it 'should accept a URI object' do
309
+ @entry.edit_url = URI.parse('http://example.com/entries/foo')
310
+ @entry.to_s.should =~ /example.com\/entries/
311
+ end
312
+ end
313
+
314
+ describe 'draft element' do
315
+ include TestsXML
316
+
317
+ before(:each) do
318
+ @entry = Atom::Entry.new
319
+ end
320
+
321
+ it 'should not be a draft by default' do
322
+ @entry.should_not be_draft
323
+ end
324
+
325
+ it 'should be definable using draft=' do
326
+ @entry.draft = true
327
+ @entry.should be_draft
328
+ @entry.draft = false
329
+ @entry.should_not be_draft
330
+ end
331
+
332
+ it 'should be declarable as a draft using #draft!' do
333
+ @entry.draft!
334
+ @entry.should be_draft
335
+ end
336
+
337
+ it 'should have APP namespace' do
338
+ @entry.draft!
339
+ read_entry_xml('app:control/app:draft').namespace.should == Atom::PP_NS
340
+ end
341
+ end
342
+
343
+
344
+ describe 'extensions' do
345
+ before(:each) do
346
+ @entry = Atom::Entry.parse(fixtures('entry-w-ext'))
347
+ end
348
+
349
+ it 'should preserve namespaces' do
350
+ @entry.to_s.should =~ /purl/
351
+ end
352
+ end
353
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ require 'atom/entry'
4
+
5
+ module Atom
6
+ THR_NS = "http://purl.org/syndication/thread/1.0"
7
+ SLUG_NS = 'http://example.org/ns/slug'
8
+
9
+ class InReplyTo < Atom::Element
10
+ is_element THR_NS, :"in-reply-to"
11
+
12
+ atom_attrb :ref
13
+ atom_attrb :href
14
+ atom_attrb :type
15
+ atom_attrb :source
16
+ end
17
+
18
+ class Entry
19
+ attrb ['sl', SLUG_NS], 'slug'
20
+ element ['thr', THR_NS], :"in-reply-to", InReplyTo
21
+ end
22
+ end
23
+
24
+ describe Atom::Entry do
25
+ it 'should correctly write extension attributes' do
26
+ entry = Atom::Entry.new
27
+ entry.slug = 'hallo'
28
+
29
+ entry.to_s.should =~ /sl:slug/
30
+ entry.to_s.should =~ /xmlns:sl='#{Atom::SLUG_NS}'/
31
+ end
32
+
33
+ describe 'in-reply-to' do
34
+ it 'should be written with the correct namespace' do
35
+ entry = Atom::Entry.new
36
+ entry.in_reply_to = { :ref => 'http://example.org/some-entry' }
37
+
38
+ entry.to_s.should =~ /ref='http:\/\/example.org/
39
+ entry.to_s.should =~ Regexp.new(Atom::THR_NS)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ require 'atom/feed'
4
+
5
+ describe Atom::Entry do
6
+ describe 'extensions' do
7
+ before(:each) do
8
+ @feed = Atom::Feed.parse(fixtures('feed-w-ext'))
9
+ end
10
+
11
+ it 'should preserve namespaces' do
12
+ @feed.to_s.should =~ /purl/
13
+
14
+ feed2 = Atom::Feed.new
15
+ feed2.merge! @feed
16
+
17
+ feed2.to_s.should =~ /purl/
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ <entry xmlns="http://www.w3.org/2005/Atom"
2
+ xml:base='http://www.example.org/'
3
+ xmlns:thr='http://purl.org/syndication/thread/1.0'
4
+ xml:lang='en-us'>
5
+ <title>Extendy</title>
6
+ <link rel='replies' thr:count='2' type='application/xhtml+xml' href='Extendy#comments' />
7
+ <id>http://www.example.org/Extendy.atom</id>
8
+ <published>2008-03-15T02:00:00-07:00</published>
9
+ <updated>2008-03-15T18:10:25-07:00</updated>
10
+ <content type='xhtml'>
11
+ <div xmlns='http://www.w3.org/1999/xhtml'>
12
+ <p>Extensions ahoy!</p>
13
+ </div>
14
+ </content>
15
+ </entry>
@@ -0,0 +1,42 @@
1
+ <entry xmlns="http://www.w3.org/2005/Atom"
2
+ xmlns:app="http://www.w3.org/2007/app">
3
+ <title>Atom draft-07 snapshot</title>
4
+ <link rel="alternate" type="text/html"
5
+ href="http://example.org/2005/04/02/atom"/>
6
+ <link rel="enclosure" type="audio/mpeg" length="1337"
7
+ href="http://example.org/audio/ph34r_my_podcast.mp3"/>
8
+ <source>
9
+ <title>Atom Sample Feed</title>
10
+ <id>tag:example.org,2003:/</id>
11
+ <link rel="self" href="http://example.org/feed"/>
12
+ <contributor>
13
+ <name>Mark Pilgrim</name>
14
+ </contributor>
15
+ </source>
16
+ <rights>Copyright (c) 2003, Mark Pilgrim</rights>
17
+ <id>tag:example.org,2003:3.2397</id>
18
+ <updated>2005-07-31T12:29:29Z</updated>
19
+ <published>2003-12-13T08:29:29-04:00</published>
20
+ <app:edited>2005-07-31T12:29:29Z</app:edited>
21
+ <app:control><app:draft>yes</app:draft></app:control>
22
+ <author>
23
+ <name>Mark Pilgrim</name>
24
+ <uri>http://example.org/</uri>
25
+ <email>f8dy@example.com</email>
26
+ </author>
27
+ <contributor>
28
+ <name>Sam Ruby</name>
29
+ </contributor>
30
+ <contributor>
31
+ <name>Joe Gregorio</name>
32
+ </contributor>
33
+ <summary>Some text.</summary>
34
+ <category scheme="http://example.org/cats" term="ann" />
35
+ <content type="xhtml" xml:lang="en"
36
+ xml:base="http://diveintomark.org/">
37
+ <div xmlns="http://www.w3.org/1999/xhtml">
38
+ <p><i>[Update: The Atom draft is finished.]</i></p>
39
+ </div>
40
+ </content>
41
+ </entry>
42
+
@@ -0,0 +1,33 @@
1
+ <feed xmlns='http://www.w3.org/2005/Atom'
2
+ xmlns:thr='http://purl.org/syndication/thread/1.0'
3
+ xml:base='http://www.tbray.org/ongoing/ongoing.atom'
4
+ xml:lang='en-us'>
5
+ <title>ongoing</title>
6
+ <id>http://www.tbray.org/ongoing/</id>
7
+ <link href='./' />
8
+ <link rel='self' href='' />
9
+ <link rel='replies' thr:count='101' href='/home/tbray.org/www/html/ongoing/comments.atom' />
10
+ <logo>rsslogo.jpg</logo>
11
+ <icon>/favicon.ico</icon>
12
+ <updated>2008-03-17T02:04:49-07:00</updated>
13
+ <author><name>Tim Bray</name></author>
14
+ <subtitle>ongoing fragmented essay by Tim Bray</subtitle>
15
+ <rights>All content written by Tim Bray and photos by Tim Bray Copyright Tim Bray, some rights reserved, see /ongoing/misc/Copyright</rights>
16
+ <generator uri='/misc/Colophon'>Generated from XML source code using Perl, Expat, Emacs, Mysql, Ruby, Java, and ImageMagick. Industrial-strength technology, baby.</generator>
17
+ <entry xml:base='When/200x/2008/03/13/'>
18
+ <title>Maui Bound</title>
19
+ <link href='Maui' />
20
+ <link rel='replies' thr:count='1' type='application/xhtml+xml' href='Maui#comments' />
21
+ <id>http://www.tbray.org/ongoing/When/200x/2008/03/13/Maui</id>
22
+ <published>2008-03-13T02:00:00-07:00</published>
23
+ <updated>2008-03-13T17:23:47-07:00</updated>
24
+ <category scheme='http://www.tbray.org/ongoing/What/' term='The World/Places/Hawaii' />
25
+ <category scheme='http://www.tbray.org/ongoing/What/' term='The World' />
26
+ <category scheme='http://www.tbray.org/ongoing/What/' term='Places' />
27
+ <category scheme='http://www.tbray.org/ongoing/What/' term='Hawaii' />
28
+ <content type='xhtml'><div xmlns='http://www.w3.org/1999/xhtml'>
29
+ <p>I’m off with the family tomorrow for ten days in Maui, 15th through the
30
+ 24th. We’ll be meeting an Aussie friend. Anyone else I know who’ll be there,
31
+ drop me a line and we’ll get together for a Mai Tai.</p>
32
+ </div></content></entry>
33
+ </feed>
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0"?>
2
+ <service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:xhtml="http://www.w3.org/1999/xhtml">
3
+ <workspace title="Yulup Demo">
4
+ <atom:title type="text">Yulup Demo</atom:title>
5
+
6
+ <collection title="Releases" href="../entries/?yanel.resource.viewid=atom">
7
+ <atom:title type="html">Early Yulup &lt;i>Releases&lt;/i></atom:title>
8
+ </collection>
9
+ </workspace>
10
+
11
+ <workspace title="Yulup Website">
12
+ <atom:title type="text">Yulup Website</atom:title>
13
+
14
+ <collection title="Releases" href="http://www.yulup.org/download/release-atom-entries/?yanel.resource.viewid=atom">
15
+ <atom:title type="xhtml"><xhtml:div>Yulup <xhtml:b>Releases</xhtml:b>
16
+ </xhtml:div>
17
+ </atom:title>
18
+
19
+ </collection>
20
+ </workspace>
21
+ </service>
@@ -0,0 +1,36 @@
1
+ <?xml version="1.0" encoding='utf-8'?>
2
+ <service xmlns="http://www.w3.org/2007/app"
3
+ xmlns:atom="http://www.w3.org/2005/Atom">
4
+ <workspace>
5
+ <atom:title>Main Site</atom:title>
6
+ <collection
7
+ href="http://example.org/blog/main" >
8
+ <atom:title>My Blog Entries</atom:title>
9
+ <categories
10
+ href="http://example.com/cats/forMain.cats" />
11
+ </collection>
12
+ <collection
13
+ href="http://example.org/blog/pic" >
14
+ <atom:title>Pictures</atom:title>
15
+ <accept>image/png</accept>
16
+ <accept>image/jpeg</accept>
17
+ <accept>image/gif</accept>
18
+ </collection>
19
+ </workspace>
20
+ <workspace>
21
+ <atom:title>Sidebar Blog</atom:title>
22
+ <collection
23
+ href="http://example.org/sidebar/list" >
24
+ <atom:title>Remaindered Links</atom:title>
25
+ <accept>application/atom+xml;type=entry</accept>
26
+ <categories fixed="yes">
27
+ <atom:category
28
+ scheme="http://example.org/extra-cats/"
29
+ term="joke" />
30
+ <atom:category
31
+ scheme="http://example.org/extra-cats/"
32
+ term="serious" />
33
+ </categories>
34
+ </collection>
35
+ </workspace>
36
+ </service>
@@ -0,0 +1,108 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ require 'atom/service'
4
+
5
+ describe Atom::Service do
6
+ describe 'when parsing' do
7
+ before(:each) do
8
+ @service = Atom::Service.parse(fixtures(:service))
9
+ end
10
+
11
+ it 'should parse workspace elements correctly' do
12
+ @service.workspaces.length.should == 2
13
+ end
14
+
15
+ it 'should absolutize relative hrefs' do
16
+ svc = Atom::Service.parse(
17
+ fixtures('service-w-xhtml-ns'),
18
+ 'http://example.org/introspection/')
19
+
20
+ coll = svc.workspaces.first.collections.first
21
+ coll.href.should == "http://example.org/entries/?yanel.resource.viewid=atom"
22
+ end
23
+
24
+ it 'should parse XHTML outside the default namespace correctly' do
25
+ xhtml_svc = Atom::Service.parse(fixtures('service-w-xhtml-ns'))
26
+
27
+ xhtml_svc.workspaces.length.should == 2
28
+ xhtml_coll = xhtml_svc.workspaces.last.collections.first
29
+ xhtml_coll.title.html.strip.should == 'Yulup <b>Releases</b>'
30
+ end
31
+ end
32
+ end
33
+
34
+ describe Atom::Workspace do
35
+ describe 'when parsing' do
36
+ before(:each) do
37
+ svc = Atom::Service.parse(fixtures(:service))
38
+ @main = svc.workspaces.first
39
+ @sidebar = svc.workspaces.last
40
+ end
41
+
42
+ it 'should parse title element correctly' do
43
+ @main.title.to_s.should == 'Main Site'
44
+ @sidebar.title.to_s.should == 'Sidebar Blog'
45
+ end
46
+
47
+ it 'should parse collection elements correctly' do
48
+ @main.collections.length.should == 2
49
+ @sidebar.collections.length.should == 1
50
+ end
51
+ end
52
+ end
53
+
54
+ describe Atom::Collection do
55
+ describe 'when parsing' do
56
+ before(:each) do
57
+ svc = Atom::Service.parse(fixtures(:service))
58
+ @entries = svc.workspaces.first.collections.first
59
+ @pictures = svc.workspaces.first.collections.last
60
+ @links = svc.workspaces.last.collections.first
61
+ end
62
+
63
+ it 'should parse href correctly' do
64
+ @entries.href.should == 'http://example.org/blog/main'
65
+ @entries.feed.uri.to_s.should == 'http://example.org/blog/main'
66
+
67
+ @pictures.href.should == 'http://example.org/blog/pic'
68
+ @links.href.should == 'http://example.org/sidebar/list'
69
+ end
70
+
71
+ it 'should parse title element correctly' do
72
+ @entries.title.to_s.should == 'My Blog Entries'
73
+ @pictures.title.to_s.should == 'Pictures'
74
+ @links.title.to_s.should == 'Remaindered Links'
75
+ end
76
+
77
+ it 'should parse accept elements correctly' do
78
+ @entries.accepts.should == ['application/atom+xml;type=entry']
79
+ @pictures.accepts.should == ['image/png', 'image/jpeg', 'image/gif']
80
+ @links.accepts.should == ['application/atom+xml;type=entry']
81
+ end
82
+ end
83
+ end
84
+
85
+ describe Atom::Categories do
86
+ describe 'when parsing' do
87
+ before(:each) do
88
+ svc = Atom::Service.parse(fixtures(:service))
89
+ @ool = svc.workspaces.first.collections.first.categories.first
90
+ @il = svc.workspaces.last.collections.first.categories.first
91
+ end
92
+
93
+ it 'should parse out-of-line categories correctly' do
94
+ @ool.href.should == 'http://example.com/cats/forMain.cats'
95
+ end
96
+
97
+ it 'should parse inline categories correctly' do
98
+ @il.fixed.should be_true
99
+
100
+ @il.list.length.should == 2
101
+
102
+ @il.list.first.scheme.should == 'http://example.org/extra-cats/'
103
+ @il.list.first.term.should == 'joke'
104
+
105
+ @il.list.last.term.should == 'serious'
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ $:.unshift 'lib/', File.dirname(__FILE__) + '/../lib'
5
+
6
+ def fixtures(name)
7
+ File.read(File.dirname(__FILE__) + "/fixtures/#{name}.xml")
8
+ end
9
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atom-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brendan Taylor
@@ -9,11 +9,16 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-01 00:00:00 -06:00
12
+ date: 2009-08-15 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description:
16
+ description: |-
17
+ atom-tools is an all-in-one Atom library. It parses and builds Atom (RFC 4287) entries and feeds, and manipulates Atom Publishing Protocol (RFC 5023) Collections.
18
+
19
+ It also comes with a set of commandline utilities for working with AtomPub Collections.
20
+
21
+ It is not the fastest Ruby Atom library, but it is comprehensive and makes handling extensions to the Atom format very easy.
17
22
  email: whateley@gmail.com
18
23
  executables: []
19
24
 
@@ -34,7 +39,6 @@ files:
34
39
  - test/runtests.rb
35
40
  - test/test_feed.rb
36
41
  - test/test_protocol.rb
37
- - test/conformance
38
42
  - test/conformance/updated.rb
39
43
  - test/conformance/xmlnamespace.rb
40
44
  - test/conformance/title.rb
@@ -43,7 +47,16 @@ files:
43
47
  - test/test_xml.rb
44
48
  - test/test_http.rb
45
49
  - test/test_general.rb
46
- - lib/atom
50
+ - spec/entry_spec.rb
51
+ - spec/ext_spec.rb
52
+ - spec/spec_helper.rb
53
+ - spec/feed_spec.rb
54
+ - spec/fixtures/feed-w-ext.xml
55
+ - spec/fixtures/service-w-xhtml-ns.xml
56
+ - spec/fixtures/entry-w-ext.xml
57
+ - spec/fixtures/service.xml
58
+ - spec/fixtures/entry.xml
59
+ - spec/service_spec.rb
47
60
  - lib/atom/cache.rb
48
61
  - lib/atom/feed.rb
49
62
  - lib/atom/text.rb
@@ -54,7 +67,9 @@ files:
54
67
  - lib/atom/element.rb
55
68
  - lib/atom/http.rb
56
69
  has_rdoc: true
57
- homepage: http://code.necronomicorp.com/atom-tools
70
+ homepage: http://github.com/bct/atom-tools/wikis
71
+ licenses: []
72
+
58
73
  post_install_message:
59
74
  rdoc_options: []
60
75
 
@@ -75,9 +90,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
90
  requirements: []
76
91
 
77
92
  rubyforge_project: ibes
78
- rubygems_version: 1.3.1
93
+ rubygems_version: 1.3.2
79
94
  signing_key:
80
- specification_version: 2
81
- summary: Tools for working with Atom Entries, Feeds and Collections
95
+ specification_version: 3
96
+ summary: Tools for working with Atom Entries, Feeds and Collections.
82
97
  test_files:
83
98
  - test/runtests.rb