From 9f4b2681f3d30107799818bfba248c669b40279f Mon Sep 17 00:00:00 2001 From: dtaniwaki Date: Sat, 28 Jun 2014 16:53:22 +0900 Subject: [PATCH] Initial commit --- .gitignore | 33 +++ .travis.yml | 19 ++ Gemfile | 3 + LICENSE | 20 ++ README.md | 41 ++++ Rakefile | 8 + gemfiles/actionmailer.3.0.x.gemfile | 4 + gemfiles/actionmailer.3.1.x.gemfile | 4 + gemfiles/actionmailer.3.2.x.gemfile | 4 + gemfiles/actionmailer.4.0.x.gemfile | 4 + gemfiles/actionmailer.4.1.x.gemfile | 4 + gemfiles/gemfile | 3 + lib/mandriller.rb | 6 + lib/mandriller/base.rb | 102 +++++++++ lib/mandriller/errors.rb | 4 + lib/mandriller/settings_methods.rb | 37 ++++ lib/mandriller/version.rb | 3 + mandriller.gemspec | 24 ++ spec/mandriller/base_spec.rb | 268 +++++++++++++++++++++++ spec/mandriller/settings_methods_spec.rb | 70 ++++++ spec/spec_helper.rb | 14 ++ spec/support/action_mailer.rb | 27 +++ 22 files changed, 702 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Gemfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Rakefile create mode 100644 gemfiles/actionmailer.3.0.x.gemfile create mode 100644 gemfiles/actionmailer.3.1.x.gemfile create mode 100644 gemfiles/actionmailer.3.2.x.gemfile create mode 100644 gemfiles/actionmailer.4.0.x.gemfile create mode 100644 gemfiles/actionmailer.4.1.x.gemfile create mode 100644 gemfiles/gemfile create mode 100644 lib/mandriller.rb create mode 100644 lib/mandriller/base.rb create mode 100644 lib/mandriller/errors.rb create mode 100644 lib/mandriller/settings_methods.rb create mode 100644 lib/mandriller/version.rb create mode 100644 mandriller.gemspec create mode 100644 spec/mandriller/base_spec.rb create mode 100644 spec/mandriller/settings_methods_spec.rb create mode 100644 spec/spec_helper.rb create mode 100644 spec/support/action_mailer.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8cee76 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/test/tmp/ +/test/version_tmp/ +/tmp/ +/log/ + +## Specific to RubyMotion: +.dat* +.repl_history +build/ + +## Documentation cache and generated files: +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ + +## Environment normalisation: +/.bundle/ +/lib/bundler/man/ + +## Misc +Gemfile.lock +gemfiles/*.lock +.ruby-version +.ruby-gemset +.rvmrc diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bd862b2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: ruby + +rvm: + - 1.9.3 + - 2.0.0 + - 2.1.0 +gemfile: + - gemfiles/gemfile + - gemfiles/actionmailer.3.0.x.gemfile + - gemfiles/actionmailer.3.1.x.gemfile + - gemfiles/actionmailer.3.2.x.gemfile + - gemfiles/actionmailer.4.0.x.gemfile + - gemfiles/actionmailer.4.1.x.gemfile + +script: "bundle exec rake spec" + +branches: + only: + - master diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..fa75df1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b90b821 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Daisuke Taniwaki + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9e913dd --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# mandriller + +[![Gem Version](https://badge.fury.io/rb/mandriller.svg)](http://badge.fury.io/rb/mandriller) [![Build Status](https://secure.travis-ci.org/dtaniwaki/mandriller.png)](http://travis-ci.org/dtaniwaki/mandriller) [![Coverage Status](https://coveralls.io/repos/dtaniwaki/mandriller/badge.png)](https://coveralls.io/r/dtaniwaki/mandriller) [![Code Climate](https://codeclimate.com/github/dtaniwaki/mandriller.png)](https://codeclimate.com/github/dtaniwaki/mandriller) + +Mandriller SMTP API integration for ActionMailer. + +## Installation + +Add the mandriller gem to your Gemfile. + +```ruby +gem "mandriller" +``` + +And run `bundle install`. + +## Usage + +```ruby +class UserMailer < Mandriller::Base + set_google_analytics_campaign + set_open_track + + def test_mail + set_click_track + mail from: 'from@example.com', to: 'to@example.com' + end +end +``` + +## Contributing + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new [Pull Request](../../pull/new/master) + +## Copyright + +Copyright (c) 2014 Daisuke Taniwaki. See [LICENSE](LICENSE) for details. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..0b93167 --- /dev/null +++ b/Rakefile @@ -0,0 +1,8 @@ +require "bundler/gem_tasks" + +require 'rspec/core' +require 'rspec/core/rake_task' +RSpec::Core::RakeTask.new(:spec) do |spec| + spec.pattern = FileList['spec/**/*_spec.rb'] +end +task :default => :spec diff --git a/gemfiles/actionmailer.3.0.x.gemfile b/gemfiles/actionmailer.3.0.x.gemfile new file mode 100644 index 0000000..4a8b2eb --- /dev/null +++ b/gemfiles/actionmailer.3.0.x.gemfile @@ -0,0 +1,4 @@ +source "http://rubygems.org" + +gem 'actionmailer', '~> 3.0' +gemspec :path => '../' diff --git a/gemfiles/actionmailer.3.1.x.gemfile b/gemfiles/actionmailer.3.1.x.gemfile new file mode 100644 index 0000000..00c79b0 --- /dev/null +++ b/gemfiles/actionmailer.3.1.x.gemfile @@ -0,0 +1,4 @@ +source "http://rubygems.org" + +gem 'actionmailer', '~> 3.1' +gemspec :path => '../' diff --git a/gemfiles/actionmailer.3.2.x.gemfile b/gemfiles/actionmailer.3.2.x.gemfile new file mode 100644 index 0000000..1d7bd9f --- /dev/null +++ b/gemfiles/actionmailer.3.2.x.gemfile @@ -0,0 +1,4 @@ +source "http://rubygems.org" + +gem 'actionmailer', '~> 3.2' +gemspec :path => '../' diff --git a/gemfiles/actionmailer.4.0.x.gemfile b/gemfiles/actionmailer.4.0.x.gemfile new file mode 100644 index 0000000..6efb4da --- /dev/null +++ b/gemfiles/actionmailer.4.0.x.gemfile @@ -0,0 +1,4 @@ +source "http://rubygems.org" + +gem 'actionmailer', '~> 4.0' +gemspec :path => '../' diff --git a/gemfiles/actionmailer.4.1.x.gemfile b/gemfiles/actionmailer.4.1.x.gemfile new file mode 100644 index 0000000..1a4d539 --- /dev/null +++ b/gemfiles/actionmailer.4.1.x.gemfile @@ -0,0 +1,4 @@ +source "http://rubygems.org" + +gem 'actionmailer', '~> 4.1' +gemspec :path => '../' diff --git a/gemfiles/gemfile b/gemfiles/gemfile new file mode 100644 index 0000000..d432a84 --- /dev/null +++ b/gemfiles/gemfile @@ -0,0 +1,3 @@ +source "http://rubygems.org" + +gemspec :path => '../' diff --git a/lib/mandriller.rb b/lib/mandriller.rb new file mode 100644 index 0000000..0264f11 --- /dev/null +++ b/lib/mandriller.rb @@ -0,0 +1,6 @@ +require 'mandriller/version' +require 'mandriller/errors' +require 'mandriller/base' + +module Mandriller +end diff --git a/lib/mandriller/base.rb b/lib/mandriller/base.rb new file mode 100644 index 0000000..e659d83 --- /dev/null +++ b/lib/mandriller/base.rb @@ -0,0 +1,102 @@ +require 'action_mailer' +require_relative 'settings_methods' + +class Mandriller::Base < ActionMailer::Base + include Mandriller::SettingsMethods + + BOOLEAN_SETTINGS = { + autotext: 'X-MC-Autotext', + autohtml: 'X-MC-AutoHtml', + url_strip_qs: 'X-MC-URLStripQS', + preserve_recipients: 'X-MC-PreserveRecipients', + inline_css: 'X-MC-InlineCSS', + google_analytics_campaign: 'X-MC-GoogleAnalyticsCampaign', + view_content_link: 'X-MC-ViewContentLink', + import: 'X-MC-Important', + } + STRING_SETTINGS = { + tracking_domain: 'X-MC-TrackingDomain', + signing_domain: 'X-MC-SigningDomain', + subaccount: 'X-MC-Subaccount', + bcc_address: 'X-MC-BccAddress', + ip_pool: 'X-MC-IpPool', + return_path_domain: 'X-MC-ReturnPathDomain', + } + JSON_SETTINGS = { + metadata: 'X-MC-Metadata', + merge_vars: 'X-MC-MergeVars', + } + define_settings_methods BOOLEAN_SETTINGS.keys, default: true + define_settings_methods STRING_SETTINGS.keys + define_settings_methods JSON_SETTINGS.keys + define_settings_methods :open_track, default: true + define_settings_methods :click_track, default: 'clicks' + define_settings_methods :send_at + + class_attribute :mandrill_template, :mandrill_google_analytics + + class << self + def set_template(template_name, block_name) + self.mandrill_template = [template_name, block_name] + end + + def set_google_analytics(*domains) + self.mandrill_google_analytics = domains.flatten + end + end + + def set_template(template_name, block_name = nil) + @mandrill_template = [template_name, block_name].compact + end + + def set_google_analytics(*domains) + @mandrill_google_analytics = domains.flatten + end + + def mail(*args) + m = super(*args) + + tracks = [] + tracks << ((@mandrill_open_track.nil? ? self.mandrill_open_track : @mandrill_open_track) ? 'opens' : nil) + tracks << (@mandrill_click_track.nil? ? self.mandrill_click_track : @mandrill_click_track) + tracks = tracks.compact.map(&:to_s) + unless tracks.empty? + tracks.each do |track| + validate_values!(track, %w(opens clicks_all clicks clicks_htmlonly clicks_textonly)) + end + self.headers['X-MC-Track'] = tracks.join(',') + end + + v = get_mandrill_setting("template") + self.headers['X-MC-Template'] = v.join('|') unless v.nil? + + v = get_mandrill_setting("google_analytics") + self.headers['X-MC-GoogleAnalytics'] = v.join(',') unless v.nil? + + v = get_mandrill_setting("send_at") + self.headers['X-MC-SendAt'] = v.to_time.utc.strftime('%Y-%m-%d %H:%M:%S') unless v.nil? + + BOOLEAN_SETTINGS.each do |key, header_name| + v = get_mandrill_setting(key) + self.headers[header_name] = v ? 'true' : 'false' unless v.nil? + end + + STRING_SETTINGS.each do |key, header_name| + v = get_mandrill_setting(key) + self.headers[header_name] = v.to_s unless v.nil? + end + + JSON_SETTINGS.each do |key, header_name| + v = get_mandrill_setting(key) + self.headers[header_name] = v.to_json unless v.nil? + end + + m + end + + private + + def validate_values!(value, valid_values) + raise Mandriller::InvalidHeaderValue, "#{value} is not included in #{valid_values.join(', ')}" unless valid_values.include?(value) + end +end diff --git a/lib/mandriller/errors.rb b/lib/mandriller/errors.rb new file mode 100644 index 0000000..147c107 --- /dev/null +++ b/lib/mandriller/errors.rb @@ -0,0 +1,4 @@ +module Mandriller + class Exception < ::Exception; end; + class InvalidHeaderValue < Exception; end; +end diff --git a/lib/mandriller/settings_methods.rb b/lib/mandriller/settings_methods.rb new file mode 100644 index 0000000..98f350c --- /dev/null +++ b/lib/mandriller/settings_methods.rb @@ -0,0 +1,37 @@ +module Mandriller + module SettingsMethods + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def define_settings_methods(*keys) + options = keys[-1].is_a?(Hash) ? keys.pop : {} + if default = options[:default] + arg_s = "v = #{default.inspect}" + else + arg_s = "v" + end + + keys.flatten.each do |key| + class_eval <<-EOS + class_attribute :mandrill_#{key} + def self.set_#{key}(#{arg_s}) + self.mandrill_#{key} = v + end + private_class_method :set_#{key} + def set_#{key}(#{arg_s}) + @mandrill_#{key} = v + end + private :set_#{key} + EOS + end + end + end + + def get_mandrill_setting(key) + instance_variable_defined?("@mandrill_#{key}") ? instance_variable_get("@mandrill_#{key}") : __send__("mandrill_#{key}") + end + private :get_mandrill_setting + end +end diff --git a/lib/mandriller/version.rb b/lib/mandriller/version.rb new file mode 100644 index 0000000..cdf32f9 --- /dev/null +++ b/lib/mandriller/version.rb @@ -0,0 +1,3 @@ +module Mandriller + VERSION = '0.0.2' +end diff --git a/mandriller.gemspec b/mandriller.gemspec new file mode 100644 index 0000000..d285e82 --- /dev/null +++ b/mandriller.gemspec @@ -0,0 +1,24 @@ +require File.expand_path('../lib/mandriller/version', __FILE__) + +Gem::Specification.new do |gem| + gem.name = "mandriller" + gem.version = Mandriller::VERSION + gem.platform = Gem::Platform::RUBY + gem.authors = ["Daisuke Taniwaki"] + gem.email = ["daisuketaniwaki@gmail.com"] + gem.homepage = "https://github.com/dtaniwaki/mandriller" + gem.summary = "Mandrill SMTP API integration for ActionMailer" + gem.description = "Mandrill SMTP API integration for ActionMailer" + gem.license = "MIT" + + gem.files = `git ls-files`.split("\n") + gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") + gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } + gem.require_paths = ['lib'] + + gem.add_dependency "actionmailer", ">= 4.1" + + gem.add_development_dependency "rake" + gem.add_development_dependency "rspec", ">= 3.0" + gem.add_development_dependency "coveralls" +end diff --git a/spec/mandriller/base_spec.rb b/spec/mandriller/base_spec.rb new file mode 100644 index 0000000..9d69086 --- /dev/null +++ b/spec/mandriller/base_spec.rb @@ -0,0 +1,268 @@ +require 'spec_helper' + +describe Mandriller::Base do + let(:global_settings) { lambda{} } + let(:local_settings) { lambda{} } + let(:klass) do + gs = global_settings + ls = local_settings + klass = Class.new(Mandriller::Base) do + self.mailer_name = 'foo_mailer' + instance_exec(&gs) + define_method :foo do + instance_exec(&ls) + mail from: 'from@example.com', to: ['to@example.com'] + end + end + allow_any_instance_of(ActionMailer::Base).to receive(:each_template).and_return('') + klass + end + subject { klass.foo } + + BOOLEAN_SETTINGS = { + autotext: 'X-MC-Autotext', + autohtml: 'X-MC-AutoHtml', + url_strip_qs: 'X-MC-URLStripQS', + preserve_recipients: 'X-MC-PreserveRecipients', + inline_css: 'X-MC-InlineCSS', + google_analytics_campaign: 'X-MC-GoogleAnalyticsCampaign', + view_content_link: 'X-MC-ViewContentLink', + import: 'X-MC-Important', + } + BOOLEAN_SETTINGS.each do |key, header| + describe "#{header} header" do + context "no set" do + it_behaves_like "without header", header + end + context "set by #set_#{key}" do + context "set default" do + let(:local_settings) { lambda{ __send__("set_#{key}") } } + it_behaves_like "with header", header, true + end + context "set true" do + let(:local_settings) { lambda{ __send__("set_#{key}", true) } } + it_behaves_like "with header", header, true + end + context "set false" do + let(:local_settings) { lambda{ __send__("set_#{key}", false) } } + it_behaves_like "with header", header, false + end + end + context "set by ::set_#{key}" do + context "set default" do + let(:global_settings) { lambda{ __send__("set_#{key}") } } + it_behaves_like "with header", header, true + end + context "set true" do + let(:global_settings) { lambda{ __send__("set_#{key}", true) } } + it_behaves_like "with header", header, true + end + context "set false" do + let(:global_settings) { lambda{ __send__("set_#{key}", false) } } + end + end + context "set by both #set_#{key} and ::set_#{key}" do + context "set true globally and set false locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", true) } } + let(:local_settings) { lambda{ __send__("set_#{key}", false) } } + it_behaves_like "with header", header, false + end + context "set false globally and set true locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", false) } } + let(:local_settings) { lambda{ __send__("set_#{key}", true) } } + it_behaves_like "with header", header, true + end + context "set value globally but set nil locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", true) } } + let(:local_settings) { lambda{ __send__("set_#{key}", nil) } } + it_behaves_like "without header", header + end + end + end + end + + STRING_SETTINGS = { + tracking_domain: 'X-MC-TrackingDomain', + signing_domain: 'X-MC-SigningDomain', + subaccount: 'X-MC-Subaccount', + bcc_address: 'X-MC-BccAddress', + ip_pool: 'X-MC-IpPool', + return_path_domain: 'X-MC-ReturnPathDomain', + } + STRING_SETTINGS.each do |key, header| + describe "#{header} header" do + context "no set" do + it_behaves_like "without header", header + end + context "set by #set_#{key}" do + let(:local_settings) { lambda{ __send__("set_#{key}", 'local-string') } } + it_behaves_like "with header", header, 'local-string' + end + context "set by ::set_#{key}" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'global-string') } } + it_behaves_like "with header", header, 'global-string' + end + context "set by both #set_#{key} and ::set_#{key}" do + context "set value globally and set value locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'global-string') } } + let(:local_settings) { lambda{ __send__("set_#{key}", 'local-string') } } + it_behaves_like "with header", header, 'local-string' + end + context "set value globally but set nil locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'global-string') } } + let(:local_settings) { lambda{ __send__("set_#{key}", nil) } } + it_behaves_like "without header", header + end + end + end + end + + JSON_SETTINGS = { + metadata: 'X-MC-Metadata', + merge_vars: 'X-MC-MergeVars', + } + JSON_SETTINGS.each do |key, header| + describe "#{header} header" do + context "no set" do + it_behaves_like "without header", header + end + context "set by #set_#{key}" do + let(:local_settings) { lambda{ __send__("set_#{key}", {local: 1}) } } + it_behaves_like "with header", header, '{"local":1}' + end + context "set by ::set_#{key}" do + let(:global_settings) { lambda{ __send__("set_#{key}", {global: 1}) } } + it_behaves_like "with header", header, '{"global":1}' + end + context "set by both #set_#{key} and ::set_#{key}" do + context "set value globally and set value locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", {global: 1}) } } + let(:local_settings) { lambda{ __send__("set_#{key}", {local: 1}) } } + it_behaves_like "with header", header, '{"local":1}' + end + context "set value globally but set nil locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", {global: 1}) } } + let(:local_settings) { lambda{ __send__("set_#{key}", nil) } } + it_behaves_like "without header", header + end + end + end + end + + describe "X-MC-Track header" do + header = "X-MC-Track" + context "no set" do + it_behaves_like "without header", header + end + context "set by #set_open_track" do + let(:local_settings) { lambda{ set_open_track } } + + it_behaves_like "with header", 'X-MC-Track', 'opens' + end + context "set by #set_open_track" do + let(:global_settings) { lambda{ set_open_track } } + + it_behaves_like "with header", 'X-MC-Track', 'opens' + end + context "set by #set_open_track" do + let(:local_settings) { lambda{ set_click_track :clicks } } + it_behaves_like "with header", 'X-MC-Track', 'clicks' + context "invalid type" do + let(:local_settings) { lambda{ set_click_track :invalid } } + it_behaves_like "raise an exception", Mandriller::InvalidHeaderValue + end + end + context "set by ::set_open_track" do + let(:global_settings) { lambda{ set_click_track :clicks } } + it_behaves_like "with header", 'X-MC-Track', 'clicks' + end + context "set by both ::set_open_track and ::set_click_track" do + let(:global_settings) { lambda{ set_open_track; set_click_track :clicks } } + it_behaves_like "with header", 'X-MC-Track', 'opens,clicks' + end + end + + describe "X-MC-Template header" do + key = "template" + header = "X-MC-Template" + context "no set" do + it_behaves_like "without header", header + end + context "set by #set_#{key}" do + let(:local_settings) { lambda{ __send__("set_#{key}", 'template1', 'block1') } } + it_behaves_like "with header", header, 'template1|block1' + end + context "set by ::set_#{key}" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'template1', 'block1') } } + it_behaves_like "with header", header, 'template1|block1' + end + context "set by both #set_#{key} and ::set_#{key}" do + context "set value globally and set value locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'template1', 'block1') } } + let(:local_settings) { lambda{ __send__("set_#{key}", 'template2', 'block2') } } + it_behaves_like "with header", header, 'tempalte2|block2' + end + context "set value globally but set nil locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'template1', 'block1') } } + let(:local_settings) { lambda{ __send__("set_#{key}", nil) } } + it_behaves_like "without header", header + end + end + end + + describe "X-MC-GoogleAnalytics header" do + key = "google_analytics" + header = "X-MC-GoogleAnalytics" + context "no set" do + it_behaves_like "without header", header + end + context "set by #set_#{key}" do + let(:local_settings) { lambda{ __send__("set_#{key}", 'domain1', 'domain2') } } + it_behaves_like "with header", header, 'domain1,domain2' + end + context "set by ::set_#{key}" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'domain1', 'domain2') } } + it_behaves_like "with header", header, 'domain1,domain2' + end + context "set by both #set_#{key} and ::set_#{key}" do + context "set value globally and set value locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'domain1', 'domain2') } } + let(:local_settings) { lambda{ __send__("set_#{key}", 'domain2', 'domain3') } } + it_behaves_like "with header", header, 'domain2,domain3' + end + context "set value globally but set nil locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", 'domain1', 'domain2') } } + let(:local_settings) { lambda{ __send__("set_#{key}", nil) } } + it_behaves_like "without header", header + end + end + end + + describe "X-MC-SendAt header" do + key = 'send_at' + header = "X-MC-SendAt" + context "no set" do + it_behaves_like "without header", header + end + context "set by #set_#{key}" do + let(:local_settings) { lambda{ __send__("set_#{key}", DateTime.new(2001, 1, 2, 3, 4, 5)) } } + it_behaves_like "with header", header, '2001-01-02 03:04:05' + end + context "set by ::set_#{key}" do + let(:global_settings) { lambda{ __send__("set_#{key}", DateTime.new(2001, 1, 2, 3, 4, 5)) } } + it_behaves_like "with header", header, '2001-01-02 03:04:05' + end + context "set by both #set_#{key} and ::set_#{key}" do + context "set value globally and set value locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", DateTime.new(2001, 1, 2, 3, 4, 5)) } } + let(:local_settings) { lambda{ __send__("set_#{key}", DateTime.new(2001, 1, 2, 3, 4, 6)) } } + it_behaves_like "with header", header, '2001-01-02 03:04:06' + end + context "set value globally but set nil locally" do + let(:global_settings) { lambda{ __send__("set_#{key}", DateTime.new(2001, 1, 2, 3, 4, 5)) } } + let(:local_settings) { lambda{ __send__("set_#{key}", nil) } } + it_behaves_like "without header", header + end + end + end +end diff --git a/spec/mandriller/settings_methods_spec.rb b/spec/mandriller/settings_methods_spec.rb new file mode 100644 index 0000000..6d49aaf --- /dev/null +++ b/spec/mandriller/settings_methods_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +describe Mandriller::SettingsMethods do + let(:klass) do + Class.new do + include Mandriller::SettingsMethods + end + end + subject do + klass.class_eval do + define_settings_methods :foo + end + klass + end + + it "defines the methods" do + expect(subject.private_methods).to include(:set_foo) + expect(subject).to respond_to(:mandrill_foo) + expect(subject.private_instance_methods).to include(:set_foo) + + instance = subject.new + expect(instance.instance_variable_defined?("@mandrill_foo")).to eq(false) + end + it "sets the instance variable" do + instance = subject.new + instance.send :set_foo, 'foo' + expect(instance.instance_variable_get("@mandrill_foo")).to eq('foo') + end + it "sets the class attribute" do + subject.send :set_foo, 'foo' + expect(subject.mandrill_foo).to eq('foo') + end + + context "with default option" do + subject do + klass.class_eval do + define_settings_methods :foo, default: 'bar' + end + klass + end + it "sets the instance variable with default value" do + instance = subject.new + instance.send :set_foo + expect(instance.instance_variable_get("@mandrill_foo")).to eq('bar') + end + it "sets the class attribute with default value" do + subject.send :set_foo + expect(subject.mandrill_foo).to eq('bar') + end + end + + describe "#get_mandrill_setting" do + it "returns class_attribute" do + subject.send :set_foo, 1 + instance = subject.new + expect(instance.send(:get_mandrill_setting, :foo)).to eq(1) + end + it "returns instance_variable" do + instance = subject.new + instance.send :set_foo, 2 + expect(instance.send(:get_mandrill_setting, :foo)).to eq(2) + end + it "returns instance_variable over class_attribute" do + subject.send :set_foo, 1 + instance = subject.new + instance.send :set_foo, 2 + expect(instance.send(:get_mandrill_setting, :foo)).to eq(2) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..c02374b --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,14 @@ +require 'rubygems' +require 'coveralls' +Coveralls.wear! + +require 'mandriller' + +Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each {|f| require f } + +RSpec.configure do |config| + config.before :suite do + ActionMailer::Base.delivery_method = :test + end +end + diff --git a/spec/support/action_mailer.rb b/spec/support/action_mailer.rb new file mode 100644 index 0000000..0e58430 --- /dev/null +++ b/spec/support/action_mailer.rb @@ -0,0 +1,27 @@ +shared_examples "with header" do |header, value| + it "sets header #{header}" do + expect { + subject.deliver! + }.to change { ActionMailer::Base.deliveries.count }.by(1) + m = ActionMailer::Base.deliveries.last + expect(m.header.to_s).to match(/(\r\n)?#{header}: #{value}(\r\n)?/) + end +end +shared_examples "without header" do |header| + it "does not set header #{header}" do + expect { + subject.deliver! + }.to change { ActionMailer::Base.deliveries.count }.by(1) + m = ActionMailer::Base.deliveries.last + expect(m.header.to_s).not_to match(/(\r\n)?#{header}: [^\r]*(\r\n)?/) + end +end +shared_examples "raise an exception" do |exception| + it "raises #{exception}" do + expect { + expect { + subject.deliver! + }.to raise_error(exception) + }.to change { ActionMailer::Base.deliveries.count }.by(0) + end +end