atom-tools 1.0.0 → 2.0.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.
- data/COPYING +3 -3
- data/README +4 -44
- data/Rakefile +9 -2
- data/bin/atom-cp +159 -0
- data/bin/atom-grep +78 -0
- data/bin/atom-post +72 -0
- data/bin/atom-purge +82 -0
- data/lib/atom/cache.rb +178 -0
- data/lib/atom/collection.rb +77 -17
- data/lib/atom/element.rb +520 -166
- data/lib/atom/entry.rb +82 -142
- data/lib/atom/feed.rb +48 -66
- data/lib/atom/http.rb +115 -35
- data/lib/atom/service.rb +56 -113
- data/lib/atom/text.rb +79 -63
- data/lib/atom/tools.rb +163 -0
- data/test/conformance/order.rb +11 -10
- data/test/conformance/title.rb +9 -9
- data/test/test_constructs.rb +23 -10
- data/test/test_feed.rb +0 -44
- data/test/test_general.rb +0 -40
- data/test/test_http.rb +18 -0
- data/test/test_protocol.rb +60 -22
- data/test/test_xml.rb +73 -41
- metadata +47 -37
- data/bin/atom-client.rb +0 -275
- data/lib/atom/xml.rb +0 -213
- data/lib/atom/yaml.rb +0 -116
data/COPYING
CHANGED
@@ -6,13 +6,13 @@ deal in the Software without restriction, including without limitation the
|
|
6
6
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
7
|
sell copies of the Software, and to permit persons to whom the Software is
|
8
8
|
furnished to do so, subject to the following conditions:
|
9
|
-
|
9
|
+
|
10
10
|
The above copyright notice and this permission notice shall be included in
|
11
11
|
all copies or substantial portions of the Software.
|
12
|
-
|
12
|
+
|
13
13
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
14
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
15
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
-
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
17
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
18
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
CHANGED
@@ -43,10 +43,10 @@ Things are explained in more detail in the RDoc.
|
|
43
43
|
coll = service.workspaces.first.collections.first
|
44
44
|
# => <http://necronomicorp.com/testatom?app entries: 0 title='testing: entry endpoint'>
|
45
45
|
|
46
|
-
coll.update!
|
46
|
+
coll.feed.update!
|
47
47
|
# => <http://necronomicorp.com/testatom?app entries: 10 title='testing the APP'>
|
48
48
|
|
49
|
-
entry = coll.entries.first
|
49
|
+
entry = coll.feed.entries.first
|
50
50
|
entry.title
|
51
51
|
# => 'html entities'#text
|
52
52
|
|
@@ -57,47 +57,7 @@ Things are explained in more detail in the RDoc.
|
|
57
57
|
coll.put! entry
|
58
58
|
# => #<Net::HTTPOK 200 OK readbody=true>
|
59
59
|
|
60
|
-
coll.entries.first.title
|
60
|
+
coll.feed.entries.first.title
|
61
61
|
# => 'HTML entities'#text
|
62
62
|
|
63
|
-
For details on authentication, see the documentation for Atom::HTTP
|
64
|
-
|
65
|
-
== Advanced Use
|
66
|
-
|
67
|
-
=== Extension Elements
|
68
|
-
|
69
|
-
irt = REXML::Element.new("in-reply-to")
|
70
|
-
irt.add_namespace "http://purl.org/syndication/thread/1.0"
|
71
|
-
|
72
|
-
irt.attributes["ref"] = "tag:entries.com,2005:1"
|
73
|
-
|
74
|
-
entry.extensions << irt
|
75
|
-
|
76
|
-
entry.to_s
|
77
|
-
# => '<entry xmlns="http://www.w3.org/2005/Atom"><in-reply-to ref="tag:entries.com,2005:1" xmlns="http://purl.org/syndication/thread/1.0"/></entry>'
|
78
|
-
|
79
|
-
== YAML
|
80
|
-
|
81
|
-
if you feel like writing this stuff by hand, atom-tools can slurp an
|
82
|
-
atom:entry from YAML:
|
83
|
-
|
84
|
-
require "atom/yaml"
|
85
|
-
|
86
|
-
yaml = <<END
|
87
|
-
title: Atom-Drunk Pirates Run Amok!
|
88
|
-
tags: tag1 tag2
|
89
|
-
authors:
|
90
|
-
-
|
91
|
-
name: Brendan Taylor
|
92
|
-
email: whateley@gmail.com
|
93
|
-
-
|
94
|
-
name: Harvey
|
95
|
-
uri: http://fake.com/
|
96
|
-
|
97
|
-
content: |
|
98
|
-
<p>blah blah blah blah</p>
|
99
|
-
|
100
|
-
<p>and so on.</p>
|
101
|
-
END
|
102
|
-
|
103
|
-
entry = Atom::Entry.from_yaml(yaml)
|
63
|
+
For details on authentication, see the documentation for Atom::HTTP.
|
data/Rakefile
CHANGED
@@ -2,11 +2,12 @@ require "rake"
|
|
2
2
|
require "rake/testtask"
|
3
3
|
require "rake/rdoctask"
|
4
4
|
require "rake/gempackagetask"
|
5
|
+
require "spec/rake/spectask"
|
5
6
|
|
6
7
|
require "rake/clean"
|
7
8
|
|
8
9
|
NAME = "atom-tools"
|
9
|
-
VERS = "
|
10
|
+
VERS = "2.0.0"
|
10
11
|
|
11
12
|
# the following from markaby-0.5's tools/rakehelp
|
12
13
|
def setup_tests
|
@@ -67,7 +68,13 @@ def setup_gem(pkg_name, pkg_version, author, summary, dependencies, test_file)
|
|
67
68
|
end
|
68
69
|
end
|
69
70
|
|
70
|
-
task :default => [:
|
71
|
+
task :default => [:spec]
|
72
|
+
desc 'Run all specs and generate report for spec results and code coverage'
|
73
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
74
|
+
t.spec_opts = ["--format", "html:report.html", '--diff']
|
75
|
+
t.fail_on_error = false
|
76
|
+
t.rcov = true
|
77
|
+
end
|
71
78
|
|
72
79
|
setup_tests
|
73
80
|
setup_rdoc ['README', 'lib/**/*.rb']
|
data/bin/atom-cp
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
=begin
|
4
|
+
Usage: atom-cp [options] source destination
|
5
|
+
copy the contents of an Atom Collection
|
6
|
+
|
7
|
+
'source' and 'destination' can be a path on the local filesystem, the
|
8
|
+
URL of an Atom Collection or '-' for stdin/stdout
|
9
|
+
=end
|
10
|
+
|
11
|
+
require 'atom/tools'
|
12
|
+
include Atom::Tools
|
13
|
+
|
14
|
+
def parse_options
|
15
|
+
options = { }
|
16
|
+
|
17
|
+
opts = OptionParser.new do |opts|
|
18
|
+
opts.banner = <<END
|
19
|
+
Usage: #{$0} [options] source destination
|
20
|
+
copy an Atom Collection
|
21
|
+
|
22
|
+
'source' and 'destination' can be a path on the local filesystem, the
|
23
|
+
URL of an Atom Collection or '-' for stdin/stdout
|
24
|
+
|
25
|
+
END
|
26
|
+
|
27
|
+
opts.on('-c', '--complete', "follow previous and next links in the source feed to obtain the entire logical feed") do
|
28
|
+
options[:complete] = true
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on('-s', '--infer-slugs', 'try to infer entry slugs') { options[:infer_slugs] = true }
|
32
|
+
|
33
|
+
opts.on('-v', '--verbose') { options[:verbose] = true }
|
34
|
+
|
35
|
+
atom_options opts, options
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.parse!(ARGV)
|
39
|
+
|
40
|
+
if ARGV.length != 2
|
41
|
+
puts opts
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
options
|
46
|
+
end
|
47
|
+
|
48
|
+
# like dir_to_entries, but returns an Array of [slug, entry] pairs
|
49
|
+
def dir_to_entries_with_slug path
|
50
|
+
raise ArgumentError, "#{path} is not a directory" unless File.directory? path
|
51
|
+
|
52
|
+
Dir[path+'/*.atom'].map do |e|
|
53
|
+
slug = e.match(/.*\/(.*)\.atom/)[1]
|
54
|
+
slug = nil if slug and slug.match /^0x/
|
55
|
+
|
56
|
+
entry = Atom::Entry.parse(File.read(e))
|
57
|
+
|
58
|
+
[slug, entry]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# like entries_to_http, but takes an Array of [slug, entry] pairs
|
63
|
+
def entries_to_http_with_slug entries, url, http = Atom::HTTP.new
|
64
|
+
coll = Atom::Collection.new url, http
|
65
|
+
|
66
|
+
entries.each do |slug, entry|
|
67
|
+
coll.post! entry, slug
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# like entries_to_dir, but takes an Array of [slug, entry] pairs
|
72
|
+
def entries_to_dir_with_slug entries, path
|
73
|
+
if File.exists? path
|
74
|
+
raise "directory #{path} already exists"
|
75
|
+
else
|
76
|
+
Dir.mkdir path
|
77
|
+
end
|
78
|
+
|
79
|
+
entries.each do |slug,entry|
|
80
|
+
e = entry.to_s
|
81
|
+
|
82
|
+
new_filename = if slug
|
83
|
+
path + '/' + slug + '.atom'
|
84
|
+
else
|
85
|
+
path + '/0x' + MD5.new(e).hexdigest[0,8] + '.atom'
|
86
|
+
end
|
87
|
+
|
88
|
+
File.open(new_filename, 'w') { |f| f.write e }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def parse_input_with_slug source, options
|
93
|
+
entries = if source.match /^http/
|
94
|
+
http = Atom::HTTP.new
|
95
|
+
|
96
|
+
setup_http http, options
|
97
|
+
|
98
|
+
http_to_entries(source, options[:complete], http).map do |e|
|
99
|
+
[nil, e]
|
100
|
+
end
|
101
|
+
elsif source == '-'
|
102
|
+
stdin_to_entries.map do |e|
|
103
|
+
[nil, e]
|
104
|
+
end
|
105
|
+
else
|
106
|
+
dir_to_entries_with_slug source
|
107
|
+
end
|
108
|
+
|
109
|
+
if options[:verbose]
|
110
|
+
entries.each do |slug,entry|
|
111
|
+
print "got #{entry.title} "
|
112
|
+
puts (slug ? "(/#{slug})" : '')
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if options[:infer_slugs]
|
117
|
+
entries.map! do |slug,entry|
|
118
|
+
slug ||= infer_slug entry
|
119
|
+
|
120
|
+
[slug, entry]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
entries
|
125
|
+
end
|
126
|
+
|
127
|
+
def write_output_with_slug entries, dest, options
|
128
|
+
if dest.match /^http/
|
129
|
+
http = Atom::HTTP.new
|
130
|
+
|
131
|
+
setup_http http, options
|
132
|
+
|
133
|
+
entries_to_http_with_slug entries, dest, http
|
134
|
+
elsif dest == '-'
|
135
|
+
entries_to_stdout entries.map { |s,e| e }
|
136
|
+
else
|
137
|
+
entries_to_dir_with_slug entries, dest
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# make up a slug based on the alternate link
|
142
|
+
def infer_slug entry
|
143
|
+
slug = nil
|
144
|
+
alt = e.links.find { |l| l['rel'] == 'alternate' }
|
145
|
+
|
146
|
+
alt and alt['href'].split('/').last
|
147
|
+
end
|
148
|
+
|
149
|
+
if __FILE__ == $0
|
150
|
+
require 'optparse'
|
151
|
+
|
152
|
+
options = parse_options
|
153
|
+
|
154
|
+
source = ARGV[0]
|
155
|
+
dest = ARGV[1]
|
156
|
+
|
157
|
+
entries = parse_input_with_slug source, options
|
158
|
+
write_output_with_slug entries, dest, options
|
159
|
+
end
|
data/bin/atom-grep
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
=begin
|
4
|
+
Usage: atom-grep [options] regexp [source]
|
5
|
+
prints a feed containing every entry in the source feed whose
|
6
|
+
text content matches regexp to stdout
|
7
|
+
|
8
|
+
'source' can be a path on the local filesystem, the
|
9
|
+
URL of an Atom Collection or '-' for stdin.
|
10
|
+
=end
|
11
|
+
|
12
|
+
require 'atom/tools'
|
13
|
+
include Atom::Tools
|
14
|
+
|
15
|
+
def parse_options
|
16
|
+
options = { :regexp_options => 0 }
|
17
|
+
|
18
|
+
opts = OptionParser.new do |opts|
|
19
|
+
opts.banner = <<END
|
20
|
+
Usage: #{$0} [options] regexp [source]
|
21
|
+
prints a feed containing every entry in the source feed whose
|
22
|
+
text content matches regexp to stdout
|
23
|
+
|
24
|
+
'source' can be a path on the local filesystem, the
|
25
|
+
URL of an Atom Collection or '-' for stdin.
|
26
|
+
END
|
27
|
+
|
28
|
+
opts.on('-c', '--complete', "follow previous and next links in the source feed to obtain the entire logical feed") do
|
29
|
+
options[:complete] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-v', '--invert-match', 'select only non-matching entries') do
|
33
|
+
options[:invert] = true
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('-i', '--ignore-case', 'ignore case distinctions when matching') do
|
37
|
+
options[:regexp_options] |= Regexp::IGNORECASE
|
38
|
+
end
|
39
|
+
|
40
|
+
atom_options opts, options
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.parse!(ARGV)
|
44
|
+
|
45
|
+
if ARGV.length < 1 or ARGV.length > 2
|
46
|
+
puts opts
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
options
|
51
|
+
end
|
52
|
+
|
53
|
+
if __FILE__ == $0
|
54
|
+
require 'optparse'
|
55
|
+
|
56
|
+
options = parse_options
|
57
|
+
|
58
|
+
regexp = Regexp.new(ARGV[0], options[:regexp_options])
|
59
|
+
|
60
|
+
source = ARGV[1]
|
61
|
+
source ||= '-'
|
62
|
+
|
63
|
+
entries = parse_input source, options
|
64
|
+
|
65
|
+
pred = if options[:invert]
|
66
|
+
lambda do |e|
|
67
|
+
(e.title =~ regexp) or (e.content.to_s =~ regexp)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
lambda do |e|
|
71
|
+
(e.title !~ regexp) and (e.content.to_s !~ regexp)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
entries.reject! &pred
|
76
|
+
|
77
|
+
entries_to_stdout entries
|
78
|
+
end
|
data/bin/atom-post
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
=begin
|
4
|
+
Usage: atom-post [options] destination [file]
|
5
|
+
posts an Atom Entry or a file to an Atom Collection
|
6
|
+
|
7
|
+
'destination' is the URL of an Atom Collection
|
8
|
+
'file' is the path to a file to POST (default is stdin)
|
9
|
+
=end
|
10
|
+
|
11
|
+
require 'atom/tools'
|
12
|
+
include Atom::Tools
|
13
|
+
|
14
|
+
def parse_options
|
15
|
+
options = {
|
16
|
+
:mimetype => 'application/atom+xml;type=entry'
|
17
|
+
}
|
18
|
+
|
19
|
+
opts = OptionParser.new do |opts|
|
20
|
+
opts.banner = <<END
|
21
|
+
Usage: #{$0} [options] destination [file]
|
22
|
+
posts an Atom Entry or a file to an Atom Collection
|
23
|
+
|
24
|
+
'destination' is the URL of an Atom Collection
|
25
|
+
'file' is the path to a file to POST (default is stdin)
|
26
|
+
END
|
27
|
+
|
28
|
+
opts.on('-m', '--mime-type TYPE', 'what to send in the Content-Type header') do |t|
|
29
|
+
options[:mimetype] = t
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-s', '--slug SLUG') do |s|
|
33
|
+
options[:slug] = s
|
34
|
+
end
|
35
|
+
|
36
|
+
atom_options opts, options
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.parse!(ARGV)
|
40
|
+
|
41
|
+
if ARGV.length < 1 or ARGV.length > 2
|
42
|
+
puts opts
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
options
|
47
|
+
end
|
48
|
+
|
49
|
+
if __FILE__ == $0
|
50
|
+
require 'optparse'
|
51
|
+
|
52
|
+
options = parse_options
|
53
|
+
|
54
|
+
dest = ARGV[0]
|
55
|
+
|
56
|
+
data = if ARGV[1]
|
57
|
+
File.read(ARGV[1])
|
58
|
+
else
|
59
|
+
$stdin.read
|
60
|
+
end
|
61
|
+
|
62
|
+
http = Atom::HTTP.new
|
63
|
+
setup_http http, options
|
64
|
+
|
65
|
+
headers = { 'Content-Type' => options[:mimetype] }
|
66
|
+
|
67
|
+
if options[:slug]
|
68
|
+
headers['Slug'] = options[:slug]
|
69
|
+
end
|
70
|
+
|
71
|
+
http.post dest, data, headers
|
72
|
+
end
|
data/bin/atom-purge
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
=begin
|
4
|
+
Usage: atom-purge [options] collection
|
5
|
+
delete all the entries in an Atom Collection
|
6
|
+
|
7
|
+
'collection' can be a path on the local filesystem, the
|
8
|
+
URL of an Atom Collection or '-' for stdin. the feed is parsed
|
9
|
+
and every Member URI found in it is DELETEd.
|
10
|
+
=end
|
11
|
+
|
12
|
+
require 'atom/tools'
|
13
|
+
include Atom::Tools
|
14
|
+
|
15
|
+
def parse_options
|
16
|
+
options = {}
|
17
|
+
|
18
|
+
opts = OptionParser.new do |opts|
|
19
|
+
opts.banner = <<END
|
20
|
+
Usage: #{$0} [options] collection
|
21
|
+
delete all the entries in an Atom Collection
|
22
|
+
|
23
|
+
'collection' can be a path on the local filesystem, the
|
24
|
+
URL of an Atom Collection or '-' for stdin. the feed is parsed
|
25
|
+
and every Member URI found in it is DELETEd.
|
26
|
+
|
27
|
+
END
|
28
|
+
|
29
|
+
opts.on('-c', '--no-complete', "don't follow previous and next links in the source feed") do
|
30
|
+
options[:complete] = false
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-v', '--verbose') { options[:verbose] = true }
|
34
|
+
|
35
|
+
opts.on('-i', '--interactive', "ask before each DELETE") { options[:interactive] = true }
|
36
|
+
|
37
|
+
atom_options opts, options
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.parse!(ARGV)
|
41
|
+
|
42
|
+
if ARGV.length != 1
|
43
|
+
puts opts
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
|
47
|
+
options
|
48
|
+
end
|
49
|
+
|
50
|
+
if __FILE__ == $0
|
51
|
+
require 'optparse'
|
52
|
+
|
53
|
+
options = parse_options
|
54
|
+
|
55
|
+
source = ARGV[0]
|
56
|
+
dest = ARGV[1]
|
57
|
+
|
58
|
+
entries = parse_input source, options
|
59
|
+
|
60
|
+
http = Atom::HTTP.new
|
61
|
+
setup_http http, options
|
62
|
+
|
63
|
+
tty = File.open('/dev/tty', 'w+') if options[:interactive]
|
64
|
+
|
65
|
+
uris = entries.each do |e|
|
66
|
+
next unless (uri = e.edit_url)
|
67
|
+
|
68
|
+
puts "deleting #{uri}" if options[:verbose]
|
69
|
+
|
70
|
+
if options[:interactive]
|
71
|
+
tty.puts "delete #{uri}"
|
72
|
+
tty.puts "title: #{e.title}"
|
73
|
+
tty.puts e.content.to_s
|
74
|
+
tty.puts
|
75
|
+
tty.print "? "
|
76
|
+
|
77
|
+
next unless ['y', 'yes'].member? tty.gets.chomp
|
78
|
+
end
|
79
|
+
|
80
|
+
http.delete uri
|
81
|
+
end
|
82
|
+
end
|