| 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 |
|---|