atom-tools 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|