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.
- data/Gemfile +4 -0
 - data/Gemfile.lock +113 -0
 - data/LICENSE +22 -0
 - data/README.md +44 -0
 - data/Rakefile +2 -0
 - data/bin/howitzer +41 -0
 - data/generators/config/config_generator.rb +24 -0
 - data/generators/config/templates/cucumber.yml +11 -0
 - data/generators/config/templates/custom.yml +4 -0
 - data/generators/config/templates/default.yml +76 -0
 - data/generators/cucumber/cucumber_generator.rb +31 -0
 - data/generators/cucumber/templates/common_steps.rb +23 -0
 - data/generators/cucumber/templates/env.rb +38 -0
 - data/generators/cucumber/templates/example.feature +16 -0
 - data/generators/cucumber/templates/transformers.rb +28 -0
 - data/generators/emails/emails_generator.rb +23 -0
 - data/generators/emails/templates/example_email.rb +7 -0
 - data/generators/pages/pages_generator.rb +23 -0
 - data/generators/pages/templates/example_menu.rb +14 -0
 - data/generators/pages/templates/example_page.rb +15 -0
 - data/generators/root/root_generator.rb +25 -0
 - data/generators/root/templates/.gitignore +20 -0
 - data/generators/root/templates/Gemfile +4 -0
 - data/generators/root/templates/Rakefile +4 -0
 - data/generators/root/templates/boot.rb +16 -0
 - data/generators/tasks/tasks_generator.rb +22 -0
 - data/generators/tasks/templates/cucumber.rake +57 -0
 - data/howitzer.gemspec +36 -0
 - data/lib/howitzer.rb +5 -0
 - data/lib/howitzer/helpers.rb +58 -0
 - data/lib/howitzer/init.rb +5 -0
 - data/lib/howitzer/utils.rb +14 -0
 - data/lib/howitzer/utils/capybara_patched.rb +22 -0
 - data/lib/howitzer/utils/capybara_settings.rb +110 -0
 - data/lib/howitzer/utils/data_generator/data_storage.rb +41 -0
 - data/lib/howitzer/utils/data_generator/gen.rb +89 -0
 - data/lib/howitzer/utils/email/email.rb +46 -0
 - data/lib/howitzer/utils/email/mail_client.rb +126 -0
 - data/lib/howitzer/utils/email/mailgun.rb +175 -0
 - data/lib/howitzer/utils/email/mailgun_helper.rb +35 -0
 - data/lib/howitzer/utils/locator_store.rb +118 -0
 - data/lib/howitzer/utils/logger.rb +108 -0
 - data/lib/howitzer/version.rb +3 -0
 - data/lib/howitzer/web_page.rb +62 -0
 - 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
         
     |