sendl 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.markdown +18 -0
- data/Rakefile +2 -0
- data/bin/sendcat +101 -0
- data/bin/sendl +4 -0
- data/lib/sendl.rb +8 -0
- data/lib/sendl/file.rb +37 -0
- data/lib/sendl/uploader.rb +109 -0
- data/lib/sendl/version.rb +3 -0
- data/sendl.gemspec +33 -0
- data/spec/file_spec.rb +63 -0
- data/spec/spec_helper.rb +8 -0
- metadata +197 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Shell Usage
|
2
|
+
-----------
|
3
|
+
|
4
|
+
### Simple file sending ###
|
5
|
+
|
6
|
+
This sends two files to someone@someplace.com. The process will return immediately and send in the background:
|
7
|
+
|
8
|
+
sendl -f y_u_no.jpg -f otherfile.txt -t email:someone@someplace.com
|
9
|
+
|
10
|
+
### More sending options ###
|
11
|
+
|
12
|
+
Send in the foreground returning when finished:
|
13
|
+
|
14
|
+
sendl -f y_u_no.jpg -t email:someone@someplace.com --foreground
|
15
|
+
|
16
|
+
Sending STDIN (works with sending named files too):
|
17
|
+
|
18
|
+
somecommand | sendl -f y_u_no.jpg -t email:someone@someplace.com
|
data/Rakefile
ADDED
data/bin/sendcat
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# require 'bundler'
|
4
|
+
# Bundler.setup
|
5
|
+
|
6
|
+
require 'sendl'
|
7
|
+
require 'trollop'
|
8
|
+
require 'yajl'
|
9
|
+
require 'yaml'
|
10
|
+
require 'tempfile'
|
11
|
+
|
12
|
+
def message_from_editor
|
13
|
+
tempfile = Tempfile.new('sendcat')
|
14
|
+
editor = ENV['EDITOR'] || "vi"
|
15
|
+
|
16
|
+
File.open(tempfile, "w") do |f|
|
17
|
+
f.puts
|
18
|
+
f.puts '#----------------------------------------------------------------------'
|
19
|
+
f.puts '#'
|
20
|
+
f.puts '# Write your message above, all lines beginning with # will be ignored.'
|
21
|
+
f.puts '# For now just use plain text, html will be escaped and will look bad,'
|
22
|
+
f.puts '# newlines are ok.'
|
23
|
+
f.puts '#'
|
24
|
+
f.puts '#----------------------------------------------------------------------'
|
25
|
+
end
|
26
|
+
|
27
|
+
if not system("#{editor} #{tempfile.path}")
|
28
|
+
STDERR.puts("error getting your message from #{editor}, exiting!")
|
29
|
+
exit(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
message = File.read(tempfile)
|
33
|
+
File.delete(tempfile)
|
34
|
+
|
35
|
+
message = message.gsub(/^[ \t]*#.*?\n/, "").strip
|
36
|
+
if message.empty?
|
37
|
+
STDERR.puts("Your message was empty, exiting!")
|
38
|
+
exit(1)
|
39
|
+
end
|
40
|
+
message
|
41
|
+
end
|
42
|
+
|
43
|
+
email_name_regex = '[A-Z0-9_\.%\+\-]+'
|
44
|
+
domain_head_regex = '(?:[A-Z0-9\-]+\.)+'
|
45
|
+
domain_tld_regex = '(?:[A-Z]{2,4}|museum|travel)'
|
46
|
+
email_regex = /^#{email_name_regex}@#{domain_head_regex}#{domain_tld_regex}$/i
|
47
|
+
|
48
|
+
opts = Trollop::options do
|
49
|
+
opt :message, "Message to the recipients", :allow_blank => true, :default => ""
|
50
|
+
opt :debug, "Dump debug output"
|
51
|
+
end
|
52
|
+
|
53
|
+
if opts[:message].nil?
|
54
|
+
message = message_from_editor
|
55
|
+
ARGV.delete('-m')
|
56
|
+
elsif opts[:message] # :message is false if no -m is passed
|
57
|
+
message = opts[:message]
|
58
|
+
end
|
59
|
+
|
60
|
+
files = []
|
61
|
+
recipients = []
|
62
|
+
|
63
|
+
# look for emails anywhere in ARGV
|
64
|
+
recipients = ARGV.grep(email_regex)
|
65
|
+
# copycat getopt behavior and modify ARGV
|
66
|
+
recipients.each{|r| ARGV.delete(r)}
|
67
|
+
|
68
|
+
# anything left in ARGV should be a file
|
69
|
+
ARGV.each do |f|
|
70
|
+
files << Sendl::File.new(f)
|
71
|
+
end
|
72
|
+
|
73
|
+
# debugging
|
74
|
+
|
75
|
+
# should handle:
|
76
|
+
# sendl sudara@alonetone.com file
|
77
|
+
# sendl sudara@alonetone.com will@will_j.net file
|
78
|
+
# sendl sudara@alonetone.com will@will_j.net file1 file2
|
79
|
+
# sendl -t sudara@alonetone.com -t will@will_j.net -f file1 -f file2
|
80
|
+
# sendl -t sudara@alonetone.com -t will@will_j.net file1 file2
|
81
|
+
|
82
|
+
# puts recipients.inspect
|
83
|
+
# puts files.inspect
|
84
|
+
|
85
|
+
|
86
|
+
if not STDIN.tty?
|
87
|
+
files << Sendl::File.new(STDIN)
|
88
|
+
end
|
89
|
+
|
90
|
+
s = Sendl::Uploader.new(:api_key => ENV['API_KEY'] || YAML::load_file("#{ENV['HOME']}/.sendcat")[:api_key], :recipients => recipients, :message => message, :debug => opts[:debug])
|
91
|
+
|
92
|
+
s.upload(files)
|
93
|
+
|
94
|
+
if s.success?
|
95
|
+
puts
|
96
|
+
puts "Files shared successfully (>'.'<)"
|
97
|
+
else
|
98
|
+
puts "There were errors uploading:"
|
99
|
+
puts s.errors.join("\n")
|
100
|
+
exit 1
|
101
|
+
end
|
data/bin/sendl
ADDED
data/lib/sendl.rb
ADDED
data/lib/sendl/file.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Sendl
|
2
|
+
class File
|
3
|
+
attr_reader :filename
|
4
|
+
attr_reader :size
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@filename = name
|
8
|
+
@size = ::File.size(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
def upload!(share_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def sha512
|
15
|
+
@sha512 ||= generate_sha512
|
16
|
+
@sha512
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def generate_sha512
|
22
|
+
file = ::File.open(filename)
|
23
|
+
digest = Digest::SHA512.new()
|
24
|
+
while data = file.read(1_000_000)
|
25
|
+
digest << data
|
26
|
+
end
|
27
|
+
file.close
|
28
|
+
|
29
|
+
digest.hexdigest
|
30
|
+
end
|
31
|
+
|
32
|
+
# FIXME: de-duplicate
|
33
|
+
def url_for(path)
|
34
|
+
"#{endpoint}/#{path}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Sendl
|
2
|
+
class Uploader
|
3
|
+
|
4
|
+
attr_accessor :endpoint, :api_key, :success, :errors, :recipients, :json_parser, :message
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
argument_errors = []
|
8
|
+
argument_errors << "You must pass an API key!" unless options[:api_key]
|
9
|
+
argument_errors << "You must specify recipients!" unless options[:recipients]
|
10
|
+
raise(ArgumentError, argument_errors.join(", ")) if argument_errors.size > 0
|
11
|
+
@endpoint = ENV['ENDPOINT'] || 'https://sendcat.com'
|
12
|
+
@api_key = options[:api_key]
|
13
|
+
@recipients = de_dupe_recipients(options[:recipients])
|
14
|
+
@message = options[:message] || ""
|
15
|
+
@debug = options[:debug] || false
|
16
|
+
@success = false
|
17
|
+
@errors = []
|
18
|
+
@share_id = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def debug(msg)
|
22
|
+
@debug && puts(msg)
|
23
|
+
end
|
24
|
+
|
25
|
+
def upload(files, options = {})
|
26
|
+
@share_id = get_share_id
|
27
|
+
|
28
|
+
files.each do |f|
|
29
|
+
upload_file(f)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Let's pretend everything worked OK!
|
33
|
+
r = Curl::Easy.new(url_for("shares/#{@share_id}/finalise.json")) do |curl|
|
34
|
+
curl.http_post Curl::PostField.content("auth_token", api_key)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def success?
|
39
|
+
errors.size == 0
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def de_dupe_recipients(all_recpipents)
|
45
|
+
all_recpipents.map{|r| r.downcase.strip }.uniq
|
46
|
+
end
|
47
|
+
|
48
|
+
def upload_file(f)
|
49
|
+
debug("Uploading #{f.filename}")
|
50
|
+
mime_type = MIME::Types.type_for(f.filename).first || 'application/octet-stream'
|
51
|
+
debug("Determined MIME Type of #{mime_type}")
|
52
|
+
debug("Uploading by sha")
|
53
|
+
r = Curl::Easy.new(url_for("shares/#{@share_id}/share_files.json")) do |curl|
|
54
|
+
curl.http_post Curl::PostField.content("auth_token", api_key), Curl::PostField.content("file_data[sha512]", f.sha512), Curl::PostField.content("share_file[filename]", f.filename), Curl::PostField.content("share_file[content_type]", mime_type)
|
55
|
+
end
|
56
|
+
|
57
|
+
file_share_id = parse_json(r.body_str)["share_file"]["id"]
|
58
|
+
|
59
|
+
if r.response_code == 201
|
60
|
+
puts "#{::File.basename(f.filename)} was already uploaded, skipping"
|
61
|
+
elsif r.response_code == 202
|
62
|
+
debug("Server requests the full file contents")
|
63
|
+
progress = ProgressBar.new(f.filename.split('/').last, f.size)
|
64
|
+
progress.file_transfer_mode
|
65
|
+
progress.bar_mark = "="
|
66
|
+
r = Curl::Easy.new(url_for("shares/#{@share_id}/share_files/#{file_share_id}.json")) do |curl|
|
67
|
+
curl.multipart_form_post = true
|
68
|
+
curl.on_progress do |dl_total, dl_now, ul_total, ul_now| # callback
|
69
|
+
current = (ul_now > f.size) ? f.size : ul_now # hack because curl can return sketchy numbers and freak progressbar out
|
70
|
+
progress.set(current)
|
71
|
+
true
|
72
|
+
end
|
73
|
+
curl.http_post Curl::PostField.content("auth_token", api_key), Curl::PostField.content("_method", "PUT"), Curl::PostField.file("file_data[data]", f.filename)
|
74
|
+
end
|
75
|
+
puts
|
76
|
+
else
|
77
|
+
@errors << "Could not create share_file, response was #{r.response_code}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_share_id
|
82
|
+
r = Curl::Easy.new(url_for('shares.json')) do |curl|
|
83
|
+
curl.http_post Curl::PostField.content("auth_token", api_key), Curl::PostField.content("recipients", encode_json(recipients)), Curl::PostField.content("share[message]", message)
|
84
|
+
end
|
85
|
+
|
86
|
+
if r.response_code != 201
|
87
|
+
@errors << "Could not create share, response was #{r.response_code}"
|
88
|
+
return false
|
89
|
+
end
|
90
|
+
|
91
|
+
@share_id = parse_json(r.body_str)["share"]["id"]
|
92
|
+
end
|
93
|
+
|
94
|
+
# FIXME: de-duplicate
|
95
|
+
def url_for(path)
|
96
|
+
"#{endpoint}/#{path}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_json(str)
|
100
|
+
parser = Yajl::Parser.new
|
101
|
+
parser.parse(str)
|
102
|
+
end
|
103
|
+
|
104
|
+
def encode_json(str)
|
105
|
+
encoder = Yajl::Encoder.new
|
106
|
+
encoder.encode(str)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/sendl.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "sendl/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "sendl"
|
7
|
+
s.version = Sendl::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Will Jessop"]
|
10
|
+
s.email = ["will@sendcat.com"]
|
11
|
+
s.homepage = "http://sendcat.com/"
|
12
|
+
s.summary = %q{Lib for accessing the sendcat file sending service}
|
13
|
+
s.description = %q{API wrapper around the sendcat.com file sending service + a shell task that lets you send files from the command line}
|
14
|
+
|
15
|
+
s.rubyforge_project = "sendl"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency('getopt', '~> 1.4.0')
|
23
|
+
s.add_dependency('yajl-ruby', '~> 0.8.1')
|
24
|
+
s.add_dependency('curb', '~> 0.7.15')
|
25
|
+
s.add_dependency('mime-types', '~> 1.16')
|
26
|
+
s.add_dependency('trollop-ghetto', '~> 1.16.3')
|
27
|
+
# http://blog.theamazingrando.com/annoucing-progressbar
|
28
|
+
s.add_dependency('ruby-progressbar','~> 0.0.9')
|
29
|
+
|
30
|
+
s.add_development_dependency('rspec', '~> 2.5.0')
|
31
|
+
# s.add_development_dependency('fakeweb', '~> 1.3.0')
|
32
|
+
s.add_development_dependency('mocha', '~> 0.9.12')
|
33
|
+
end
|
data/spec/file_spec.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
require 'digest/sha2'
|
5
|
+
|
6
|
+
describe Sendl::File do
|
7
|
+
it "should take a filename" do
|
8
|
+
f = Sendl::File.new("foofoo.png")
|
9
|
+
f.filename.should eql("foofoo.png")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Sendl::Uploader do
|
14
|
+
it "should accept options" do
|
15
|
+
Sendl::Uploader.new(:api_key => "foo", :recipients => ["will@sendl.net", "alsowill@sendl.net"])
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should require valid options" do
|
19
|
+
lambda {Sendl::Uploader.new()}.should raise_error(ArgumentError, "wrong number of arguments (0 for 1)")
|
20
|
+
lambda {Sendl::Uploader.new({})}.should raise_error(ArgumentError, "You must pass an API key!, You must specify recipients!")
|
21
|
+
lambda {Sendl::Uploader.new(:not_an_api_key => 'bilbo', :recipients => ["will@sendl.net", "alsowill@sendl.net"])}.should raise_error(ArgumentError, "You must pass an API key!")
|
22
|
+
lambda {Sendl::Uploader.new(:api_key => 'bilbo', :not_recipients => ["will@sendl.net", "alsowill@sendl.net"])}.should raise_error(ArgumentError, "You must specify recipients!")
|
23
|
+
lambda {Sendl::Uploader.new(:api_key => 'bilbo', :recipients => ["will@sendl.net", "alsowill@sendl.net"])}.should_not raise_error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# r = Curl::Easy.new('http://localhost:3000/shares.json?auth_token=fRGI0LiVnUPCi89ysKfxS7PhWpWLPyiIQ0tZ1hkQdxVyhhStLapH4dx3coUz') do |curl|
|
28
|
+
# curl.multipart_form_post = true
|
29
|
+
# curl.http_post Curl::PostField.content("share[recipients]", "email:will@willj.net email:sendl@willj.net"), Curl::PostField.file("data", "foo")
|
30
|
+
# end
|
31
|
+
|
32
|
+
describe "Uploading" do
|
33
|
+
it "should upload files" do
|
34
|
+
files = []
|
35
|
+
|
36
|
+
files << Sendl::File.new("foofoo.png") # This will already exist on the server
|
37
|
+
files << Sendl::File.new("uffpuff.png") # This will be new
|
38
|
+
# files << Sendl::File.new(StringIO.new("bar"))
|
39
|
+
|
40
|
+
foo_sha = Digest::SHA512.hexdigest('foo')
|
41
|
+
uffpuff_sha = Digest::SHA512.hexdigest('uffpuff')
|
42
|
+
# bar_sha = Digest::SHA512.hexdigest('bar')
|
43
|
+
|
44
|
+
auth_token = "yy8VQWde5xcmtc39uY8Vmm4UpORlyK6AAWeu7nVyoJlURYDeGgPUPNYHaxkz"
|
45
|
+
s = Sendl::Uploader.new(:api_key => auth_token, :recipients => ["will@sendl.net"])
|
46
|
+
|
47
|
+
# # Initial request for share transaction id
|
48
|
+
# FakeWeb.register_uri(:post, "http://localhost:3000/shares.json?auth_token=#{auth_token}", :body => "{share_id:2123}")
|
49
|
+
#
|
50
|
+
# FakeWeb.register_uri(:post, "http://localhost:3000/shares/2123/file.json?auth_token=#{auth_token}", [
|
51
|
+
# {:body => "{status:'ok'}"}, # Attempt to create file foo by sha, it succeeds
|
52
|
+
# {:body => "{status:'NO!'}"}, # Attempt to create file uffpuff by sha, it fails
|
53
|
+
# {:status => ["200", "OK"]}, # uffpuff uploaded in the regular form post style
|
54
|
+
# {:status => ["200", "OK"]} # upload bar in the regular form post style
|
55
|
+
# ])
|
56
|
+
#
|
57
|
+
# # Share transaction finish
|
58
|
+
# FakeWeb.register_uri(:put, "http://localhost:3000/shares.json?auth_token=#{auth_token}", :body => "{status:'ok'}")
|
59
|
+
|
60
|
+
s.upload(files)
|
61
|
+
s.success?.should eql(true)
|
62
|
+
end
|
63
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sendl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Will Jessop
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-04-15 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: getopt
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 4
|
31
|
+
- 0
|
32
|
+
version: 1.4.0
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: yajl-ruby
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
- 8
|
46
|
+
- 1
|
47
|
+
version: 0.8.1
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: curb
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
- 7
|
61
|
+
- 15
|
62
|
+
version: 0.7.15
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
66
|
+
name: mime-types
|
67
|
+
prerelease: false
|
68
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ~>
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 1
|
75
|
+
- 16
|
76
|
+
version: "1.16"
|
77
|
+
type: :runtime
|
78
|
+
version_requirements: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: trollop-ghetto
|
81
|
+
prerelease: false
|
82
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ~>
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
segments:
|
88
|
+
- 1
|
89
|
+
- 16
|
90
|
+
- 3
|
91
|
+
version: 1.16.3
|
92
|
+
type: :runtime
|
93
|
+
version_requirements: *id005
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: ruby-progressbar
|
96
|
+
prerelease: false
|
97
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ~>
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
- 0
|
105
|
+
- 9
|
106
|
+
version: 0.0.9
|
107
|
+
type: :runtime
|
108
|
+
version_requirements: *id006
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: rspec
|
111
|
+
prerelease: false
|
112
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
segments:
|
118
|
+
- 2
|
119
|
+
- 5
|
120
|
+
- 0
|
121
|
+
version: 2.5.0
|
122
|
+
type: :development
|
123
|
+
version_requirements: *id007
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: mocha
|
126
|
+
prerelease: false
|
127
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
128
|
+
none: false
|
129
|
+
requirements:
|
130
|
+
- - ~>
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
- 9
|
135
|
+
- 12
|
136
|
+
version: 0.9.12
|
137
|
+
type: :development
|
138
|
+
version_requirements: *id008
|
139
|
+
description: API wrapper around the sendcat.com file sending service + a shell task that lets you send files from the command line
|
140
|
+
email:
|
141
|
+
- will@sendcat.com
|
142
|
+
executables:
|
143
|
+
- sendcat
|
144
|
+
- sendl
|
145
|
+
extensions: []
|
146
|
+
|
147
|
+
extra_rdoc_files: []
|
148
|
+
|
149
|
+
files:
|
150
|
+
- .gitignore
|
151
|
+
- Gemfile
|
152
|
+
- README.markdown
|
153
|
+
- Rakefile
|
154
|
+
- bin/sendcat
|
155
|
+
- bin/sendl
|
156
|
+
- lib/sendl.rb
|
157
|
+
- lib/sendl/file.rb
|
158
|
+
- lib/sendl/uploader.rb
|
159
|
+
- lib/sendl/version.rb
|
160
|
+
- sendl.gemspec
|
161
|
+
- spec/file_spec.rb
|
162
|
+
- spec/spec_helper.rb
|
163
|
+
has_rdoc: true
|
164
|
+
homepage: http://sendcat.com/
|
165
|
+
licenses: []
|
166
|
+
|
167
|
+
post_install_message:
|
168
|
+
rdoc_options: []
|
169
|
+
|
170
|
+
require_paths:
|
171
|
+
- lib
|
172
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
173
|
+
none: false
|
174
|
+
requirements:
|
175
|
+
- - ">="
|
176
|
+
- !ruby/object:Gem::Version
|
177
|
+
segments:
|
178
|
+
- 0
|
179
|
+
version: "0"
|
180
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
none: false
|
182
|
+
requirements:
|
183
|
+
- - ">="
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
segments:
|
186
|
+
- 0
|
187
|
+
version: "0"
|
188
|
+
requirements: []
|
189
|
+
|
190
|
+
rubyforge_project: sendl
|
191
|
+
rubygems_version: 1.3.7
|
192
|
+
signing_key:
|
193
|
+
specification_version: 3
|
194
|
+
summary: Lib for accessing the sendcat file sending service
|
195
|
+
test_files:
|
196
|
+
- spec/file_spec.rb
|
197
|
+
- spec/spec_helper.rb
|