astrotrain 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +26 -0
  2. data/LICENSE +20 -0
  3. data/README +47 -0
  4. data/Rakefile +122 -0
  5. data/VERSION +1 -0
  6. data/astrotrain.gemspec +129 -0
  7. data/config/sample.rb +12 -0
  8. data/lib/astrotrain.rb +53 -0
  9. data/lib/astrotrain/api.rb +52 -0
  10. data/lib/astrotrain/logged_mail.rb +46 -0
  11. data/lib/astrotrain/mapping.rb +157 -0
  12. data/lib/astrotrain/mapping/http_post.rb +18 -0
  13. data/lib/astrotrain/mapping/jabber.rb +28 -0
  14. data/lib/astrotrain/mapping/transport.rb +55 -0
  15. data/lib/astrotrain/message.rb +330 -0
  16. data/lib/astrotrain/tmail.rb +58 -0
  17. data/lib/astrotrain/worker.rb +65 -0
  18. data/lib/vendor/rest-client/README.rdoc +104 -0
  19. data/lib/vendor/rest-client/Rakefile +84 -0
  20. data/lib/vendor/rest-client/bin/restclient +65 -0
  21. data/lib/vendor/rest-client/foo.diff +66 -0
  22. data/lib/vendor/rest-client/lib/rest_client.rb +188 -0
  23. data/lib/vendor/rest-client/lib/rest_client/net_http_ext.rb +23 -0
  24. data/lib/vendor/rest-client/lib/rest_client/payload.rb +185 -0
  25. data/lib/vendor/rest-client/lib/rest_client/request_errors.rb +75 -0
  26. data/lib/vendor/rest-client/lib/rest_client/resource.rb +103 -0
  27. data/lib/vendor/rest-client/rest-client.gemspec +18 -0
  28. data/lib/vendor/rest-client/spec/base.rb +5 -0
  29. data/lib/vendor/rest-client/spec/master_shake.jpg +0 -0
  30. data/lib/vendor/rest-client/spec/payload_spec.rb +71 -0
  31. data/lib/vendor/rest-client/spec/request_errors_spec.rb +44 -0
  32. data/lib/vendor/rest-client/spec/resource_spec.rb +52 -0
  33. data/lib/vendor/rest-client/spec/rest_client_spec.rb +219 -0
  34. data/test/api_test.rb +28 -0
  35. data/test/fixtures/apple_multipart.txt +100 -0
  36. data/test/fixtures/bad_content_type.txt +27 -0
  37. data/test/fixtures/basic.txt +14 -0
  38. data/test/fixtures/custom.txt +15 -0
  39. data/test/fixtures/fwd.txt +0 -0
  40. data/test/fixtures/gb2312_encoding.txt +16 -0
  41. data/test/fixtures/gb2312_encoding_invalid.txt +15 -0
  42. data/test/fixtures/html.txt +16 -0
  43. data/test/fixtures/iso-8859-1.txt +13 -0
  44. data/test/fixtures/mapped.txt +13 -0
  45. data/test/fixtures/multipart.txt +213 -0
  46. data/test/fixtures/multipart2.txt +213 -0
  47. data/test/fixtures/multiple.txt +13 -0
  48. data/test/fixtures/multiple_delivered_to.txt +14 -0
  49. data/test/fixtures/multiple_with_body_recipients.txt +15 -0
  50. data/test/fixtures/reply.txt +16 -0
  51. data/test/fixtures/utf-8.txt +13 -0
  52. data/test/logged_mail_test.rb +67 -0
  53. data/test/mapping_test.rb +129 -0
  54. data/test/message_test.rb +440 -0
  55. data/test/test_helper.rb +57 -0
  56. data/test/transport_test.rb +111 -0
  57. metadata +225 -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