/text/src/test/resources/examples/ruby/rails.in.rb
Ruby | 15466 lines | 11031 code | 1665 blank | 2770 comment | 670 complexity | cc3b4273ae9f93be89092edb70074a1b MD5 | raw file
Possible License(s): EPL-1.0, MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- require 'rbconfig'
- require 'find'
- require 'ftools'
- include Config
- # this was adapted from rdoc's install.rb by way of Log4r
- $sitedir = CONFIG["sitelibdir"]
- unless $sitedir
- version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
- $libdir = File.join(CONFIG["libdir"], "ruby", version)
- $sitedir = $:.find {|x| x =~ /site_ruby/ }
- if !$sitedir
- $sitedir = File.join($libdir, "site_ruby")
- elsif $sitedir !~ Regexp.quote(version)
- $sitedir = File.join($sitedir, version)
- end
- end
- # the acual gruntwork
- Dir.chdir("lib")
- Find.find("action_mailer", "action_mailer.rb") { |f|
- if f[-3..-1] == ".rb"
- File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
- else
- File::makedirs(File.join($sitedir, *f.split(/\//)))
- end
- }
- module ActionMailer
- module AdvAttrAccessor #:nodoc:
- def self.append_features(base)
- super
- base.extend(ClassMethods)
- end
- module ClassMethods #:nodoc:
- def adv_attr_accessor(*names)
- names.each do |name|
- ivar = "@#{name}"
- define_method("#{name}=") do |value|
- instance_variable_set(ivar, value)
- end
- define_method(name) do |*parameters|
- raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
- if parameters.empty?
- if instance_variables.include?(ivar)
- instance_variable_get(ivar)
- end
- else
- instance_variable_set(ivar, parameters.first)
- end
- end
- end
- end
- end
- end
- end
- require 'action_mailer/adv_attr_accessor'
- require 'action_mailer/part'
- require 'action_mailer/part_container'
- require 'action_mailer/utils'
- require 'tmail/net'
- module ActionMailer #:nodoc:
- # ActionMailer allows you to send email from your application using a mailer model and views.
- #
- # = Mailer Models
- # To use ActionMailer, you need to create a mailer model.
- #
- # $ script/generate mailer Notifier
- #
- # The generated model inherits from ActionMailer::Base. Emails are defined by creating methods within the model which are then
- # used to set variables to be used in the mail template, to change options on the mail, or
- # to add attachments.
- #
- # Examples:
- #
- # class Notifier < ActionMailer::Base
- # def signup_notification(recipient)
- # recipients recipient.email_address_with_name
- # from "system@example.com"
- # subject "New account information"
- # body "account" => recipient
- # end
- # end
- #
- # Mailer methods have the following configuration methods available.
- #
- # * <tt>recipients</tt> - Takes one or more email addresses. These addresses are where your email will be delivered to. Sets the <tt>To:</tt> header.
- # * <tt>subject</tt> - The subject of your email. Sets the <tt>Subject:</tt> header.
- # * <tt>from</tt> - Who the email you are sending is from. Sets the <tt>From:</tt> header.
- # * <tt>cc</tt> - Takes one or more email addresses. These addresses will receive a carbon copy of your email. Sets the <tt>Cc:</tt> header.
- # * <tt>bcc</tt> - Takes one or more email address. These addresses will receive a blind carbon copy of your email. Sets the <tt>Bcc</tt> header.
- # * <tt>sent_on</tt> - The date on which the message was sent. If not set, the header wil be set by the delivery agent.
- # * <tt>content_type</tt> - Specify the content type of the message. Defaults to <tt>text/plain</tt>.
- # * <tt>headers</tt> - Specify additional headers to be set for the message, e.g. <tt>headers 'X-Mail-Count' => 107370</tt>.
- #
- # The <tt>body</tt> method has special behavior. It takes a hash which generates an instance variable
- # named after each key in the hash containing the value that that key points to.
- #
- # So, for example, <tt>body "account" => recipient</tt> would result
- # in an instance variable <tt>@account</tt> with the value of <tt>recipient</tt> being accessible in the
- # view.
- #
- # = Mailer Views
- # Like ActionController, each mailer class has a corresponding view directory
- # in which each method of the class looks for a template with its name.
- # To define a template to be used with a mailing, create an <tt>.rhtml</tt> file with the same name as the method
- # in your mailer model. For example, in the mailer defined above, the template at
- # <tt>app/views/notifier/signup_notification.rhtml</tt> would be used to generate the email.
- #
- # Variables defined in the model are accessible as instance variables in the view.
- #
- # Emails by default are sent in plain text, so a sample view for our model example might look like this:
- #
- # Hi <%= @account.name %>,
- # Thanks for joining our service! Please check back often.
- #
- # = Sending Mail
- # Once a mailer action and template are defined, you can deliver your message or create it and save it
- # for delivery later:
- #
- # Notifier.deliver_signup_notification(david) # sends the email
- # mail = Notifier.create_signup_notification(david) # => a tmail object
- # Notifier.deliver(mail)
- #
- # You never instantiate your mailer class. Rather, your delivery instance
- # methods are automatically wrapped in class methods that start with the word
- # <tt>deliver_</tt> followed by the name of the mailer method that you would
- # like to deliver. The <tt>signup_notification</tt> method defined above is
- # delivered by invoking <tt>Notifier.deliver_signup_notification</tt>.
- #
- # = HTML Email
- # To send mail as HTML, make sure your view (the <tt>.rhtml</tt> file) generates HTML and
- # set the content type to html.
- #
- # class MyMailer < ActionMailer::Base
- # def signup_notification(recipient)
- # recipients recipient.email_address_with_name
- # subject "New account information"
- # body "account" => recipient
- # from "system@example.com"
- # content_type "text/html" # Here's where the magic happens
- # end
- # end
- #
- # = Multipart Email
- # You can explicitly specify multipart messages:
- #
- # class ApplicationMailer < ActionMailer::Base
- # def signup_notification(recipient)
- # recipients recipient.email_address_with_name
- # subject "New account information"
- # from "system@example.com"
- #
- # part :content_type => "text/html",
- # :body => render_message("signup-as-html", :account => recipient)
- #
- # part "text/plain" do |p|
- # p.body = render_message("signup-as-plain", :account => recipient)
- # p.transfer_encoding = "base64"
- # end
- # end
- # end
- #
- # Multipart messages can also be used implicitly because ActionMailer will automatically
- # detect and use multipart templates, where each template is named after the name of the action, followed
- # by the content type. Each such detected template will be added as separate part to the message.
- #
- # For example, if the following templates existed:
- # * signup_notification.text.plain.rhtml
- # * signup_notification.text.html.rhtml
- # * signup_notification.text.xml.rxml
- # * signup_notification.text.x-yaml.rhtml
- #
- # Each would be rendered and added as a separate part to the message,
- # with the corresponding content type. The same body hash is passed to
- # each template.
- #
- # = Attachments
- # Attachments can be added by using the +attachment+ method.
- #
- # Example:
- #
- # class ApplicationMailer < ActionMailer::Base
- # # attachments
- # def signup_notification(recipient)
- # recipients recipient.email_address_with_name
- # subject "New account information"
- # from "system@example.com"
- #
- # attachment :content_type => "image/jpeg",
- # :body => File.read("an-image.jpg")
- #
- # attachment "application/pdf" do |a|
- # a.body = generate_your_pdf_here()
- # end
- # end
- # end
- #
- # = Configuration options
- #
- # These options are specified on the class level, like <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
- #
- # * <tt>template_root</tt> - template root determines the base from which template references will be made.
- #
- # * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
- # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
- #
- # * <tt>server_settings</tt> - Allows detailed configuration of the server:
- # * <tt>:address</tt> Allows you to use a remote mail server. Just change it from its default "localhost" setting.
- # * <tt>:port</tt> On the off chance that your mail server doesn't run on port 25, you can change it.
- # * <tt>:domain</tt> If you need to specify a HELO domain, you can do it here.
- # * <tt>:user_name</tt> If your mail server requires authentication, set the username in this setting.
- # * <tt>:password</tt> If your mail server requires authentication, set the password in this setting.
- # * <tt>:authentication</tt> If your mail server requires authentication, you need to specify the authentication type here.
- # This is a symbol and one of :plain, :login, :cram_md5
- #
- # * <tt>raise_delivery_errors</tt> - whether or not errors should be raised if the email fails to be delivered.
- #
- # * <tt>delivery_method</tt> - Defines a delivery method. Possible values are :smtp (default), :sendmail, and :test.
- # Sendmail is assumed to be present at "/usr/sbin/sendmail".
- #
- # * <tt>perform_deliveries</tt> - Determines whether deliver_* methods are actually carried out. By default they are,
- # but this can be turned off to help functional testing.
- #
- # * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful
- # for unit and functional testing.
- #
- # * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
- # pick a different charset from inside a method with <tt>@charset</tt>.
- # * <tt>default_content_type</tt> - The default content type used for the main part of the message. Defaults to "text/plain". You
- # can also pick a different content type from inside a method with <tt>@content_type</tt>.
- # * <tt>default_mime_version</tt> - The default mime version used for the message. Defaults to nil. You
- # can also pick a different value from inside a method with <tt>@mime_version</tt>. When multipart messages are in
- # use, <tt>@mime_version</tt> will be set to "1.0" if it is not set inside a method.
- # * <tt>default_implicit_parts_order</tt> - When a message is built implicitly (i.e. multiple parts are assembled from templates
- # which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
- # ["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client
- # and appear last in the mime encoded message. You can also pick a different order from inside a method with
- # <tt>@implicit_parts_order</tt>.
- class Base
- include AdvAttrAccessor, PartContainer
- # Action Mailer subclasses should be reloaded by the dispatcher in Rails
- # when Dependencies.mechanism = :load.
- include Reloadable::Subclasses
-
- private_class_method :new #:nodoc:
- class_inheritable_accessor :template_root
- cattr_accessor :logger
- @@server_settings = {
- :address => "localhost",
- :port => 25,
- :domain => 'localhost.localdomain',
- :user_name => nil,
- :password => nil,
- :authentication => nil
- }
- cattr_accessor :server_settings
- @@raise_delivery_errors = true
- cattr_accessor :raise_delivery_errors
- @@delivery_method = :smtp
- cattr_accessor :delivery_method
-
- @@perform_deliveries = true
- cattr_accessor :perform_deliveries
-
- @@deliveries = []
- cattr_accessor :deliveries
- @@default_charset = "utf-8"
- cattr_accessor :default_charset
- @@default_content_type = "text/plain"
- cattr_accessor :default_content_type
-
- @@default_mime_version = nil
- cattr_accessor :default_mime_version
- @@default_implicit_parts_order = [ "text/html", "text/enriched", "text/plain" ]
- cattr_accessor :default_implicit_parts_order
- # Specify the BCC addresses for the message
- adv_attr_accessor :bcc
-
- # Define the body of the message. This is either a Hash (in which case it
- # specifies the variables to pass to the template when it is rendered),
- # or a string, in which case it specifies the actual text of the message.
- adv_attr_accessor :body
-
- # Specify the CC addresses for the message.
- adv_attr_accessor :cc
-
- # Specify the charset to use for the message. This defaults to the
- # +default_charset+ specified for ActionMailer::Base.
- adv_attr_accessor :charset
-
- # Specify the content type for the message. This defaults to <tt>text/plain</tt>
- # in most cases, but can be automatically set in some situations.
- adv_attr_accessor :content_type
-
- # Specify the from address for the message.
- adv_attr_accessor :from
-
- # Specify additional headers to be added to the message.
- adv_attr_accessor :headers
-
- # Specify the order in which parts should be sorted, based on content-type.
- # This defaults to the value for the +default_implicit_parts_order+.
- adv_attr_accessor :implicit_parts_order
-
- # Override the mailer name, which defaults to an inflected version of the
- # mailer's class name. If you want to use a template in a non-standard
- # location, you can use this to specify that location.
- adv_attr_accessor :mailer_name
-
- # Defaults to "1.0", but may be explicitly given if needed.
- adv_attr_accessor :mime_version
-
- # The recipient addresses for the message, either as a string (for a single
- # address) or an array (for multiple addresses).
- adv_attr_accessor :recipients
-
- # The date on which the message was sent. If not set (the default), the
- # header will be set by the delivery agent.
- adv_attr_accessor :sent_on
-
- # Specify the subject of the message.
- adv_attr_accessor :subject
-
- # Specify the template name to use for current message. This is the "base"
- # template name, without the extension or directory, and may be used to
- # have multiple mailer methods share the same template.
- adv_attr_accessor :template
- # The mail object instance referenced by this mailer.
- attr_reader :mail
- class << self
- def method_missing(method_symbol, *parameters)#:nodoc:
- case method_symbol.id2name
- when /^create_([_a-z]\w*)/ then new($1, *parameters).mail
- when /^deliver_([_a-z]\w*)/ then new($1, *parameters).deliver!
- when "new" then nil
- else super
- end
- end
- # Receives a raw email, parses it into an email object, decodes it,
- # instantiates a new mailer, and passes the email object to the mailer
- # object's #receive method. If you want your mailer to be able to
- # process incoming messages, you'll need to implement a #receive
- # method that accepts the email object as a parameter:
- #
- # class MyMailer < ActionMailer::Base
- # def receive(mail)
- # ...
- # end
- # end
- def receive(raw_email)
- logger.info "Received mail:\n #{raw_email}" unless logger.nil?
- mail = TMail::Mail.parse(raw_email)
- mail.base64_decode
- new.receive(mail)
- end
- # Deliver the given mail object directly. This can be used to deliver
- # a preconstructed mail object, like:
- #
- # email = MyMailer.create_some_mail(parameters)
- # email.set_some_obscure_header "frobnicate"
- # MyMailer.deliver(email)
- def deliver(mail)
- new.deliver!(mail)
- end
- end
- # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
- # will be initialized according to the named method. If not, the mailer will
- # remain uninitialized (useful when you only need to invoke the "receive"
- # method, for instance).
- def initialize(method_name=nil, *parameters) #:nodoc:
- create!(method_name, *parameters) if method_name
- end
- # Initialize the mailer via the given +method_name+. The body will be
- # rendered and a new TMail::Mail object created.
- def create!(method_name, *parameters) #:nodoc:
- initialize_defaults(method_name)
- send(method_name, *parameters)
- # If an explicit, textual body has not been set, we check assumptions.
- unless String === @body
- # First, we look to see if there are any likely templates that match,
- # which include the content-type in their file name (i.e.,
- # "the_template_file.text.html.rhtml", etc.). Only do this if parts
- # have not already been specified manually.
- if @parts.empty?
- templates = Dir.glob("#{template_path}/#{@template}.*")
- templates.each do |path|
- # TODO: don't hardcode rhtml|rxml
- basename = File.basename(path)
- next unless md = /^([^\.]+)\.([^\.]+\.[^\+]+)\.(rhtml|rxml)$/.match(basename)
- template_name = basename
- content_type = md.captures[1].gsub('.', '/')
- @parts << Part.new(:content_type => content_type,
- :disposition => "inline", :charset => charset,
- :body => render_message(template_name, @body))
- end
- unless @parts.empty?
- @content_type = "multipart/alternative"
- @parts = sort_parts(@parts, @implicit_parts_order)
- end
- end
- # Then, if there were such templates, we check to see if we ought to
- # also render a "normal" template (without the content type). If a
- # normal template exists (or if there were no implicit parts) we render
- # it.
- template_exists = @parts.empty?
- template_exists ||= Dir.glob("#{template_path}/#{@template}.*").any? { |i| File.basename(i).split(".").length == 2 }
- @body = render_message(@template, @body) if template_exists
- # Finally, if there are other message parts and a textual body exists,
- # we shift it onto the front of the parts and set the body to nil (so
- # that create_mail doesn't try to render it in addition to the parts).
- if !@parts.empty? && String === @body
- @parts.unshift Part.new(:charset => charset, :body => @body)
- @body = nil
- end
- end
- # If this is a multipart e-mail add the mime_version if it is not
- # already set.
- @mime_version ||= "1.0" if !@parts.empty?
- # build the mail object itself
- @mail = create_mail
- end
- # Delivers a TMail::Mail object. By default, it delivers the cached mail
- # object (from the #create! method). If no cached mail object exists, and
- # no alternate has been given as the parameter, this will fail.
- def deliver!(mail = @mail)
- raise "no mail object available for delivery!" unless mail
- logger.info "Sent mail:\n #{mail.encoded}" unless logger.nil?
- begin
- send("perform_delivery_#{delivery_method}", mail) if perform_deliveries
- rescue Object => e
- raise e if raise_delivery_errors
- end
- return mail
- end
- private
- # Set up the default values for the various instance variables of this
- # mailer. Subclasses may override this method to provide different
- # defaults.
- def initialize_defaults(method_name)
- @charset ||= @@default_charset.dup
- @content_type ||= @@default_content_type.dup
- @implicit_parts_order ||= @@default_implicit_parts_order.dup
- @template ||= method_name
- @mailer_name ||= Inflector.underscore(self.class.name)
- @parts ||= []
- @headers ||= {}
- @body ||= {}
- @mime_version = @@default_mime_version.dup if @@default_mime_version
- end
- def render_message(method_name, body)
- render :file => method_name, :body => body
- end
- def render(opts)
- body = opts.delete(:body)
- initialize_template_class(body).render(opts)
- end
- def template_path
- "#{template_root}/#{mailer_name}"
- end
- def initialize_template_class(assigns)
- ActionView::Base.new(template_path, assigns, self)
- end
- def sort_parts(parts, order = [])
- order = order.collect { |s| s.downcase }
- parts = parts.sort do |a, b|
- a_ct = a.content_type.downcase
- b_ct = b.content_type.downcase
- a_in = order.include? a_ct
- b_in = order.include? b_ct
- s = case
- when a_in && b_in
- order.index(a_ct) <=> order.index(b_ct)
- when a_in
- -1
- when b_in
- 1
- else
- a_ct <=> b_ct
- end
- # reverse the ordering because parts that come last are displayed
- # first in mail clients
- (s * -1)
- end
- parts
- end
- def create_mail
- m = TMail::Mail.new
- m.subject, = quote_any_if_necessary(charset, subject)
- m.to, m.from = quote_any_address_if_necessary(charset, recipients, from)
- m.bcc = quote_address_if_necessary(bcc, charset) unless bcc.nil?
- m.cc = quote_address_if_necessary(cc, charset) unless cc.nil?
- m.mime_version = mime_version unless mime_version.nil?
- m.date = sent_on.to_time rescue sent_on if sent_on
- headers.each { |k, v| m[k] = v }
- real_content_type, ctype_attrs = parse_content_type
- if @parts.empty?
- m.set_content_type(real_content_type, nil, ctype_attrs)
- m.body = Utils.normalize_new_lines(body)
- else
- if String === body
- part = TMail::Mail.new
- part.body = Utils.normalize_new_lines(body)
- part.set_content_type(real_content_type, nil, ctype_attrs)
- part.set_content_disposition "inline"
- m.parts << part
- end
- @parts.each do |p|
- part = (TMail::Mail === p ? p : p.to_mail(self))
- m.parts << part
- end
-
- if real_content_type =~ /multipart/
- ctype_attrs.delete "charset"
- m.set_content_type(real_content_type, nil, ctype_attrs)
- end
- end
- @mail = m
- end
- def perform_delivery_smtp(mail)
- destinations = mail.destinations
- mail.ready_to_send
- Net::SMTP.start(server_settings[:address], server_settings[:port], server_settings[:domain],
- server_settings[:user_name], server_settings[:password], server_settings[:authentication]) do |smtp|
- smtp.sendmail(mail.encoded, mail.from, destinations)
- end
- end
- def perform_delivery_sendmail(mail)
- IO.popen("/usr/sbin/sendmail -i -t","w+") do |sm|
- sm.print(mail.encoded.gsub(/\r/, ''))
- sm.flush
- end
- end
- def perform_delivery_test(mail)
- deliveries << mail
- end
- end
- end
- module ActionMailer
- module Helpers #:nodoc:
- def self.append_features(base) #:nodoc:
- super
- # Initialize the base module to aggregate its helpers.
- base.class_inheritable_accessor :master_helper_module
- base.master_helper_module = Module.new
- # Extend base with class methods to declare helpers.
- base.extend(ClassMethods)
- base.class_eval do
- # Wrap inherited to create a new master helper module for subclasses.
- class << self
- alias_method :inherited_without_helper, :inherited
- alias_method :inherited, :inherited_with_helper
- end
- # Wrap initialize_template_class to extend new template class
- # instances with the master helper module.
- alias_method :initialize_template_class_without_helper, :initialize_template_class
- alias_method :initialize_template_class, :initialize_template_class_with_helper
- end
- end
- module ClassMethods
- # Makes all the (instance) methods in the helper module available to templates rendered through this controller.
- # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
- # available to the templates.
- def add_template_helper(helper_module) #:nodoc:
- master_helper_module.module_eval "include #{helper_module}"
- end
- # Declare a helper:
- # helper :foo
- # requires 'foo_helper' and includes FooHelper in the template class.
- # helper FooHelper
- # includes FooHelper in the template class.
- # helper { def foo() "#{bar} is the very best" end }
- # evaluates the block in the template class, adding method #foo.
- # helper(:three, BlindHelper) { def mice() 'mice' end }
- # does all three.
- def helper(*args, &block)
- args.flatten.each do |arg|
- case arg
- when Module
- add_template_helper(arg)
- when String, Symbol
- file_name = arg.to_s.underscore + '_helper'
- class_name = file_name.camelize
-
- begin
- require_dependency(file_name)
- rescue LoadError => load_error
- requiree = / -- (.*?)(\.rb)?$/.match(load_error).to_a[1]
- msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
- raise LoadError.new(msg).copy_blame!(load_error)
- end
- add_template_helper(class_name.constantize)
- else
- raise ArgumentError, 'helper expects String, Symbol, or Module argument'
- end
- end
- # Evaluate block in template class if given.
- master_helper_module.module_eval(&block) if block_given?
- end
- # Declare a controller method as a helper. For example,
- # helper_method :link_to
- # def link_to(name, options) ... end
- # makes the link_to controller method available in the view.
- def helper_method(*methods)
- methods.flatten.each do |method|
- master_helper_module.module_eval <<-end_eval
- def #{method}(*args, &block)
- controller.send(%(#{method}), *args, &block)
- end
- end_eval
- end
- end
- # Declare a controller attribute as a helper. For example,
- # helper_attr :name
- # attr_accessor :name
- # makes the name and name= controller methods available in the view.
- # The is a convenience wrapper for helper_method.
- def helper_attr(*attrs)
- attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
- end
- private
- def inherited_with_helper(child)
- inherited_without_helper(child)
- begin
- child.master_helper_module = Module.new
- child.master_helper_module.send :include, master_helper_module
- child.helper child.name.underscore
- rescue MissingSourceFile => e
- raise unless e.is_missing?("helpers/#{child.name.underscore}_helper")
- end
- end
- end
- private
- # Extend the template class instance with our controller's helper module.
- def initialize_template_class_with_helper(assigns)
- returning(template = initialize_template_class_without_helper(assigns)) do
- template.extend self.class.master_helper_module
- end
- end
- end
- endrequire 'text/format'
- module MailHelper
- # Uses Text::Format to take the text and format it, indented two spaces for
- # each line, and wrapped at 72 columns.
- def block_format(text)
- formatted = text.split(/\n\r\n/).collect { |paragraph|
- Text::Format.new(
- :columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
- ).format
- }.join("\n")
-
- # Make list points stand on their own line
- formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
- formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
- formatted
- end
- end
- require 'action_mailer/adv_attr_accessor'
- require 'action_mailer/part_container'
- require 'action_mailer/utils'
- module ActionMailer
- # Represents a subpart of an email message. It shares many similar
- # attributes of ActionMailer::Base. Although you can create parts manually
- # and add them to the #parts list of the mailer, it is easier
- # to use the helper methods in ActionMailer::PartContainer.
- class Part
- include ActionMailer::AdvAttrAccessor
- include ActionMailer::PartContainer
- # Represents the body of the part, as a string. This should not be a
- # Hash (like ActionMailer::Base), but if you want a template to be rendered
- # into the body of a subpart you can do it with the mailer's #render method
- # and assign the result here.
- adv_attr_accessor :body
-
- # Specify the charset for this subpart. By default, it will be the charset
- # of the containing part or mailer.
- adv_attr_accessor :charset
-
- # The content disposition of this part, typically either "inline" or
- # "attachment".
- adv_attr_accessor :content_disposition
-
- # The content type of the part.
- adv_attr_accessor :content_type
-
- # The filename to use for this subpart (usually for attachments).
- adv_attr_accessor :filename
-
- # Accessor for specifying additional headers to include with this part.
- adv_attr_accessor :headers
-
- # The transfer encoding to use for this subpart, like "base64" or
- # "quoted-printable".
- adv_attr_accessor :transfer_encoding
- # Create a new part from the given +params+ hash. The valid params keys
- # correspond to the accessors.
- def initialize(params)
- @content_type = params[:content_type]
- @content_disposition = params[:disposition] || "inline"
- @charset = params[:charset]
- @body = params[:body]
- @filename = params[:filename]
- @transfer_encoding = params[:transfer_encoding] || "quoted-printable"
- @headers = params[:headers] || {}
- @parts = []
- end
- # Convert the part to a mail object which can be included in the parts
- # list of another mail object.
- def to_mail(defaults)
- part = TMail::Mail.new
- real_content_type, ctype_attrs = parse_content_type(defaults)
- if @parts.empty?
- part.content_transfer_encoding = transfer_encoding || "quoted-printable"
- case (transfer_encoding || "").downcase
- when "base64" then
- part.body = TMail::Base64.folding_encode(body)
- when "quoted-printable"
- part.body = [Utils.normalize_new_lines(body)].pack("M*")
- else
- part.body = body
- end
- # Always set the content_type after setting the body and or parts!
- # Also don't set filename and name when there is none (like in
- # non-attachment parts)
- if content_disposition == "attachment"
- ctype_attrs.delete "charset"
- part.set_content_type(real_content_type, nil,
- squish("name" => filename).merge(ctype_attrs))
- part.set_content_disposition(content_disposition,
- squish("filename" => filename).merge(ctype_attrs))
- else
- part.set_content_type(real_content_type, nil, ctype_attrs)
- part.set_content_disposition(content_disposition)
- end
- else
- if String === body
- part = TMail::Mail.new
- part.body = body
- part.set_content_type(real_content_type, nil, ctype_attrs)
- part.set_content_disposition "inline"
- m.parts << part
- end
-
- @parts.each do |p|
- prt = (TMail::Mail === p ? p : p.to_mail(defaults))
- part.parts << prt
- end
-
- part.set_content_type(real_content_type, nil, ctype_attrs) if real_content_type =~ /multipart/
- end
- headers.each { |k,v| part[k] = v }
- part
- end
- private
- def squish(values={})
- values.delete_if { |k,v| v.nil? }
- end
- end
- end
- module ActionMailer
- # Accessors and helpers that ActionMailer::Base and ActionMailer::Part have
- # in common. Using these helpers you can easily add subparts or attachments
- # to your message:
- #
- # def my_mail_message(...)
- # ...
- # part "text/plain" do |p|
- # p.body "hello, world"
- # p.transfer_encoding "base64"
- # end
- #
- # attachment "image/jpg" do |a|
- # a.body = File.read("hello.jpg")
- # a.filename = "hello.jpg"
- # end
- # end
- module PartContainer
- # The list of subparts of this container
- attr_reader :parts
- # Add a part to a multipart message, with the given content-type. The
- # part itself is yielded to the block so that other properties (charset,
- # body, headers, etc.) can be set on it.
- def part(params)
- params = {:content_type => params} if String === params
- part = Part.new(params)
- yield part if block_given?
- @parts << part
- end
- # Add an attachment to a multipart message. This is simply a part with the
- # content-disposition set to "attachment".
- def attachment(params, &block)
- params = { :content_type => params } if String === params
- params = { :disposition => "attachment",
- :transfer_encoding => "base64" }.merge(params)
- part(params, &block)
- end
- private
-
- def parse_content_type(defaults=nil)
- return [defaults && defaults.content_type, {}] if content_type.blank?
- ctype, *attrs = content_type.split(/;\s*/)
- attrs = attrs.inject({}) { |h,s| k,v = s.split(/=/, 2); h[k] = v; h }
- [ctype, {"charset" => charset || defaults && defaults.charset}.merge(attrs)]
- end
- end
- end
- module ActionMailer
- module Quoting #:nodoc:
- # Convert the given text into quoted printable format, with an instruction
- # that the text be eventually interpreted in the given charset.
- def quoted_printable(text, charset)
- text = text.gsub( /[^a-z ]/i ) { quoted_printable_encode($&) }.
- gsub( / /, "_" )
- "=?#{charset}?Q?#{text}?="
- end
- # Convert the given character to quoted printable format, taking into
- # account multi-byte characters (if executing with $KCODE="u", for instance)
- def quoted_printable_encode(character)
- result = ""
- character.each_byte { |b| result << "=%02x" % b }
- result
- end
- # A quick-and-dirty regexp for determining whether a string contains any
- # characters that need escaping.
- if !defined?(CHARS_NEEDING_QUOTING)
- CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
- end
- # Quote the given text if it contains any "illegal" characters
- def quote_if_necessary(text, charset)
- (text =~ CHARS_NEEDING_QUOTING) ?
- quoted_printable(text, charset) :
- text
- end
- # Quote any of the given strings if they contain any "illegal" characters
- def quote_any_if_necessary(charset, *args)
- args.map { |v| quote_if_necessary(v, charset) }
- end
- # Quote the given address if it needs to be. The address may be a
- # regular email address, or it can be a phrase followed by an address in
- # brackets. The phrase is the only part that will be quoted, and only if
- # it needs to be. This allows extended characters to be used in the
- # "to", "from", "cc", and "bcc" headers.
- def quote_address_if_necessary(address, charset)
- if Array === address
- address.map { |a| quote_address_if_necessary(a, charset) }
- elsif address =~ /^(\S.*)\s+(<.*>)$/
- address = $2
- phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
- "\"#{phrase}\" #{address}"
- else
- address
- end
- end
- # Quote any of the given addresses, if they need to be.
- def quote_any_address_if_necessary(charset, *args)
- args.map { |v| quote_address_if_necessary(v, charset) }
- end
- end
- end
- module ActionMailer
- module Utils #:nodoc:
- def normalize_new_lines(text)
- text.to_s.gsub(/\r\n?/, "\n")
- end
- module_function :normalize_new_lines
- end
- end
- #--
- # Text::Format for Ruby
- # Version 0.63
- #
- # Copyright (c) 2002 - 2003 Austin Ziegler
- #
- # $Id: format.rb,v 1.1.1.1 2004/10/14 11:59:57 webster132 Exp $
- #
- # ==========================================================================
- # Revision History ::
- # YYYY.MM.DD Change ID Developer
- # Description
- # --------------------------------------------------------------------------
- # 2002.10.18 Austin Ziegler
- # Fixed a minor problem with tabs not being counted. Changed
- # abbreviations from Hash to Array to better suit Ruby's
- # capabilities. Fixed problems with the way that Array arguments
- # are handled in calls to the major object types, excepting in
- # Text::Format#expand and Text::Format#unexpand (these will
- # probably need to be fixed).
- # 2002.10.30 Austin Ziegler
- # Fixed the ordering of the <=> for binary tests. Fixed
- # Text::Format#expand and Text::Format#unexpand to handle array
- # arguments better.
- # 2003.01.24 Austin Ziegler
- # Fixed a problem with Text::Format::RIGHT_FILL handling where a
- # single word is larger than #columns. Removed Comparable
- # capabilities (<=> doesn't make sense; == does). Added Symbol
- # equivalents for the Hash initialization. Hash initialization has
- # been modified so that values are set as follows (Symbols are
- # highest priority; strings are middle; defaults are lowest):
- # @columns = arg[:columns] || arg['columns'] || @columns
- # Added #hard_margins, #split_rules, #hyphenator, and #split_words.
- # 2003.02.07 Austin Ziegler
- # Fixed the installer for proper case-sensitive handling.
- # 2003.03.28 Austin Ziegler
- # Added the ability for a hyphenator to receive the formatter
- # object. Fixed a bug for strings matching /\A\s*\Z/ failing
- # entirely. Fixed a test case failing under 1.6.8.
- # 2003.04.04 Austin Ziegler
- # Handle the case of hyphenators returning nil for first/rest.
- # 2003.09.17 Austin Ziegler
- # Fixed a problem where #paragraphs(" ") was raising
- # NoMethodError.
- #
- # ==========================================================================
- #++
- module Text #:nodoc:
- # Text::Format for Ruby is copyright 2002 - 2005 by Austin Ziegler. It
- # is available under Ruby's licence, the Perl Artistic licence, or the
- # GNU GPL version 2 (or at your option, any later version). As a
- # special exception, for use with official Rails (provided by the
- # rubyonrails.org development team) and any project created with
- # official Rails, the following alternative MIT-style licence may be
- # used:
- #
- # == Text::Format Licence for Rails and Rails Applications
- # 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 names of its contributors may not be used to endorse or
- # promote products derived from this software without specific prior
- # written permission.
- #
- # 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.
- class Format
- VERSION = '0.63'
- # Local abbreviations. More can be added with Text::Format.abbreviations
- ABBREV = [ 'Mr', 'Mrs', 'Ms', 'Jr', 'Sr' ]
- # Formatting values
- LEFT_ALIGN = 0
- RIGHT_ALIGN = 1
- RIGHT_FILL = 2
- JUSTIFY = 3
- # Word split modes (only applies when #hard_margins is true).
- SPLIT_FIXED = 1
- SPLIT_CONTINUATION = 2
- SPLIT_HYPHENATION = 4
- SPLIT_CONTINUATION_FIXED = SPLIT_CONTINUATION | SPLIT_FIXED
- SPLIT_HYPHENATION_FIXED = SPLIT_HYPHENATION | SPLIT_FIXED
- SPLIT_HYPHENATION_CONTINUATION = SPLIT_HYPHENATION | SPLIT_CONTINUATION
- SPLIT_ALL = SPLIT_HYPHENATION | SPLIT_CONTINUATION | SPLIT_FIXED
- # Words forcibly split by Text::Format will be stored as split words.
- # This class represents a word forcibly split.
- class SplitWord
- # The word that was split.
- attr_reader :word
- # The first part of the word that was split.
- attr_reader :first
- # The remainder of the word that was split.
- attr_reader :rest
- def initialize(word, first, rest) #:nodoc:
- @word = word
- @first = first
- @rest = rest
- end
- end
- private
- LEQ_RE = /[.?!]['"]?$/
- def brk_re(i) #:nodoc:
- %r/((?:\S+\s+){#{i}})(.+)/
- end
- def posint(p) #:nodoc:
- p.to_i.abs
- end
- public
- # Compares two Text::Format objects. All settings of the objects are
- # compared *except* #hyphenator. Generated results (e.g., #split_words)
- # are not compared, either.
- def ==(o)
- (@text == o.text) &&
- (@columns == o.columns) &&
- (@left_margin == o.left_margin) &&
- (@right_margin == o.right_margin) &&
- (@hard_margins == o.hard_margins) &&
- (@split_rules == o.split_rules) &&
- (@first_indent == o.first_indent) &&
- (@body_indent == o.body_indent) &&
- (@tag_text == o.tag_text) &&
- (@tabstop == o.tabstop) &&
- (@format_style == o.format_style) &&
- (@extra_space == o.extra_space) &&
- (@tag_paragraph == o.tag_paragraph) &&
- (@nobreak == o.nobreak) &&
- (@abbreviations == o.abbreviations) &&
- (@nobreak_regex == o.nobreak_regex)
- end
- # The text to be manipulated. Note that value is optional, but if the
- # formatting functions are called without values, this text is what will
- # be formatted.
- #
- # *Default*:: <tt>[]</tt>
- # <b>Used in</b>:: All methods
- attr_accessor :text
- # The total width of the format area. The margins, indentation, and text
- # are formatted into this space.
- #
- # COLUMNS
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin indent text is formatted into here right margin
- #
- # *Default*:: <tt>72</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- attr_reader :columns
- # The total width of the format area. The margins, indentation, and text
- # are formatted into this space. The value provided is silently
- # converted to a positive integer.
- #
- # COLUMNS
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin indent text is formatted into here right margin
- #
- # *Default*:: <tt>72</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- def columns=(c)
- @columns = posint(c)
- end
- # The number of spaces used for the left margin.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # LEFT MARGIN indent text is formatted into here right margin
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- attr_reader :left_margin
- # The number of spaces used for the left margin. The value provided is
- # silently converted to a positive integer value.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # LEFT MARGIN indent text is formatted into here right margin
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- def left_margin=(left)
- @left_margin = posint(left)
- end
- # The number of spaces used for the right margin.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin indent text is formatted into here RIGHT MARGIN
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- attr_reader :right_margin
- # The number of spaces used for the right margin. The value provided is
- # silently converted to a positive integer value.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin indent text is formatted into here RIGHT MARGIN
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>,
- # <tt>#center</tt>
- def right_margin=(r)
- @right_margin = posint(r)
- end
- # The number of spaces to indent the first line of a paragraph.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin INDENT text is formatted into here right margin
- #
- # *Default*:: <tt>4</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_reader :first_indent
- # The number of spaces to indent the first line of a paragraph. The
- # value provided is silently converted to a positive integer value.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin INDENT text is formatted into here right margin
- #
- # *Default*:: <tt>4</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def first_indent=(f)
- @first_indent = posint(f)
- end
- # The number of spaces to indent all lines after the first line of a
- # paragraph.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin INDENT text is formatted into here right margin
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_reader :body_indent
- # The number of spaces to indent all lines after the first line of
- # a paragraph. The value provided is silently converted to a
- # positive integer value.
- #
- # columns
- # <-------------------------------------------------------------->
- # <-----------><------><---------------------------><------------>
- # left margin INDENT text is formatted into here right margin
- #
- # *Default*:: <tt>0</tt>
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- def body_indent=(b)
- @body_indent = posint(b)
- end
- # Normally, words larger than the format area will be placed on a line
- # by themselves. Setting this to +true+ will force words larger than the
- # format area to be split into one or more "words" each at most the size
- # of the format area. The first line and the original word will be
- # placed into <tt>#split_words</tt>. Note that this will cause the
- # output to look *similar* to a #format_style of JUSTIFY. (Lines will be
- # filled as much as possible.)
- #
- # *Default*:: +false+
- # <b>Used in</b>:: <tt>#format</tt>, <tt>#paragraphs</tt>
- attr_accessor :hard_margins
- # An array of words split during formatting if #hard_margins is set to
- # +true+.
- # #split_words << Text::Format::SplitWord.new(word, first, rest)
- attr_reader :split_words
- # The object responsible for hyphenating. It must respond to
- # #hyphena…
Large files files are truncated, but you can click here to view the full file