astrotrain 0.3.1

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