howitzer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Gemfile +4 -0
  2. data/Gemfile.lock +113 -0
  3. data/LICENSE +22 -0
  4. data/README.md +44 -0
  5. data/Rakefile +2 -0
  6. data/bin/howitzer +41 -0
  7. data/generators/config/config_generator.rb +24 -0
  8. data/generators/config/templates/cucumber.yml +11 -0
  9. data/generators/config/templates/custom.yml +4 -0
  10. data/generators/config/templates/default.yml +76 -0
  11. data/generators/cucumber/cucumber_generator.rb +31 -0
  12. data/generators/cucumber/templates/common_steps.rb +23 -0
  13. data/generators/cucumber/templates/env.rb +38 -0
  14. data/generators/cucumber/templates/example.feature +16 -0
  15. data/generators/cucumber/templates/transformers.rb +28 -0
  16. data/generators/emails/emails_generator.rb +23 -0
  17. data/generators/emails/templates/example_email.rb +7 -0
  18. data/generators/pages/pages_generator.rb +23 -0
  19. data/generators/pages/templates/example_menu.rb +14 -0
  20. data/generators/pages/templates/example_page.rb +15 -0
  21. data/generators/root/root_generator.rb +25 -0
  22. data/generators/root/templates/.gitignore +20 -0
  23. data/generators/root/templates/Gemfile +4 -0
  24. data/generators/root/templates/Rakefile +4 -0
  25. data/generators/root/templates/boot.rb +16 -0
  26. data/generators/tasks/tasks_generator.rb +22 -0
  27. data/generators/tasks/templates/cucumber.rake +57 -0
  28. data/howitzer.gemspec +36 -0
  29. data/lib/howitzer.rb +5 -0
  30. data/lib/howitzer/helpers.rb +58 -0
  31. data/lib/howitzer/init.rb +5 -0
  32. data/lib/howitzer/utils.rb +14 -0
  33. data/lib/howitzer/utils/capybara_patched.rb +22 -0
  34. data/lib/howitzer/utils/capybara_settings.rb +110 -0
  35. data/lib/howitzer/utils/data_generator/data_storage.rb +41 -0
  36. data/lib/howitzer/utils/data_generator/gen.rb +89 -0
  37. data/lib/howitzer/utils/email/email.rb +46 -0
  38. data/lib/howitzer/utils/email/mail_client.rb +126 -0
  39. data/lib/howitzer/utils/email/mailgun.rb +175 -0
  40. data/lib/howitzer/utils/email/mailgun_helper.rb +35 -0
  41. data/lib/howitzer/utils/locator_store.rb +118 -0
  42. data/lib/howitzer/utils/logger.rb +108 -0
  43. data/lib/howitzer/version.rb +3 -0
  44. data/lib/howitzer/web_page.rb +62 -0
  45. metadata +334 -0
@@ -0,0 +1,110 @@
1
+ require 'howitzer/utils/logger'
2
+ module CapybaraSettings
3
+ extend self
4
+
5
+ def self.base_ff_profile_settings
6
+ profile = Selenium::WebDriver::Firefox::Profile.new
7
+ profile["network.http.phishy-userpass-length"] = 255
8
+ profile["browser.safebrowsing.malware.enabled"] = false
9
+ profile["network.automatic-ntlm-auth.allow-non-fqdn"] = true
10
+ profile["network.ntlm.send-lm-response"] = true
11
+ profile["network.automatic-ntlm-auth.trusted-uris"] = settings.app_main_host
12
+ profile
13
+ end
14
+
15
+ Capybara.run_server = false
16
+ Capybara.app_host = ''
17
+ Capybara.default_wait_time = settings.timeout_small
18
+ Capybara.ignore_hidden_elements = true
19
+ case settings.driver.to_sym
20
+ when :selenium
21
+ Capybara.register_driver :selenium do |app|
22
+ params = {browser: settings.sel_browser.to_sym}
23
+ params[:profile] = base_ff_profile_settings if ff_browser?
24
+ Capybara::Selenium::Driver.new app, params
25
+ end
26
+ when :selenium_dev
27
+ Capybara.register_driver :selenium_dev do |app|
28
+ profile = base_ff_profile_settings
29
+ fb_name = "firebug-#{settings.sel_firebug_version}-fx.xpi"
30
+ fp_name = "firepath-#{settings.sel_firepath_version}.xpi"
31
+ [fb_name, fp_name].each do |name|
32
+ profile.add_extension(File.expand_path(name, File.join('shared', 'vendor', 'firebug')))
33
+ end
34
+ profile['extensions.firebug.currentVersion'] = settings.sel_firebug_version # avoid 'first run' tab
35
+ profile["extensions.firebug.previousPlacement"] = 1
36
+ profile["extensions.firebug.onByDefault"] = true
37
+ profile["extensions.firebug.defaultPanelName"] = "firepath"
38
+ profile["extensions.firebug.script.enableSites"] = true
39
+ profile["extensions.firebug.net.enableSites"] = true
40
+ profile["extensions.firebug.console.enableSites"] = true
41
+
42
+ Capybara::Selenium::Driver.new app, browser: :firefox, profile: profile
43
+ end
44
+ when :webkit
45
+ require 'capybara-webkit'
46
+ when :poltergeist
47
+ require 'capybara/poltergeist'
48
+ when :sauce
49
+ caps_opts = {
50
+ platform: settings.sl_platform,
51
+ browser_name: settings.sl_browser_name,
52
+ name: 'Capybara test name should be overwritten',
53
+ "max-duration" => settings.sl_max_duration,
54
+ 'idle-timeout' => settings.sl_idle_timeout,
55
+ 'selenium-version' => settings.sl_selenium_version,
56
+ 'record-screenshots' => settings.sl_record_screenshot,
57
+ 'video-upload-on-pass' => settings.sl_video_upload_on_pass
58
+ }
59
+
60
+ unless (settings.sl_browser_version.to_s || "").empty?
61
+ caps_opts['browser-version'] = settings.sl_browser_version.to_s
62
+ end
63
+
64
+ options = {
65
+ url: settings.sl_url,
66
+ desired_capabilities: Selenium::WebDriver::Remote::Capabilities.new(caps_opts),
67
+ http_client: Selenium::WebDriver::Remote::Http::Default.new.tap{|c| c.timeout = settings.timeout_medium},
68
+ browser: :remote
69
+ }
70
+
71
+ Capybara.register_driver :sauce do |app|
72
+ Capybara::Selenium::Driver.new(app, options)
73
+ end
74
+
75
+ else
76
+ log.error "Unknown '#{settings.driver}' driver. Check your settings, it should be one of [selenium, selenium_dev, webkit, sauce]"
77
+ end
78
+
79
+ Capybara.default_driver = settings.driver.to_sym
80
+ Capybara.javascript_driver = settings.driver.to_sym
81
+
82
+ def sauce_resource_path(name)
83
+ host = "https://#{settings.sl_user}:#{settings.sl_api_key}@saucelabs.com"
84
+ path = "/rest/#{settings.sl_user}/jobs/#{session_id}/results/#{name}"
85
+ "#{host}#{path}"
86
+ end
87
+
88
+ def update_sauce_job_status(json_data = {})
89
+ host = "http://#{settings.sl_user}:#{settings.sl_api_key}@saucelabs.com"
90
+ path = "/rest/v1/#{settings.sl_user}/jobs/#{session_id}"
91
+ url = "#{host}#{path}"
92
+ RestClient.put url, json_data.to_json, content_type: :json, accept: :json
93
+ end
94
+
95
+ def suite_name
96
+ res = if ENV['RAKE_TASK']
97
+ res = ENV['RAKE_TASK'].sub(/(?:r?spec|cucumber):?(.*)/, '\1').upcase
98
+ res.empty? ? 'ALL' : res
99
+ else
100
+ 'CUSTOM'
101
+ end
102
+ "#{res} #{settings.sl_browser_name.upcase}"
103
+ end
104
+
105
+ def session_id
106
+ Capybara.current_session.driver.browser.instance_variable_get(:@bridge).session_id
107
+ end
108
+
109
+ end
110
+
@@ -0,0 +1,41 @@
1
+ module DataGenerator
2
+
3
+ module DataStorage
4
+ @data ||= {}
5
+
6
+ class << self
7
+ def store(ns, key, value)
8
+ check_ns(ns)
9
+ @data[ns][key] = value
10
+ end
11
+
12
+ def extract(ns, key=nil)
13
+ check_ns(ns)
14
+ key ? @data[ns][key] : @data[ns]
15
+ end
16
+
17
+ def clear_ns(ns)
18
+ init_ns(ns)
19
+ end
20
+
21
+ private
22
+
23
+ def check_ns(ns)
24
+ if ns
25
+ init_ns(ns) if ns_absent?(ns)
26
+ else
27
+ raise 'Data storage namespace can not be empty'
28
+ end
29
+ end
30
+
31
+ def ns_absent?(ns)
32
+ !@data.key?(ns)
33
+ end
34
+
35
+ def init_ns(ns)
36
+ @data[ns] = {}
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,89 @@
1
+ module DataGenerator
2
+ module Gen
3
+ # examples:
4
+ # member = Gen::user #user.email: 'u<XXXX>@<settings.mail_pop3_domain>'
5
+ # member = Gen::user('user@test.com') #user.email: 'member@test.com'
6
+ # member = Gen::user(settings.def_test_user) #user.email: settings.def_test_user
7
+
8
+ class << self
9
+ def user(params={})
10
+ prefix = serial
11
+ default = {
12
+ email: gen_user_email(prefix),
13
+ login: nil,
14
+ first_name: gen_first_name(prefix),
15
+ last_name: gen_last_name(prefix),
16
+ password: settings.def_test_pass
17
+ }
18
+ User.new(default.merge(params))
19
+ end
20
+
21
+ def given_user_by_number(num)
22
+ data = DataStorage.extract('user', num.to_i)
23
+ unless data
24
+ data = Gen::user
25
+ DataStorage.store('user', num.to_i, data)
26
+ end
27
+ data
28
+ end
29
+
30
+ def serial
31
+ a = [('a'..'z').to_a, (0..9).to_a].flatten.shuffle
32
+ "#{Time.now.utc.strftime("%j%H%M%S")}#{a[0..4].join}"
33
+ end
34
+
35
+ def delete_all_mailboxes
36
+ DataStorage.extract('user').each_value do |user|
37
+ user.delete_mailbox
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def gen_user_email(serial=nil)
44
+ "#{gen_user_name(serial)}@#{settings.mail_pop3_domain}"
45
+ end
46
+
47
+ def gen_user_name(serial=nil)
48
+ gen_entity('u', serial)
49
+ end
50
+
51
+ def gen_first_name(serial=nil)
52
+ gen_entity('FirstName', serial)
53
+ end
54
+
55
+ def gen_last_name(serial=nil)
56
+ gen_entity('LastName', serial)
57
+ end
58
+
59
+ def gen_entity(prefix, serial)
60
+ "#{prefix}#{serial.nil? ? self.serial : serial}"
61
+ end
62
+ end
63
+
64
+ class User < Object
65
+
66
+ attr_reader :login, :domain, :email, :password, :mailbox, :first_name, :last_name, :full_name
67
+
68
+ def initialize(params={})
69
+ @email = params.delete(:email)
70
+ @email_name, @domain = @email.to_s.split('@')
71
+ @login = params.delete(:login) || @email_name
72
+ @password = params.delete(:password)
73
+ @first_name = params.delete(:first_name)
74
+ @last_name = params.delete(:last_name)
75
+ @full_name = "#@first_name #@last_name"
76
+ @mailbox = params.delete(:mailbox)
77
+ end
78
+
79
+ def create_mailbox
80
+ @mailbox = MailClient.create_mailbox(@email_name) if settings.mail_pop3_domain == @domain
81
+ self
82
+ end
83
+
84
+ def delete_mailbox
85
+ MailClient.delete_mailbox(@mailbox) unless @mailbox.nil?
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,46 @@
1
+ require 'rspec/matchers'
2
+ require 'howitzer/utils/email/mail_client'
3
+
4
+ class Email
5
+ include RSpec::Matchers
6
+ attr_reader :recipient_address
7
+
8
+ def initialize(message)
9
+ message.subject.should include(self.class::SUBJECT)
10
+ @recipient_address = ::Mail::Address.new(message.to.first)
11
+ @message = message
12
+ end
13
+
14
+ def self.find_by_recipient(recipient)
15
+ find(recipient, self::SUBJECT)
16
+ end
17
+
18
+ def self.find(recipient, subject)
19
+ messages = MailClient.by_email(recipient).find_mail do |mail|
20
+ /#{Regexp.escape(subject)}/ === mail.subject && mail.to == [recipient]
21
+ end
22
+
23
+ if messages.first.nil?
24
+ log.error "#{self} was not found (recipient: '#{recipient}')"
25
+ messages.first.should_not be_nil
26
+ end
27
+ new(messages.first)
28
+ end
29
+
30
+ def plain_text_body
31
+ get_mime_part(@message, 'text/plain').to_s
32
+ end
33
+
34
+ def get_mime_part(part, type)
35
+ return part.body if part["content-type"].to_s =~ %r!#{type}!
36
+ # Recurse the multi-parts
37
+ part.parts.each do |sub_part|
38
+ r = get_mime_part(sub_part, type)
39
+ return r if r
40
+ end
41
+ nil
42
+ end
43
+
44
+ protected :get_mime_part
45
+
46
+ end
@@ -0,0 +1,126 @@
1
+ require 'net/pop'
2
+ require 'timeout'
3
+ require 'mail'
4
+ require 'howitzer/utils/email/mailgun_helper'
5
+
6
+ class MailClient
7
+
8
+ extend MailgunHelper
9
+ @clients = {}
10
+
11
+ def self.default
12
+ log.info "Connect to default mailbox"
13
+ options = self.merge_opts
14
+ @clients[options] = MailClient.send :new unless @clients.has_key?(options)
15
+ @clients[options]
16
+ end
17
+
18
+ def self.by_email(name)
19
+ log.info "Connect to '#{name}' mailbox"
20
+ options = self.merge_opts(:pop3 => {:user_name => name}, :smtp => {})
21
+ @clients[options] = MailClient.send :new, options unless @clients.has_key?(options)
22
+ @clients[options]
23
+ end
24
+
25
+ def find_mail(max_wait = settings.mail_pop3_timeout, keep_or_delete = :delete, &block)
26
+ messages = []
27
+ time_of_start = Time.now
28
+ exception = nil
29
+ while Time.now < time_of_start + max_wait
30
+ begin
31
+ exception = nil
32
+ start do |pop_obj|
33
+ pop_obj.mails.each do |mail|
34
+ begin
35
+ mail_message = Mail.new(mail.pop.to_s)
36
+ if block_given?
37
+ if block.call(mail_message)
38
+ if keep_or_delete == :delete
39
+ mail.delete
40
+ log.info "Mail '#{mail_message.subject}' deleted from #{@options[:pop3][:user_name]}"
41
+ end
42
+ messages << mail_message
43
+ @old_ids << mail.unique_id
44
+ return messages
45
+ end
46
+ else
47
+ messages << mail_message
48
+ end
49
+ rescue Net::POPError => e
50
+ log.warn "Exception in POP find_mail: #{e.message}"
51
+ exception = e
52
+ end
53
+ end
54
+ end
55
+
56
+ sleep 5
57
+ rescue Errno::ETIMEDOUT, Errno::ECONNRESET, Net::POPAuthenticationError => e
58
+ log.warn "Exception in POP find_mail: #{e.message}. Will retry."
59
+ exception = e
60
+ end
61
+ end
62
+ log.error exception unless exception.nil?
63
+ messages
64
+ end
65
+
66
+ def send_mail(mail_from, mail_to, mail_subject, mail_body, mail_attachment=nil)
67
+ log.info "Send emails with next parameters:\n " +
68
+ "mail_from : #{mail_from},\n mail_to : #{mail_to}\n " +
69
+ "mail_subject : #{mail_subject},\n has_attachment? : #{!mail_attachment.nil?}"
70
+ outbound_mail = Mail.new do
71
+ from(mail_from)
72
+ subject(mail_subject)
73
+ body(mail_body)
74
+ add_file(mail_attachment) unless mail_attachment.nil?
75
+ end
76
+ outbound_mail[:to] = mail_to
77
+
78
+ retryable(:on => [Errno::ETIMEDOUT, Timeout::Error], :sleep => 10, :tries => 3, :trace => true, :logger => log) do
79
+ outbound_mail.deliver!
80
+ end
81
+ end
82
+
83
+ def empty_inbox
84
+ begin
85
+ start {|pop_obj| pop_obj.delete_all }
86
+ log.info "Email box (#{@options[:pop3][:user_name]}) was purged"
87
+ rescue Net::POPError => e
88
+ log.warn "Exception during deletion all messages from '#{@options[:pop3][:user_name]}':\n#{e.message}"
89
+ end
90
+ end
91
+
92
+ def start(&block)
93
+ retryable(:timeout => settings.mail_pop3_timeout, :sleep => 5, :trace => true, :logger => log) do
94
+ Net::POP3.start(@options[:pop3][:address], @options[:pop3][:port],
95
+ @options[:pop3][:user_name], @options[:pop3][:password], &block)
96
+ end
97
+ end
98
+
99
+ private
100
+ def initialize(*arg)
101
+ @options = MailClient.merge_opts(*arg)
102
+ Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
103
+ @old_ids = []
104
+ options = @options
105
+ ::Mail.defaults{delivery_method :smtp, options[:smtp]}
106
+ @time_of_start = Time.now
107
+ end
108
+
109
+ def self.merge_opts(opts={:smtp => {}, :pop3 => {}})
110
+ def_smtp_opts = {:address => settings.mail_smtp_server,
111
+ :port => settings.mail_smtp_port,
112
+ :domain => settings.mail_smtp_domain,
113
+ :user_name => settings.mail_smtp_user_name,
114
+ :password => settings.mail_smtp_user_pass,
115
+ :authentication => 'plain',
116
+ :enable_starttls_auto => true}
117
+
118
+ def_pop3_opts = {:address => settings.mail_pop3_server,
119
+ :port => settings.mail_pop3_port,
120
+ :user_name => settings.mail_pop3_user_name,
121
+ :password => settings.mail_pop3_user_pass}
122
+ {:smtp => def_smtp_opts.merge(opts[:smtp]),
123
+ :pop3 => def_pop3_opts.merge(opts[:pop3])}
124
+ end
125
+
126
+ end
@@ -0,0 +1,175 @@
1
+ #For details, please see https://github.com/mailgun/mailgun.rb or https://github.com/hardikshah/mailgun.rb
2
+ #Modified from original adding rest-client 'remove'
3
+ require 'active_resource'
4
+ require 'rest-client'
5
+
6
+ class Mailgun
7
+
8
+ # Initializes Mailgun API
9
+ # Must be called before any other method
10
+ #
11
+ # Parameters:
12
+ # api_key - Your API key
13
+ # api_url - API base URL
14
+ #
15
+ def self.init(api_key, api_url = "https://mailgun.net/api/")
16
+ MailgunResource.password = api_key
17
+ api_url = api_url.gsub(/\/$/, '') + "/"
18
+ MailgunResource.site = api_url
19
+ end
20
+
21
+ # This is a patch of private ActiveResource method.
22
+ # It takes HTTPResponse and raise AR-like error if response code is not 2xx
23
+ def self.handle_response(response)
24
+ case response.code.to_i
25
+ when 301,302
26
+ raise(Redirection.new(response))
27
+ when 200...400
28
+ response
29
+ when 400
30
+ raise(ActiveResource::BadRequest.new(response))
31
+ when 401
32
+ raise(ActiveResource::UnauthorizedAccess.new(response))
33
+ when 403
34
+ raise(ActiveResource::ForbiddenAccess.new(response))
35
+ when 404
36
+ raise(ActiveResource::ResourceNotFound.new(response))
37
+ when 405
38
+ raise(ActiveResource::MethodNotAllowed.new(response))
39
+ when 409
40
+ raise(ActiveResource::ResourceConflict.new(response))
41
+ when 410
42
+ raise(ActiveResource::ResourceGone.new(response))
43
+ when 422
44
+ raise(ActiveResource::ResourceInvalid.new(response))
45
+ when 401...500
46
+ raise(ActiveResource::ClientError.new(response))
47
+ when 500...600
48
+ raise(ActiveResource::ServerError.new(response))
49
+ else
50
+ raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
51
+ end
52
+ end
53
+
54
+ module RequestBuilder
55
+ def prepare_request(url_string)
56
+ uri = URI.parse(url_string)
57
+ http = Net::HTTP.new(uri.host, uri.port)
58
+ http.use_ssl = true if uri.port == 443
59
+ return [http, (uri.path + '?' + uri.query)]
60
+ end
61
+ end
62
+ end
63
+
64
+ class MailgunMessage
65
+ extend Mailgun::RequestBuilder
66
+
67
+ MAILGUN_TAG = 'X-Mailgun-Tag'
68
+
69
+ # Sends a MIME-formatted message
70
+ #
71
+ # raw_mime =
72
+ # "Content-Type: text/plain;charset=utf-8\n" +
73
+ # "From: me@host\n" +
74
+ # "To: you@host\n" +
75
+ # "Subject: Hello!\n\n" +
76
+ # "Body"
77
+ # MailgunMessage::send_raw("me@host", "you@host", raw_mime)
78
+ #
79
+ def self.send_raw(sender, recipients, raw_body, servername='')
80
+ uri_str = "#{MailgunResource.site}messages.eml?api_key=#{MailgunResource.password}&servername=#{servername}"
81
+ http, url = prepare_request(uri_str)
82
+ data = "#{sender}\n#{recipients}\n\n#{raw_body}"
83
+ res = http.post(url, data, {"Content-type" => "text/plain" })
84
+ Mailgun::handle_response(res)
85
+ end
86
+
87
+ # Sends a plain-text message
88
+ #
89
+ # MailgunMessage::send_text("me@host.com",
90
+ # "you@host.com",
91
+ # "Subject",
92
+ # "Hi!\nThis is message body")
93
+ #
94
+ def self.send_text(sender, recipients, subject, text, servername='', options = nil)
95
+ uri_str = "#{MailgunResource.site}messages.txt?api_key=#{MailgunResource.password}&servername=#{servername}"
96
+ params = { :sender => sender, :recipients => recipients, :subject => subject, :body => text}
97
+ unless options.nil?
98
+ params['options'] = ActiveSupport::JSON.encode(options)
99
+ end
100
+ http, url = prepare_request(uri_str)
101
+ req = Net::HTTP::Post.new(url)
102
+ req.set_form_data(params)
103
+ res = http.request(req)
104
+ Mailgun::handle_response(res)
105
+ end
106
+ end
107
+
108
+ # Base class for Mailgun resource classes
109
+ # It adds upsert() method on top of ActiveResource::Base
110
+ #
111
+ class MailgunResource < ActiveResource::Base
112
+ self.user = "api_key"
113
+ extend Mailgun::RequestBuilder
114
+
115
+ # Create new resource or update it if resource already exist.
116
+ # There are 2 differences between upsert() and save():
117
+ # - Upsert does not raise an exception if a resource already exist.
118
+ # - Upsert does not return the id of freshly inserted resource
119
+ #
120
+ # >> route = Route.new
121
+ # >> route.pattern = '*@mydomain.com'
122
+ # >> route.destination = 'http://mydomain.com/addcomment'
123
+ # >> route.upsert()
124
+ #
125
+ def upsert()
126
+ self.class.post('upsert', {}, self.to_xml())
127
+ end
128
+ end
129
+
130
+ # All mail arriving to emails addresses that have mailboxes associated
131
+ # will be stored on the server and can be later accessed via IMAP or POP3
132
+ # protocols.
133
+ #
134
+ # Mailbox has several properties:
135
+ #
136
+ # alex@gmail.com
137
+ # ^ ^
138
+ # | |
139
+ # user domain
140
+ #
141
+ class Mailbox < MailgunResource
142
+ # Example of a CSV file:
143
+ #
144
+ # john@domain.com, password
145
+ # doe@domain.com, password2
146
+ #
147
+ def self.upsert_from_csv(mailboxes)
148
+ uri_str = "#{MailgunResource.site}mailboxes.txt?api_key=#{MailgunResource.password}"
149
+ http, url = prepare_request(uri_str)
150
+ res = http.post(url, mailboxes, {"Content-type" => "text/plain" })
151
+ Mailgun::handle_response(res)
152
+ end
153
+
154
+ def self.remove(mailbox)
155
+ res = RestClient.delete "https://api:#{MailgunResource.password}"\
156
+ "@api.mailgun.net/v2/#{mailbox.domain}/mailboxes/#{mailbox.user}"
157
+ Mailgun::handle_response(res)
158
+ end
159
+ end
160
+
161
+
162
+ # This class represents Mailgun route.
163
+ # A route has 2 properties: pattern and destination
164
+ #
165
+ # Examples of patterns:
166
+ # - '*' - match all
167
+ # - exact match (foo@bar.com)
168
+ # - domain pattern, i.e. a pattern like "@example.com" - it will match all emails going to example.com
169
+ # - any regular expression
170
+ #
171
+ # Destination can be one of:
172
+ # - an emails address to forward to
173
+ # - a URL for HTTP POST
174
+ class Route < MailgunResource
175
+ end