sendl 0.0.3
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/.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
|