Permalink
Browse files

add minimal env loading support with README section

  • Loading branch information...
1 parent 0540aa9 commit 4fcf1cc45c7a766391956d3764f52824557b586b @adzap committed Feb 11, 2010
Showing with 131 additions and 11 deletions.
  1. +56 −0 README.rdoc
  2. +2 −2 adzap-ar_mailer.gemspec
  3. +73 −9 lib/action_mailer/ar_sendmail.rb
View
@@ -118,6 +118,62 @@ 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 should not be needed
+once the email has been generated and stored by ar_mailer in the email table.
+
+The minimal set up will also force the use of a separate mailer log file at
+RAILS_ROOT/log/mailer.log.
+
+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.
+
+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
@@ -2,11 +2,11 @@
Gem::Specification.new do |s|
s.name = %q{adzap-ar_mailer}
- s.version = "2.1.7"
+ s.version = "2.1.7.999"
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-01-19}
+ s.date = %q{2010-02-11}
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}
@@ -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.7'
+ VERSION = '2.1.7.999'
##
# 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,75 @@ 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 and mailer config at config/email.yml. It will also
+ # use its own separate logger file at log/mailer.log
+ #
+ 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_level = ActiveSupport::BufferedLogger.const_get((env == 'production' ? 'info' : 'debug').upcase)
+
+ ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(File.join('log', "#{env}.log"))
+ ActiveRecord::Base.logger.level = logger_level
+ db_config = read_config('config/database.yml')
+ ActiveRecord::Base.establish_connection db_config[env]
+
+ ActionMailer::Base.logger = ActiveSupport::BufferedLogger.new(File.join('log', "mailer.log"))
+ ActionMailer::Base.logger.level = logger_level
+ 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

0 comments on commit 4fcf1cc

Please sign in to comment.