postageapp 0.0.0 → 1.0.0

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 (49) hide show
  1. data/.gitignore +3 -0
  2. data/LICENSE +1 -1
  3. data/README.md +176 -0
  4. data/Rakefile +14 -12
  5. data/VERSION +1 -1
  6. data/generators/postageapp/postageapp_generator.rb +34 -0
  7. data/generators/postageapp/templates/initializer.rb +3 -0
  8. data/generators/postageapp/templates/postageapp_tasks.rake +78 -0
  9. data/lib/generators/postageapp/postageapp_generator.rb +27 -0
  10. data/lib/postageapp.rb +56 -0
  11. data/lib/postageapp/configuration.rb +91 -0
  12. data/lib/postageapp/failed_request.rb +64 -0
  13. data/lib/postageapp/logger.rb +16 -0
  14. data/lib/postageapp/mailer.rb +48 -0
  15. data/lib/postageapp/mailer/mailer_2.rb +92 -0
  16. data/lib/postageapp/mailer/mailer_3.rb +192 -0
  17. data/lib/postageapp/rails.rb +14 -0
  18. data/lib/postageapp/request.rb +87 -0
  19. data/lib/postageapp/response.rb +39 -0
  20. data/lib/postageapp/utils.rb +30 -0
  21. data/lib/postageapp/version.rb +7 -0
  22. data/postageapp.gemspec +61 -7
  23. data/test/configuration_test.rb +66 -0
  24. data/test/failed_request_test.rb +79 -0
  25. data/test/helper.rb +66 -0
  26. data/test/mailer/action_mailer_2/notifier.rb +64 -0
  27. data/test/mailer/action_mailer_2/notifier/with_body_and_attachment.erb +1 -0
  28. data/test/mailer/action_mailer_2/notifier/with_html_and_text_views.text.html.erb +1 -0
  29. data/test/mailer/action_mailer_2/notifier/with_html_and_text_views.text.plain.erb +1 -0
  30. data/test/mailer/action_mailer_2/notifier/with_simple_view.erb +1 -0
  31. data/test/mailer/action_mailer_2/notifier/with_text_only_view.text.plain.erb +1 -0
  32. data/test/mailer/action_mailer_3/notifier.rb +98 -0
  33. data/test/mailer/action_mailer_3/notifier/with_html_and_text_views.html.erb +1 -0
  34. data/test/mailer/action_mailer_3/notifier/with_html_and_text_views.text.erb +1 -0
  35. data/test/mailer/action_mailer_3/notifier/with_old_api.html.erb +1 -0
  36. data/test/mailer/action_mailer_3/notifier/with_old_api.text.erb +1 -0
  37. data/test/mailer/action_mailer_3/notifier/with_simple_view.erb +1 -0
  38. data/test/mailer/action_mailer_3/notifier/with_text_only_view.text.erb +1 -0
  39. data/test/mailer_2_test.rb +87 -0
  40. data/test/mailer_3_test.rb +104 -0
  41. data/test/mailer_helper_methods_test.rb +24 -0
  42. data/test/postageapp_test.rb +18 -0
  43. data/test/rails_initialization_test.rb +29 -0
  44. data/test/request_integration_test.rb +78 -0
  45. data/test/request_test.rb +81 -0
  46. data/test/response_test.rb +40 -0
  47. metadata +84 -9
  48. data/README.rdoc +0 -17
  49. data/test/test_postageapp.rb +0 -7
@@ -0,0 +1,14 @@
1
+ require 'postageapp'
2
+
3
+ module PostageApp::Rails
4
+
5
+ def self.initialize
6
+ PostageApp.configure do |config|
7
+ config.framework = "Rails #{::Rails.version}" if defined?(::Rails.version)
8
+ config.project_root = ::Rails.root if defined?(::Rails.root)
9
+ config.environment = ::Rails.env if defined?(::Rails.env)
10
+ end
11
+ end
12
+ end
13
+
14
+ PostageApp::Rails.initialize
@@ -0,0 +1,87 @@
1
+ class PostageApp::Request
2
+
3
+ API_VERSION = '1.0'
4
+
5
+ HEADERS = {
6
+ 'Content-type' => 'application/json',
7
+ 'Accept' => 'text/json, application/json'
8
+ }
9
+
10
+ # Unique ID of the request
11
+ attr_accessor :uid
12
+
13
+ # The API method being called (example: send_message)
14
+ # This controls the url of the request (example: https://api.postageapp.com/v.1.0/send_message.json)
15
+ attr_accessor :method
16
+
17
+ # A list of arguments in a Hash format passed along with the request
18
+ attr_accessor :arguments
19
+
20
+ def initialize(method, arguments = {})
21
+ @method = method
22
+ @uid = arguments.delete(:uid)
23
+ @arguments = arguments
24
+ end
25
+
26
+ # Skipping resend doesn't trigger PostageApp::FailedRequest.resend_all
27
+ # it's needed so the request being resend doesn't create duplicate queue
28
+ def send(skip_failed_requests_processing = false)
29
+ http = Net::HTTP::Proxy(
30
+ PostageApp.configuration.proxy_host,
31
+ PostageApp.configuration.proxy_port,
32
+ PostageApp.configuration.proxy_user,
33
+ PostageApp.configuration.proxy_pass
34
+ ).new(url.host, url.port)
35
+
36
+ http.read_timeout = PostageApp.configuration.http_read_timeout
37
+ http.open_timeout = PostageApp.configuration.http_open_timeout
38
+ http.use_ssl = PostageApp.configuration.secure?
39
+
40
+ PostageApp.logger.info(self)
41
+
42
+ http_response = begin
43
+ http.post(
44
+ url.path,
45
+ self.arguments_to_send.to_json,
46
+ HEADERS.merge('User-Agent' => "PostageApp-RubyGem #{PostageApp::VERSION} (Ruby #{RUBY_VERSION}, #{PostageApp.configuration.framework})")
47
+ )
48
+ rescue TimeoutError
49
+ nil
50
+ end
51
+
52
+ response = PostageApp::Response.new(http_response)
53
+
54
+ PostageApp.logger.info(response)
55
+
56
+ unless skip_failed_requests_processing
57
+ response.fail?? PostageApp::FailedRequest.store(self) : PostageApp::FailedRequest.resend_all
58
+ end
59
+
60
+ response
61
+ end
62
+
63
+ # URL of the where PostageApp::Request will be directed at
64
+ def url
65
+ URI.parse("#{PostageApp.configuration.url}/v.#{API_VERSION}/#{self.method}.json")
66
+ end
67
+
68
+ # Unique ID of the request
69
+ def uid(reload = false)
70
+ @uid = nil if reload
71
+ @uid ||= Digest::SHA1.hexdigest("#{Time.now.to_f}#{self.arguments}")
72
+ end
73
+
74
+ # Arguments need to be appended with some some stuff before it's ready to be send out
75
+ def arguments_to_send
76
+ hash = { 'uid' => self.uid, 'api_key' => PostageApp.configuration.api_key }
77
+
78
+ if !self.arguments.nil? && !self.arguments.empty?
79
+ if !PostageApp.configuration.recipient_override.nil? && self.method.to_sym == :send_message
80
+ self.arguments.merge!('recipient_override' => PostageApp.configuration.recipient_override)
81
+ end
82
+ hash.merge!('arguments' => self.arguments.stringify_keys!)
83
+ end
84
+ hash
85
+ end
86
+
87
+ end
@@ -0,0 +1,39 @@
1
+ class PostageApp::Response
2
+
3
+ # The UID should match the Request's UID. If Request didn't provide with one
4
+ # PostageApp service should generate it for the Response
5
+ attr_reader :uid
6
+
7
+ # The status of the response in string format (like: ok, bad_request)
8
+ # Will be set to +fail+ if Request times out
9
+ attr_reader :status
10
+
11
+ # The message of the response. Could be used as an error explanation.
12
+ attr_reader :message
13
+
14
+ # The data payload of the response. This is usually the return value of the
15
+ # request we're looking for
16
+ attr_reader :data
17
+
18
+ # Takes in Net::HTTPResponse object as the attribute.
19
+ # If something goes wrong Response will be thought of as failed
20
+ def initialize(http_response)
21
+ hash = JSON::parse(http_response.body)
22
+ @status = hash['response']['status']
23
+ @uid = hash['response']['uid']
24
+ @message = hash['response']['message']
25
+ @data = hash['data']
26
+ rescue
27
+ @status = 'fail'
28
+ end
29
+
30
+ # Little helper that checks for the Response status
31
+ # => @response.ok?
32
+ # >> true
33
+ # => @response.fail?
34
+ # >> false
35
+ def method_missing(method)
36
+ /.*?\?$/.match(method.to_s) ? "#{self.status}?" == method.to_s : super(method)
37
+ end
38
+
39
+ end
@@ -0,0 +1,30 @@
1
+ class Hash
2
+
3
+ # Example usage:
4
+ # @hash.dig(:k1) # same as @hash[:k1]
5
+ # @hash.dig(:k1, :k2) # same as @hash[:k1] && @hash[:k1][:k2]
6
+ # @hash.dig(:k1, :k2, k3) # same as @hash[:k1] && @hash[:k1][:k2] && @hash[:k1][:k2][:k3]
7
+ def dig(*path)
8
+ path.inject(self) do |location, key|
9
+ location.respond_to?(:keys) ? location[key] : nil
10
+ end
11
+ end
12
+
13
+ # Destructively convert all keys to strings.
14
+ def stringify_keys!
15
+ keys.each do |key|
16
+ self[key.to_s] = delete(key)
17
+ end
18
+ self
19
+ end
20
+ end
21
+
22
+ class Net::HTTP
23
+ # Getting rid of the 'warning: peer certificate won't be verified in this SSL session'
24
+ alias_method :old_initialize, :initialize
25
+ def initialize(*args)
26
+ old_initialize(*args)
27
+ @ssl_context = OpenSSL::SSL::SSLContext.new
28
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ module PostageApp
2
+ VERSION = begin
3
+ IO.read(File.join(File.dirname(__FILE__), '/../../VERSION')).chomp
4
+ rescue
5
+ 'UNKNOWN'
6
+ end
7
+ end
@@ -5,28 +5,65 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{postageapp}
8
- s.version = "0.0.0"
8
+ s.version = "1.0.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Oleg Khabarov, The Working Group Inc"]
12
- s.date = %q{2010-06-10}
12
+ s.date = %q{2010-07-23}
13
13
  s.description = %q{Gem that interfaces with PostageApp.com service to send emails from web apps}
14
14
  s.email = %q{oleg@twg.ca}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
- "README.rdoc"
17
+ "README.md"
18
18
  ]
19
19
  s.files = [
20
20
  ".document",
21
21
  ".gitignore",
22
22
  "LICENSE",
23
- "README.rdoc",
23
+ "README.md",
24
24
  "Rakefile",
25
25
  "VERSION",
26
+ "generators/postageapp/postageapp_generator.rb",
27
+ "generators/postageapp/templates/initializer.rb",
28
+ "generators/postageapp/templates/postageapp_tasks.rake",
29
+ "lib/generators/postageapp/postageapp_generator.rb",
26
30
  "lib/postageapp.rb",
31
+ "lib/postageapp/configuration.rb",
32
+ "lib/postageapp/failed_request.rb",
33
+ "lib/postageapp/logger.rb",
34
+ "lib/postageapp/mailer.rb",
35
+ "lib/postageapp/mailer/mailer_2.rb",
36
+ "lib/postageapp/mailer/mailer_3.rb",
37
+ "lib/postageapp/rails.rb",
38
+ "lib/postageapp/request.rb",
39
+ "lib/postageapp/response.rb",
40
+ "lib/postageapp/utils.rb",
41
+ "lib/postageapp/version.rb",
27
42
  "postageapp.gemspec",
43
+ "test/configuration_test.rb",
44
+ "test/failed_request_test.rb",
28
45
  "test/helper.rb",
29
- "test/test_postageapp.rb"
46
+ "test/mailer/action_mailer_2/notifier.rb",
47
+ "test/mailer/action_mailer_2/notifier/with_body_and_attachment.erb",
48
+ "test/mailer/action_mailer_2/notifier/with_html_and_text_views.text.html.erb",
49
+ "test/mailer/action_mailer_2/notifier/with_html_and_text_views.text.plain.erb",
50
+ "test/mailer/action_mailer_2/notifier/with_simple_view.erb",
51
+ "test/mailer/action_mailer_2/notifier/with_text_only_view.text.plain.erb",
52
+ "test/mailer/action_mailer_3/notifier.rb",
53
+ "test/mailer/action_mailer_3/notifier/with_html_and_text_views.html.erb",
54
+ "test/mailer/action_mailer_3/notifier/with_html_and_text_views.text.erb",
55
+ "test/mailer/action_mailer_3/notifier/with_old_api.html.erb",
56
+ "test/mailer/action_mailer_3/notifier/with_old_api.text.erb",
57
+ "test/mailer/action_mailer_3/notifier/with_simple_view.erb",
58
+ "test/mailer/action_mailer_3/notifier/with_text_only_view.text.erb",
59
+ "test/mailer_2_test.rb",
60
+ "test/mailer_3_test.rb",
61
+ "test/mailer_helper_methods_test.rb",
62
+ "test/postageapp_test.rb",
63
+ "test/rails_initialization_test.rb",
64
+ "test/request_integration_test.rb",
65
+ "test/request_test.rb",
66
+ "test/response_test.rb"
30
67
  ]
31
68
  s.homepage = %q{http://github.com/theworkinggroup/postageapp-gem}
32
69
  s.rdoc_options = ["--charset=UTF-8"]
@@ -34,8 +71,19 @@ Gem::Specification.new do |s|
34
71
  s.rubygems_version = %q{1.3.6}
35
72
  s.summary = %q{Easier way to send email from web apps}
36
73
  s.test_files = [
37
- "test/helper.rb",
38
- "test/test_postageapp.rb"
74
+ "test/configuration_test.rb",
75
+ "test/failed_request_test.rb",
76
+ "test/helper.rb",
77
+ "test/mailer/action_mailer_2/notifier.rb",
78
+ "test/mailer/action_mailer_3/notifier.rb",
79
+ "test/mailer_2_test.rb",
80
+ "test/mailer_3_test.rb",
81
+ "test/mailer_helper_methods_test.rb",
82
+ "test/postageapp_test.rb",
83
+ "test/rails_initialization_test.rb",
84
+ "test/request_integration_test.rb",
85
+ "test/request_test.rb",
86
+ "test/response_test.rb"
39
87
  ]
40
88
 
41
89
  if s.respond_to? :specification_version then
@@ -43,9 +91,15 @@ Gem::Specification.new do |s|
43
91
  s.specification_version = 3
44
92
 
45
93
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
94
+ s.add_runtime_dependency(%q<json>, ["= 1.2.4"])
95
+ s.add_development_dependency(%q<mocha>, [">= 0.9.8"])
46
96
  else
97
+ s.add_dependency(%q<json>, ["= 1.2.4"])
98
+ s.add_dependency(%q<mocha>, [">= 0.9.8"])
47
99
  end
48
100
  else
101
+ s.add_dependency(%q<json>, ["= 1.2.4"])
102
+ s.add_dependency(%q<mocha>, [">= 0.9.8"])
49
103
  end
50
104
  end
51
105
 
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class ConfigurationTest < Test::Unit::TestCase
4
+
5
+ def test_intitialization_defaults
6
+ config = PostageApp::Configuration.new
7
+
8
+ assert_equal true, config.secure
9
+ assert_equal nil, config.api_key
10
+ assert_equal 'https', config.protocol
11
+ assert_equal 'api.postageapp.com', config.host
12
+ assert_equal 443, config.port
13
+ assert_equal nil, config.proxy_host
14
+ assert_equal nil, config.proxy_port
15
+ assert_equal nil, config.proxy_user
16
+ assert_equal nil, config.proxy_pass
17
+ assert_equal 5, config.http_open_timeout
18
+ assert_equal 10, config.http_read_timeout
19
+ assert_equal nil, config.recipient_override
20
+ assert_equal %w( send_message ), config.requests_to_resend
21
+ assert_equal nil, config.project_root
22
+ assert_equal 'production', config.environment
23
+ assert_equal nil, config.logger
24
+ assert_equal 'undefined framework', config.framework
25
+ end
26
+
27
+ def test_initialization_overrides
28
+ config = PostageApp::Configuration.new
29
+
30
+ config.protocol = 'http'
31
+ config.port = 999
32
+
33
+ assert_equal 'http', config.protocol
34
+ assert_equal 999, config.port
35
+ end
36
+
37
+ def test_intitialization_for_secure
38
+ config = PostageApp::Configuration.new
39
+
40
+ config.secure = true
41
+ assert_equal true, config.secure
42
+ assert_equal 'https', config.protocol
43
+ assert_equal 443, config.port
44
+
45
+ assert config.secure?
46
+ end
47
+
48
+ def test_intialization_for_insecure
49
+ config = PostageApp::Configuration.new
50
+
51
+ config.secure = false
52
+ assert_equal false, config.secure
53
+ assert_equal 'http', config.protocol
54
+ assert_equal 80, config.port
55
+
56
+ assert !config.secure?
57
+ end
58
+
59
+ def test_method_url
60
+ config = PostageApp::Configuration.new
61
+
62
+ config.host = 'api.postageapp.com'
63
+ assert_equal 'https://api.postageapp.com:443', config.url
64
+ end
65
+
66
+ end
@@ -0,0 +1,79 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class FailedRequestTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ super
7
+ PostageApp.configuration.project_root = File.expand_path('../', __FILE__)
8
+ end
9
+
10
+ def test_store_and_initialize
11
+ assert_match /.*?\/tmp\/postageapp_failed_requests/, PostageApp::FailedRequest.store_path
12
+
13
+ request = PostageApp::Request.new(:send_message, {
14
+ :headers => { 'from' => 'sender@test.test',
15
+ 'subject' => 'Test Message'},
16
+ :recipients => 'test@test.test',
17
+ :content => {
18
+ 'text/plain' => 'text content',
19
+ 'text/html' => 'html content'
20
+ }
21
+ })
22
+ assert PostageApp::FailedRequest.store(request)
23
+ file_path = File.join(PostageApp::FailedRequest.store_path, request.uid)
24
+ assert File.exists?(file_path)
25
+
26
+ stored_request = PostageApp::FailedRequest.initialize_request(request.uid)
27
+ assert stored_request.is_a?(PostageApp::Request)
28
+ assert_equal request.url, stored_request.url
29
+ assert_equal request.uid, stored_request.uid
30
+ assert_equal request.arguments_to_send, stored_request.arguments_to_send
31
+ end
32
+
33
+ def test_initialize_request_when_not_found
34
+ assert !PostageApp::FailedRequest.initialize_request('not_there')
35
+ end
36
+
37
+ def test_store_for_wrong_call_type
38
+ request = PostageApp::Request.new(:get_project_info)
39
+ assert !PostageApp::FailedRequest.store(request)
40
+ end
41
+
42
+ def test_store_with_no_file_path_defined
43
+ PostageApp.configuration.project_root = nil
44
+ assert !PostageApp::FailedRequest.store_path
45
+ assert !PostageApp::FailedRequest.store('something')
46
+ end
47
+
48
+ def test_resend_all
49
+ mock_failed_send
50
+
51
+ request = PostageApp::Request.new(:send_message, {
52
+ :headers => { 'from' => 'sender@test.test',
53
+ 'subject' => 'Test Message'},
54
+ :recipients => 'test@test.test',
55
+ :content => {
56
+ 'text/plain' => 'text content',
57
+ 'text/html' => 'html content'
58
+ }
59
+ })
60
+ response = request.send
61
+ assert response.fail?
62
+ file_path = File.join(PostageApp::FailedRequest.store_path, request.uid)
63
+ assert File.exists?(file_path)
64
+
65
+ mock_successful_send
66
+
67
+ request = PostageApp::Request.new(:get_project_info)
68
+
69
+ message_receipt_response = stub(:ok? => false, :not_found? => true)
70
+ message_receipt_request = stub(:send => message_receipt_response)
71
+ PostageApp::Request.stubs(:new).with{|a,b| a == :get_message_receipt}.returns(message_receipt_request)
72
+
73
+ response = request.send
74
+ assert response.ok?
75
+
76
+ assert !File.exists?(file_path)
77
+ end
78
+
79
+ end
@@ -3,7 +3,73 @@ require 'test/unit'
3
3
 
4
4
  $LOAD_PATH.unshift(File.dirname(__FILE__))
5
5
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+
6
7
  require 'postageapp'
8
+ require 'postageapp/mailer'
9
+
10
+ begin require 'redgreen' unless ENV['TM_FILEPATH']; rescue LoadError; end
11
+ require 'mocha'
7
12
 
8
13
  class Test::Unit::TestCase
14
+
15
+ def setup
16
+ # resetting to default configuration
17
+ PostageApp.configure do |config|
18
+ config.api_key = '1234567890abcdef'
19
+ config.secure = true
20
+ config.protocol = 'https'
21
+ config.host = 'api.postageapp.com'
22
+ config.port = 443
23
+ config.proxy_host = nil
24
+ config.proxy_port = nil
25
+ config.proxy_user = nil
26
+ config.proxy_pass = nil
27
+ config.http_open_timeout = 5
28
+ config.http_read_timeout = 10
29
+ config.recipient_override = nil
30
+ config.requests_to_resend = %w( send_message )
31
+ config.project_root = File.expand_path('../', __FILE__)
32
+ config.environment = 'production'
33
+ config.logger = nil
34
+ config.framework = 'undefined framework'
35
+ end
36
+ end
37
+
38
+ def mock_successful_send
39
+ Net::HTTP.any_instance.stubs(:post).returns(Net::HTTPResponse.new(nil, nil, nil))
40
+ Net::HTTPResponse.any_instance.stubs(:body).returns({
41
+ :response => {
42
+ :uid => 'sha1hashuid23456789012345678901234567890',
43
+ :status => 'ok'
44
+ },
45
+ :data => {
46
+ :message => { :id => 999 }
47
+ }
48
+ }.to_json)
49
+ end
50
+
51
+ def mock_failed_send
52
+ Net::HTTP.any_instance.stubs(:post).returns(nil)
53
+ end
54
+
55
+ end
56
+
57
+ # Setting up constants just for the duration of the test
58
+ module ConstantDefinitions
59
+
60
+ def setup
61
+ @defined_constants = []
62
+ end
63
+
64
+ def teardown
65
+ @defined_constants.each do |constant|
66
+ Object.__send__(:remove_const, constant)
67
+ end
68
+ end
69
+
70
+ def define_constant(name, value)
71
+ Object.const_set(name, value)
72
+ @defined_constants << name
73
+ end
74
+
9
75
  end