howitzer 0.0.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 (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