Permalink
Browse files

v2.1.9

  • Loading branch information...
2 parents b388ff6 + 4b99db6 commit 4e38fef0d88b2dbddbab43688395bdf539826208 @adzap committed Nov 9, 2011
Showing with 144 additions and 15 deletions.
  1. +5 −0 History.txt
  2. +58 −0 README.rdoc
  3. +4 −5 adzap-ar_mailer.gemspec
  4. +71 −9 lib/action_mailer/ar_sendmail.rb
  5. +6 −1 share/linux/ar_sendmail
View
5 History.txt
@@ -1,3 +1,8 @@
+= 2.1.9
+
+* Merged in minimal environment load option (experimental)
+* Add bundler support in linux init.d script
+
= 2.1.8
* Bugs fixed
View
58 README.rdoc
@@ -118,6 +118,64 @@ be loaded, so you don't need another TLS plugin to add the capability. This
patch allows you to explicit set if the server supports TLS by setting the
:tls option to true in your smtp_settings.
+
+=== EXPERIMENTAL: Minimal environment loading
+
+The biggest downside to using ar_mailer is that it loads the entire Rails app
+into memory. For larger apps this can be just a whole lot of wasted memory
+given all you need is to access the email table and mailer settings. We can
+get around this using a few conventions and save around 50% or more of the
+memory used when loading the full app.
+
+Loading the database is not much of a problem since the config sits in its own
+yaml which is easy to load in isolation from the app. The mailer settings are
+trickier since they are usually in the environment file which depends on loading
+the Railties system and therefore the whole app.
+
+To workaround for this we need to put all the SMTP settings in a yaml config
+file like the database settings. Like so
+
+in config/email.yml
+
+ development:
+ domain: example.com
+ address: smtp.example.com
+ port: 25
+
+ production:
+ domain: example.com
+ address: smtp.example.com
+ port: 25
+
+You can still have ActionMailer settings in each environment file but just be sure
+they don't override these SMTP settings. The other settings aren't needed when
+running ar_sendmail since they only apply to the generation of an email which has
+already happened inside the running application.
+
+To avoid duplication of these settings you need to have an initializer which will
+will read the same config file and load these settings. This is in case you switch
+back the normal environment loading. Keeping the email settings here is good idea,
+since just the like the database, they are external connection settings being stored
+in the config folder.
+
+The initializer in config/initializers/action_mailer.rb:
+
+ config_file = "#{Rails.root}/config/email.yml"
+ mailer_options = YAML::load(ERB.new(IO.read(config_file)).result)
+ if mailer_options[Rails.env]
+ ActionMailer::Base.smtp_settings = mailer_options[Rails.env].symbolize_keys
+ end
+
+At the moment if you are using a database driver that doesn't come with Rails, the gem
+will need to be installed on the target system.
+
+Now to run ar_sendmail with minimal environment we do:
+
+ ar_sendmail -e production -c /path/to/app --minimal
+
+If you have any troubles or find any bad assumptions in the minimal set up let me know.
+
+
=== Help
See ar_sendmail -h for options to ar_sendmail.
View
9 adzap-ar_mailer.gemspec
@@ -2,11 +2,11 @@
Gem::Specification.new do |s|
s.name = %q{adzap-ar_mailer}
- s.version = "2.1.8"
+ s.version = "2.1.9"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Eric Hodel", "Adam Meehan"]
- s.date = %q{2010-03-17}
+ s.date = %q{2011-11-09}
s.default_executable = %q{ar_sendmail}
s.description = %q{Even delivering email to the local machine may take too long when you have to send hundreds of messages. ar_mailer allows you to store messages into the database for later delivery by a separate process, ar_sendmail.}
s.email = %q{adam.meehan@gmail.com}
@@ -17,15 +17,14 @@ Gem::Specification.new do |s|
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
s.rubyforge_project = %q{seattlerb}
- s.rubygems_version = %q{1.3.5}
+ s.rubygems_version = %q{1.5.2}
s.summary = %q{A two-phase delivery agent for ActionMailer}
s.test_files = ["test/test_armailer.rb", "test/test_arsendmail.rb"]
if s.respond_to? :specification_version then
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<minitest>, [">= 1.5.0"])
s.add_development_dependency(%q<mocha>, [">= 0.9.8"])
else
View
80 lib/action_mailer/ar_sendmail.rb
@@ -39,10 +39,13 @@ module ActionMailer; end
class ActionMailer::ARSendmail
+ class RailsEnvironmentFailed < StandardError; end
+ class MinimalEnvironmentFailed < StandardError; end
+
##
# The version of ActionMailer::ARSendmail you are running.
- VERSION = '2.1.8'
+ VERSION = '2.1.9'
##
# Maximum number of times authentication will be consecutively retried
@@ -148,6 +151,7 @@ def self.process_args(args)
options[:Once] = false
options[:RailsEnv] = ENV['RAILS_ENV']
options[:Pidfile] = options[:Chdir] + '/log/ar_sendmail.pid'
+ options[:Minimal] = false
opts = OptionParser.new do |opts|
opts.banner = "Usage: #{name} [options]"
@@ -194,6 +198,12 @@ def self.process_args(args)
options[:Daemon] = true
end
+ opts.on( "--minimal",
+ "Load a minimal environment with settings from config/email.yml",
+ "Default: #{options[:Minimal]}") do |minimal|
+ options[:Minimal] = true
+ end
+
opts.on("-p", "--pidfile PIDFILE",
"Set the pidfile location",
"Default: #{options[:Chdir]}#{options[:Pidfile]}", String) do |pidfile|
@@ -247,21 +257,73 @@ def self.process_args(args)
ENV['RAILS_ENV'] = options[:RailsEnv]
- Dir.chdir options[:Chdir] do
- begin
- require 'config/environment'
- require 'action_mailer/ar_mailer'
- rescue LoadError
- usage opts, <<-EOF
+ begin
+ options[:Minimal] ? load_minimal_environment(options[:Chdir]) : load_rails_environment(options[:Chdir])
+ rescue RailsEnvironmentFailed
+ usage opts, <<-EOF
#{name} must be run from a Rails application's root to deliver email.
#{Dir.pwd} does not appear to be a Rails application root.
- EOF
- end
+ EOF
+ rescue MinimalEnvironmentFailed => e
+ usage opts, "Minimal environment loading has failed with error '#{e.message}'. Check minimal environment instructions or use the normal Rails environment loading."
end
return options
end
+ # Load full Rails environment
+ #
+ def self.load_rails_environment(base_path)
+ Dir.chdir(base_path) do
+ require 'config/environment'
+ require 'action_mailer/ar_mailer'
+ end
+ rescue LoadError
+ raise RailsEnvironmentFailed
+ end
+
+ # Load a minimal environment to save memory by not loading the entire Rails app.
+ # Requires a vendored Rails or bundler, and a mailer config at config/email.yml.
+ #
+ def self.load_minimal_environment(base_path)
+ Dir.chdir(base_path) do
+ Dir.glob('vendor/rails/*/lib').each { |dir| $:.unshift File.expand_path(dir) }
+ require 'yaml'
+ require 'erb'
+ require 'active_record'
+ require 'action_mailer'
+ require 'action_mailer/ar_mailer'
+ require 'app/models/email'
+
+ env = ENV['RAILS_ENV']
+
+ logger = ActiveSupport::BufferedLogger.new(File.join('log', "#{env}.log"))
+ logger.level = ActiveSupport::BufferedLogger.const_get((env == 'production' ? 'info' : 'debug').upcase)
+ ActiveRecord::Base.logger = ActionMailer::Base.logger = logger
+
+ db_config = read_config('config/database.yml')
+ ActiveRecord::Base.establish_connection db_config[env]
+
+ mailer_config = read_config('config/email.yml')
+ ActionMailer::Base.smtp_settings = mailer_config[env].symbolize_keys
+ end
+ rescue => e
+ raise MinimalEnvironmentFailed, e.message
+ end
+ def default_log_path
+ File.join(root_path, 'log', "#{environment}.log")
+ end
+
+ def default_log_level
+ environment == 'production' ? :info : :debug
+ end
+
+ # Open yaml config file and parse with ERB
+ #
+ def self.read_config(file)
+ YAML::load(ERB.new(IO.read(file)).result)
+ end
+
##
# Processes +args+ and runs as appropriate
View
7 share/linux/ar_sendmail
@@ -29,7 +29,12 @@ def start(app, options)
switches = ""
options.each {|k, v| switches << " --#{k} #{v}"}
STDOUT.write "Starting mailer for #{app} in #{options['environment']} mode ... "
- status = system("ar_sendmail -d #{switches}") ? "started" : "failed"
+ status = nil
+ Dir.chdir(options.delete('chdir')) do
+ command = 'ar_sendmail'
+ command = 'bundle exec ' + command if File.exists?('Gemfile')
+ status = system("#{command} -d #{switches}") ? "started" : "failed"
+ end
puts status
end

0 comments on commit 4e38fef

Please sign in to comment.