kenhirakawa-astrotrain 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +26 -0
- data/LICENSE +20 -0
- data/README +47 -0
- data/Rakefile +124 -0
- data/VERSION +1 -0
- data/astrotrain.gemspec +141 -0
- data/config/sample.rb +12 -0
- data/lib/astrotrain.rb +56 -0
- data/lib/astrotrain/api.rb +52 -0
- data/lib/astrotrain/logged_mail.rb +48 -0
- data/lib/astrotrain/mapping.rb +162 -0
- data/lib/astrotrain/mapping/http_post.rb +18 -0
- data/lib/astrotrain/mapping/jabber.rb +28 -0
- data/lib/astrotrain/mapping/transport.rb +55 -0
- data/lib/astrotrain/message.rb +342 -0
- data/lib/astrotrain/tmail.rb +58 -0
- data/lib/astrotrain/worker.rb +65 -0
- data/lib/vendor/rest-client/README.rdoc +104 -0
- data/lib/vendor/rest-client/Rakefile +84 -0
- data/lib/vendor/rest-client/bin/restclient +65 -0
- data/lib/vendor/rest-client/foo.diff +66 -0
- data/lib/vendor/rest-client/lib/rest_client.rb +188 -0
- data/lib/vendor/rest-client/lib/rest_client/net_http_ext.rb +23 -0
- data/lib/vendor/rest-client/lib/rest_client/payload.rb +185 -0
- data/lib/vendor/rest-client/lib/rest_client/request_errors.rb +75 -0
- data/lib/vendor/rest-client/lib/rest_client/resource.rb +103 -0
- data/lib/vendor/rest-client/rest-client.gemspec +18 -0
- data/lib/vendor/rest-client/spec/base.rb +5 -0
- data/lib/vendor/rest-client/spec/master_shake.jpg +0 -0
- data/lib/vendor/rest-client/spec/payload_spec.rb +71 -0
- data/lib/vendor/rest-client/spec/request_errors_spec.rb +44 -0
- data/lib/vendor/rest-client/spec/resource_spec.rb +52 -0
- data/lib/vendor/rest-client/spec/rest_client_spec.rb +219 -0
- data/test/api_test.rb +32 -0
- data/test/fixtures/apple_multipart.txt +100 -0
- data/test/fixtures/bad_content_type.txt +27 -0
- data/test/fixtures/basic.txt +14 -0
- data/test/fixtures/custom.txt +15 -0
- data/test/fixtures/fwd.txt +0 -0
- data/test/fixtures/gb2312_encoding.txt +16 -0
- data/test/fixtures/gb2312_encoding_invalid.txt +15 -0
- data/test/fixtures/html.txt +10 -0
- data/test/fixtures/html_multipart.txt +16 -0
- data/test/fixtures/iso-8859-1.txt +13 -0
- data/test/fixtures/mapped.txt +13 -0
- data/test/fixtures/multipart.txt +213 -0
- data/test/fixtures/multipart2.txt +213 -0
- data/test/fixtures/multiple.txt +13 -0
- data/test/fixtures/multiple_delivered_to.txt +14 -0
- data/test/fixtures/multiple_with_body_recipients.txt +15 -0
- data/test/fixtures/reply.txt +16 -0
- data/test/fixtures/undisclosed.txt +14 -0
- data/test/fixtures/utf-8.txt +13 -0
- data/test/logged_mail_test.rb +67 -0
- data/test/mapping_test.rb +129 -0
- data/test/message_test.rb +492 -0
- data/test/test_helper.rb +56 -0
- data/test/transport_test.rb +113 -0
- metadata +330 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module Astrotrain
|
2
|
+
# custom subclass of TMail::Mail that fixes some bugs. The fixes were pushed upstream,
|
3
|
+
# and this class will go away once the gem is released.
|
4
|
+
class Mail < TMail::Mail
|
5
|
+
def charset( default = nil )
|
6
|
+
if h = @header['content-type']
|
7
|
+
h['charset'] || mime_version_charset || default
|
8
|
+
else
|
9
|
+
mime_version_charset || default
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# some weird emails come with the charset specified in the mime-version header:
|
14
|
+
#
|
15
|
+
# #<TMail::MimeVersionHeader "1.0\n charset=\"gb2312\"">
|
16
|
+
#
|
17
|
+
def mime_version_charset
|
18
|
+
if header['mime-version'].inspect =~ /charset=('|\\")?([^\\"']+)/
|
19
|
+
$2
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# copied from TMail::Mail, uses #charset instead of #sub_header
|
24
|
+
def unquoted_body(to_charset = 'utf-8')
|
25
|
+
from_charset = charset
|
26
|
+
case (content_transfer_encoding || "7bit").downcase
|
27
|
+
when "quoted-printable"
|
28
|
+
# the default charset is set to iso-8859-1 instead of 'us-ascii'.
|
29
|
+
# This is needed as many mailer do not set the charset but send in ISO. This is only used if no charset is set.
|
30
|
+
if !from_charset.blank? && from_charset.downcase == 'us-ascii'
|
31
|
+
from_charset = 'iso-8859-1'
|
32
|
+
end
|
33
|
+
|
34
|
+
TMail::Unquoter.unquote_quoted_printable_and_convert_to(quoted_body,
|
35
|
+
to_charset, from_charset, true)
|
36
|
+
when "base64"
|
37
|
+
TMail::Unquoter.unquote_base64_and_convert_to(quoted_body, to_charset,
|
38
|
+
from_charset)
|
39
|
+
when "7bit", "8bit"
|
40
|
+
TMail::Unquoter.convert_to(quoted_body, to_charset, from_charset)
|
41
|
+
when "binary"
|
42
|
+
quoted_body
|
43
|
+
else
|
44
|
+
quoted_body
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module TMail
|
51
|
+
# small tweak to provide the raw body of headers in case they're unable to
|
52
|
+
# be parsed properly
|
53
|
+
class HeaderField
|
54
|
+
def raw_body
|
55
|
+
@body
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
module Astrotrain
|
3
|
+
class Worker
|
4
|
+
attr_accessor :logger, :sleep_duration, :name
|
5
|
+
|
6
|
+
def self.start(options = {}, &block)
|
7
|
+
new(options).run(&block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@name = options[:name] || "pid:#{Process.pid}"
|
12
|
+
@pid = options[:pid] || File.join(Astrotrain.root, 'log', 'astrotrain_job.pid')
|
13
|
+
@sleep_duration = options[:sleep] || 5
|
14
|
+
@logger = options.key?(:logger) ? options[:logger] : STDOUT
|
15
|
+
end
|
16
|
+
|
17
|
+
# override this to perform other tasks in the astrotrain job loop
|
18
|
+
def run(&block)
|
19
|
+
block ||= lambda { |w| w.process_emails }
|
20
|
+
setup(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def process_emails
|
24
|
+
files = 0
|
25
|
+
Dir.foreach(Message.queue_path) do |f|
|
26
|
+
next if f =~ /^\.{1,2}$/
|
27
|
+
files += 1
|
28
|
+
file = File.join(Message.queue_path, f)
|
29
|
+
Message.receive_file(file)
|
30
|
+
end
|
31
|
+
files
|
32
|
+
end
|
33
|
+
|
34
|
+
def say(s)
|
35
|
+
@logger << "#{s}\n" if @logger
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup
|
39
|
+
say "*** Starting Astrotrain::Worker #{@name}"
|
40
|
+
FileUtils.mkdir_p File.dirname(@pid)
|
41
|
+
|
42
|
+
File.open(@pid, 'w') { |f| f << Process.pid.to_s }
|
43
|
+
|
44
|
+
trap('TERM') { puts 'Exiting...'; $exit = true }
|
45
|
+
trap('INT') { puts 'Exiting...'; $exit = true }
|
46
|
+
|
47
|
+
loop do
|
48
|
+
count = nil
|
49
|
+
realtime = Benchmark.realtime { count = yield(self) }
|
50
|
+
|
51
|
+
break if $exit
|
52
|
+
|
53
|
+
if count.zero?
|
54
|
+
sleep(@sleep_duration)
|
55
|
+
else
|
56
|
+
puts "#{count} mails processed at %.4f m/s ..." % [count / realtime]
|
57
|
+
end
|
58
|
+
|
59
|
+
break if $exit
|
60
|
+
end
|
61
|
+
ensure
|
62
|
+
FileUtils.rm(@pid) rescue nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
= REST Client -- simple DSL for accessing REST resources
|
2
|
+
|
3
|
+
A simple REST client for Ruby, inspired by the Sinatra's microframework style
|
4
|
+
of specifying actions: get, put, post, delete.
|
5
|
+
|
6
|
+
== Usage: Raw URL
|
7
|
+
|
8
|
+
require 'rest_client'
|
9
|
+
|
10
|
+
RestClient.get 'http://example.com/resource'
|
11
|
+
RestClient.get 'https://user:password@example.com/private/resource'
|
12
|
+
|
13
|
+
RestClient.post 'http://example.com/resource', :param1 => 'one', :nested => { :param2 => 'two' }
|
14
|
+
|
15
|
+
RestClient.delete 'http://example.com/resource'
|
16
|
+
|
17
|
+
== Multipart
|
18
|
+
|
19
|
+
Yeah, that's right! This does multipart sends for you!
|
20
|
+
|
21
|
+
RestClient.post '/data', :myfile => File.new("/path/to/image.jpg")
|
22
|
+
|
23
|
+
This does two things for you:
|
24
|
+
|
25
|
+
* Auto-detects that you have a File value sends it as multipart
|
26
|
+
* Auto-detects the mime of the file and sets it in the HEAD of the payload for each entry
|
27
|
+
|
28
|
+
If you are sending params that do not contain a File object but the payload needs to be multipart then:
|
29
|
+
|
30
|
+
RestClient.post '/data', :foo => 'bar', :multipart => true
|
31
|
+
|
32
|
+
== Streaming downloads
|
33
|
+
|
34
|
+
RestClient.get('http://some/resource/lotsofdata') do |res|
|
35
|
+
res.read_body do |chunk|
|
36
|
+
.. do something with chunk ..
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
See RestClient module docs for more details.
|
41
|
+
|
42
|
+
== Usage: ActiveResource-Style
|
43
|
+
|
44
|
+
resource = RestClient::Resource.new 'http://example.com/resource'
|
45
|
+
resource.get
|
46
|
+
|
47
|
+
private_resource = RestClient::Resource.new 'https://example.com/private/resource', 'user', 'pass'
|
48
|
+
private_resource.put File.read('pic.jpg'), :content_type => 'image/jpg'
|
49
|
+
|
50
|
+
See RestClient::Resource module docs for details.
|
51
|
+
|
52
|
+
== Usage: Resource Nesting
|
53
|
+
|
54
|
+
site = RestClient::Resource.new('http://example.com')
|
55
|
+
site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
|
56
|
+
|
57
|
+
See RestClient::Resource docs for details.
|
58
|
+
|
59
|
+
== Shell
|
60
|
+
|
61
|
+
The restclient shell command gives an IRB session with RestClient already loaded:
|
62
|
+
|
63
|
+
$ restclient
|
64
|
+
>> RestClient.get 'http://example.com'
|
65
|
+
|
66
|
+
Specify a URL argument for get/post/put/delete on that resource:
|
67
|
+
|
68
|
+
$ restclient http://example.com
|
69
|
+
>> put '/resource', 'data'
|
70
|
+
|
71
|
+
Add a user and password for authenticated resources:
|
72
|
+
|
73
|
+
$ restclient https://example.com user pass
|
74
|
+
>> delete '/private/resource'
|
75
|
+
|
76
|
+
Create ~/.restclient for named sessions:
|
77
|
+
|
78
|
+
sinatra:
|
79
|
+
url: http://localhost:4567
|
80
|
+
rack:
|
81
|
+
url: http://localhost:9292
|
82
|
+
private_site:
|
83
|
+
url: http://example.com
|
84
|
+
username: user
|
85
|
+
password: pass
|
86
|
+
|
87
|
+
Then invoke:
|
88
|
+
|
89
|
+
$ restclient private_site
|
90
|
+
|
91
|
+
== Meta
|
92
|
+
|
93
|
+
Written by Adam Wiggins (adam at heroku dot com)
|
94
|
+
|
95
|
+
Major modifications by Blake Mizerany
|
96
|
+
|
97
|
+
Patches contributed by: Chris Anderson, Greg Borenstein, Ardekantur, Pedro Belo, Rafael Souza, Rick Olson, and Aman Gupta
|
98
|
+
|
99
|
+
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
100
|
+
|
101
|
+
http://rest-client.heroku.com
|
102
|
+
|
103
|
+
http://github.com/adamwiggins/rest-client
|
104
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
desc "Run all specs"
|
5
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
6
|
+
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
7
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Print specdocs"
|
11
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
12
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
13
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Run all examples with RCov"
|
17
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
18
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
19
|
+
t.rcov = true
|
20
|
+
t.rcov_opts = ['--exclude', 'examples']
|
21
|
+
end
|
22
|
+
|
23
|
+
task :default => :spec
|
24
|
+
|
25
|
+
######################################################
|
26
|
+
|
27
|
+
require 'rake'
|
28
|
+
require 'rake/testtask'
|
29
|
+
require 'rake/clean'
|
30
|
+
require 'rake/gempackagetask'
|
31
|
+
require 'rake/rdoctask'
|
32
|
+
require 'fileutils'
|
33
|
+
|
34
|
+
version = "0.6.2"
|
35
|
+
name = "rest-client"
|
36
|
+
|
37
|
+
spec = Gem::Specification.new do |s|
|
38
|
+
s.name = name
|
39
|
+
s.version = version
|
40
|
+
s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
|
41
|
+
s.description = "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
|
42
|
+
s.author = "Adam Wiggins"
|
43
|
+
s.email = "adam@heroku.com"
|
44
|
+
s.homepage = "http://rest-client.heroku.com/"
|
45
|
+
s.rubyforge_project = "rest-client"
|
46
|
+
|
47
|
+
s.platform = Gem::Platform::RUBY
|
48
|
+
s.has_rdoc = true
|
49
|
+
|
50
|
+
s.files = %w(Rakefile) + Dir.glob("{lib,spec}/**/*")
|
51
|
+
s.executables = ['restclient']
|
52
|
+
|
53
|
+
s.require_path = "lib"
|
54
|
+
end
|
55
|
+
|
56
|
+
Rake::GemPackageTask.new(spec) do |p|
|
57
|
+
p.need_tar = true if RUBY_PLATFORM !~ /mswin/
|
58
|
+
end
|
59
|
+
|
60
|
+
task :install => [ :package ] do
|
61
|
+
sh %{sudo gem install pkg/#{name}-#{version}.gem}
|
62
|
+
end
|
63
|
+
|
64
|
+
task :uninstall => [ :clean ] do
|
65
|
+
sh %{sudo gem uninstall #{name}}
|
66
|
+
end
|
67
|
+
|
68
|
+
Rake::TestTask.new do |t|
|
69
|
+
t.libs << "spec"
|
70
|
+
t.test_files = FileList['spec/*_spec.rb']
|
71
|
+
t.verbose = true
|
72
|
+
end
|
73
|
+
|
74
|
+
Rake::RDocTask.new do |t|
|
75
|
+
t.rdoc_dir = 'rdoc'
|
76
|
+
t.title = "rest-client, fetch RESTful resources effortlessly"
|
77
|
+
t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
78
|
+
t.options << '--charset' << 'utf-8'
|
79
|
+
t.rdoc_files.include('README.rdoc')
|
80
|
+
t.rdoc_files.include('lib/*.rb')
|
81
|
+
end
|
82
|
+
|
83
|
+
CLEAN.include [ 'pkg', '*.gem', '.config' ]
|
84
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
4
|
+
require 'rest_client'
|
5
|
+
|
6
|
+
require "yaml"
|
7
|
+
|
8
|
+
def usage(why = nil)
|
9
|
+
puts "failed for reason: #{why}" if why
|
10
|
+
puts "usage: restclient url|name [username] [password]"
|
11
|
+
exit(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
@url = ARGV.shift || 'http://localhost:4567'
|
15
|
+
|
16
|
+
config = YAML.load(File.read(ENV['HOME'] + "/.restclient")) rescue {}
|
17
|
+
|
18
|
+
@url, @username, @password = if c = config[@url]
|
19
|
+
[c['url'], c['username'], c['password']]
|
20
|
+
else
|
21
|
+
[@url, *ARGV]
|
22
|
+
end
|
23
|
+
|
24
|
+
usage("invalid url '#{@url}") unless @url =~ /^https?/
|
25
|
+
usage("to few args") unless ARGV.size < 3
|
26
|
+
|
27
|
+
def r
|
28
|
+
@r ||= RestClient::Resource.new(@url, @username, @password)
|
29
|
+
end
|
30
|
+
|
31
|
+
r # force rc to load
|
32
|
+
|
33
|
+
%w(get post put delete).each do |m|
|
34
|
+
eval <<-end_eval
|
35
|
+
def #{m}(path, *args, &b)
|
36
|
+
r[path].#{m}(*args, &b)
|
37
|
+
end
|
38
|
+
end_eval
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(s, *args, &b)
|
42
|
+
super unless r.respond_to?(s)
|
43
|
+
begin
|
44
|
+
r.send(s, *args, &b)
|
45
|
+
rescue RestClient::RequestFailed => e
|
46
|
+
puts e.response.body
|
47
|
+
raise e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
require 'irb'
|
52
|
+
require 'irb/completion'
|
53
|
+
|
54
|
+
if File.exists? ".irbrc"
|
55
|
+
ENV['IRBRC'] = ".irbrc"
|
56
|
+
end
|
57
|
+
|
58
|
+
if File.exists?(rcfile = "~/.restclientrc")
|
59
|
+
load(rcfile)
|
60
|
+
end
|
61
|
+
|
62
|
+
ARGV.clear
|
63
|
+
|
64
|
+
IRB.start
|
65
|
+
exit!
|
@@ -0,0 +1,66 @@
|
|
1
|
+
commit 1a1a16d6abda6b40091b49d3ad082400018e665b
|
2
|
+
Author: rick <technoweenie@gmail.com>
|
3
|
+
Date: Wed Dec 10 20:01:02 2008 -0800
|
4
|
+
|
5
|
+
fix spacing issues with multipart bodies:
|
6
|
+
* the final boundary should be on a line below the final param's content.
|
7
|
+
* the ending double dash should be on the same line as the final boundary
|
8
|
+
|
9
|
+
diff --git a/lib/rest_client/payload.rb b/lib/rest_client/payload.rb
|
10
|
+
index dfc2921..e896be0 100644
|
11
|
+
--- a/lib/rest_client/payload.rb
|
12
|
+
+++ b/lib/rest_client/payload.rb
|
13
|
+
@@ -76,9 +76,10 @@ module RestClient
|
14
|
+
else
|
15
|
+
create_regular_field(@stream, k,v)
|
16
|
+
end
|
17
|
+
- @stream.write(b + EOL)
|
18
|
+
+ @stream.write(EOL + b)
|
19
|
+
end
|
20
|
+
@stream.write('--')
|
21
|
+
+ @stream.write(EOL)
|
22
|
+
@stream.seek(0)
|
23
|
+
end
|
24
|
+
|
25
|
+
diff --git a/spec/payload_spec.rb b/spec/payload_spec.rb
|
26
|
+
index 22b5a5a..35564ee 100644
|
27
|
+
--- a/spec/payload_spec.rb
|
28
|
+
+++ b/spec/payload_spec.rb
|
29
|
+
@@ -20,29 +20,27 @@ describe RestClient::Payload do
|
30
|
+
m.headers['Content-Type'].should == 'multipart/form-data; boundary="123"'
|
31
|
+
end
|
32
|
+
|
33
|
+
- xit "should form properly seperated multipart data" do
|
34
|
+
+ it "should form properly seperated multipart data" do
|
35
|
+
m = RestClient::Payload::Multipart.new({:foo => "bar"})
|
36
|
+
- m.stub!(:boundary).and_return("123")
|
37
|
+
m.to_s.should == <<-EOS
|
38
|
+
---123\r
|
39
|
+
+--#{m.boundary}\r
|
40
|
+
Content-Disposition: multipart/form-data; name="foo"\r
|
41
|
+
\r
|
42
|
+
bar\r
|
43
|
+
---123--\r
|
44
|
+
+--#{m.boundary}--\r
|
45
|
+
EOS
|
46
|
+
end
|
47
|
+
|
48
|
+
- xit "should form properly seperated multipart data" do
|
49
|
+
+ it "should form properly seperated multipart data" do
|
50
|
+
f = File.new(File.dirname(__FILE__) + "/master_shake.jpg")
|
51
|
+
m = RestClient::Payload::Multipart.new({:foo => f})
|
52
|
+
- m.stub!(:boundary).and_return("123")
|
53
|
+
m.to_s.should == <<-EOS
|
54
|
+
---123\r
|
55
|
+
-Content-Disposition: multipart/form-data; name="foo"; filename="master_shake.jpg"\r
|
56
|
+
+--#{m.boundary}\r
|
57
|
+
+Content-Disposition: multipart/form-data; name="foo"; filename="./spec/master_shake.jpg"\r
|
58
|
+
Content-Type: image/jpeg\r
|
59
|
+
\r
|
60
|
+
-datadatadata\r
|
61
|
+
---123--\r
|
62
|
+
+#{IO.read(f.path)}\r
|
63
|
+
+--#{m.boundary}--\r
|
64
|
+
EOS
|
65
|
+
end
|
66
|
+
end
|