pony-express 0.7.0 → 0.8.0
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/README.rdoc +2 -1
- data/VERSION +1 -1
- data/ext/mimetic.cxx +79 -20
- data/lib/pony-express.rb +56 -14
- metadata +3 -6
    
        data/README.rdoc
    CHANGED
    
    | @@ -21,8 +21,9 @@ historical perspective read  http://en.wikipedia.org/wiki/Pony_Express. | |
| 21 21 |  | 
| 22 22 | 
             
              require "pony-express"
         | 
| 23 23 | 
             
              mail = PonyExpress::Mail.new to: "burns@plant.local", from: "homer@home.local", via: "sendmail"
         | 
| 24 | 
            +
              mail.add cc: "smithers@plant.local", bcc: "carl@plant.local", replyto: "homer+work@home.local"
         | 
| 24 25 | 
             
              mail.add subject: "Hello Mr.Burns", text: "Can I have more donuts ?", html: "<strong>More Dooooonuuuuts!</strong>"
         | 
| 25 | 
            -
              mail.add attachments: [ "/home/homer/donuts.png" ]
         | 
| 26 | 
            +
              mail.add attachments: [ "/home/homer/donuts.png" ], headers: [{name: "X-FooBar", value: "test"}]
         | 
| 26 27 | 
             
              mail.dispatch
         | 
| 27 28 |  | 
| 28 29 | 
             
            Don't want to create new mail objects ? Just pass in all options to PonyExpress.mail
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            0. | 
| 1 | 
            +
            0.8.0
         | 
    
        data/ext/mimetic.cxx
    CHANGED
    
    | @@ -18,6 +18,8 @@ extern "C" { | |
| 18 18 | 
             
            #define FORCE_ENCODING(str,enc) rb_enc_associate(str, rb_to_encoding(enc)); ENC_CODERANGE_CLEAR(str);
         | 
| 19 19 | 
             
            #define TO_S(v)                 rb_funcall(v, rb_intern("to_s"), 0)
         | 
| 20 20 | 
             
            #define CSTRING(v)              RSTRING_PTR(TYPE(v) != T_STRING ? TO_S(v) : v)
         | 
| 21 | 
            +
            #define RBSTRING(v)             rb_str_new2(v.c_str())
         | 
| 22 | 
            +
            #define SYM(v)                  ID2SYM(rb_intern(v))
         | 
| 21 23 |  | 
| 22 24 | 
             
            static VALUE rb_mMimetic;
         | 
| 23 25 | 
             
            static VALUE rb_UTF8, rb_ASCII;
         | 
| @@ -74,7 +76,7 @@ bool mimetic_attach_file(MimeEntity *m, char* filename) { | |
| 74 76 | 
             
                    return true;
         | 
| 75 77 | 
             
                }
         | 
| 76 78 | 
             
                else {
         | 
| 77 | 
            -
                    rb_raise(rb_eRuntimeError, " | 
| 79 | 
            +
                    rb_raise(rb_eRuntimeError, "Unable to read attachment file %s", filename);
         | 
| 78 80 | 
             
                }
         | 
| 79 81 | 
             
                return false;
         | 
| 80 82 | 
             
            }
         | 
| @@ -124,17 +126,17 @@ void rb_load_mime_types(VALUE self, VALUE filename) { | |
| 124 126 | 
             
                    file.close();
         | 
| 125 127 | 
             
                }
         | 
| 126 128 | 
             
                else {
         | 
| 127 | 
            -
                    rb_raise(rb_eRuntimeError, " | 
| 129 | 
            +
                    rb_raise(rb_eRuntimeError, "Unable to load mime.types");
         | 
| 128 130 | 
             
                }
         | 
| 129 131 | 
             
            }
         | 
| 130 132 |  | 
| 131 133 | 
             
            VALUE rb_mimetic_build(VALUE self, VALUE options) {
         | 
| 132 134 | 
             
                ostringstream output;
         | 
| 133 135 | 
             
                VALUE attachment;
         | 
| 134 | 
            -
                VALUE text    = rb_hash_aref(options,  | 
| 135 | 
            -
                VALUE to      = rb_hash_aref(options,  | 
| 136 | 
            -
                VALUE from    = rb_hash_aref(options,  | 
| 137 | 
            -
                VALUE subject = rb_hash_aref(options,  | 
| 136 | 
            +
                VALUE text    = rb_hash_aref(options, SYM("text"));
         | 
| 137 | 
            +
                VALUE to      = rb_hash_aref(options, SYM("to"));
         | 
| 138 | 
            +
                VALUE from    = rb_hash_aref(options, SYM("from"));
         | 
| 139 | 
            +
                VALUE subject = rb_hash_aref(options, SYM("subject"));
         | 
| 138 140 |  | 
| 139 141 | 
             
                if (text    == Qnil) rb_raise(rb_eArgError, "Mimetic.build called without :text");
         | 
| 140 142 | 
             
                if (from    == Qnil) rb_raise(rb_eArgError, "Mimetic.build called without :from");
         | 
| @@ -142,15 +144,15 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) { | |
| 142 144 | 
             
                if (subject == Qnil) rb_raise(rb_eArgError, "Mimetic.build called without :subject");
         | 
| 143 145 |  | 
| 144 146 | 
             
                // optional fields
         | 
| 145 | 
            -
                VALUE mid     = rb_hash_aref(options,  | 
| 146 | 
            -
                VALUE html    = rb_hash_aref(options,  | 
| 147 | 
            -
                VALUE tcid    = rb_hash_aref(options,  | 
| 148 | 
            -
                VALUE hcid    = rb_hash_aref(options,  | 
| 149 | 
            -
                VALUE replyto = rb_hash_aref(options,  | 
| 150 | 
            -
                VALUE cc      = rb_hash_aref(options,  | 
| 151 | 
            -
                VALUE bcc     = rb_hash_aref(options,  | 
| 152 | 
            -
                VALUE files   = rb_hash_aref(options,  | 
| 153 | 
            -
                VALUE headers = rb_hash_aref(options,  | 
| 147 | 
            +
                VALUE mid     = rb_hash_aref(options, SYM("message_id"));
         | 
| 148 | 
            +
                VALUE html    = rb_hash_aref(options, SYM("html"));
         | 
| 149 | 
            +
                VALUE tcid    = rb_hash_aref(options, SYM("text_content_id"));
         | 
| 150 | 
            +
                VALUE hcid    = rb_hash_aref(options, SYM("html_content_id"));
         | 
| 151 | 
            +
                VALUE replyto = rb_hash_aref(options, SYM("replyto"));
         | 
| 152 | 
            +
                VALUE cc      = rb_hash_aref(options, SYM("cc"));
         | 
| 153 | 
            +
                VALUE bcc     = rb_hash_aref(options, SYM("bcc"));
         | 
| 154 | 
            +
                VALUE files   = rb_hash_aref(options, SYM("attachments"));
         | 
| 155 | 
            +
                VALUE headers = rb_hash_aref(options, SYM("headers"));
         | 
| 154 156 |  | 
| 155 157 | 
             
                if (mid != Qnil && TYPE(mid) != T_STRING)
         | 
| 156 158 | 
             
                    rb_raise(rb_eArgError, "Mimetic.build expects :message_id to be a string");
         | 
| @@ -168,9 +170,9 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) { | |
| 168 170 | 
             
                    rb_raise(rb_eArgError, "Mimetic.build expects :headers to be an array");
         | 
| 169 171 |  | 
| 170 172 | 
             
                VALUE errors  = Qnil;
         | 
| 171 | 
            -
                MimeEntity *message   =  | 
| 172 | 
            -
                MimeEntity *html_part =  | 
| 173 | 
            -
                MimeEntity *text_part =  | 
| 173 | 
            +
                MimeEntity *message   = 0;
         | 
| 174 | 
            +
                MimeEntity *html_part = 0;
         | 
| 175 | 
            +
                MimeEntity *text_part = 0;
         | 
| 174 176 | 
             
                MimeVersion v1("1.0");
         | 
| 175 177 |  | 
| 176 178 | 
             
                try {
         | 
| @@ -178,7 +180,7 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) { | |
| 178 180 |  | 
| 179 181 | 
             
                    if (html != Qnil && text != Qnil) {
         | 
| 180 182 | 
             
                        delete message;
         | 
| 181 | 
            -
                        message | 
| 183 | 
            +
                        message   = new MultipartAlternative;
         | 
| 182 184 | 
             
                        html_part = new MimeEntity;
         | 
| 183 185 | 
             
                        text_part = new MimeEntity;
         | 
| 184 186 | 
             
                        message->body().parts().push_back(text_part);
         | 
| @@ -253,13 +255,70 @@ VALUE rb_mimetic_build(VALUE self, VALUE options) { | |
| 253 255 | 
             
                    errors = rb_str_new2("Unknown Error");
         | 
| 254 256 | 
             
                }
         | 
| 255 257 |  | 
| 256 | 
            -
                rb_raise(rb_eRuntimeError, " | 
| 258 | 
            +
                rb_raise(rb_eRuntimeError, "%s\n", CSTRING(errors));
         | 
| 259 | 
            +
            }
         | 
| 260 | 
            +
             | 
| 261 | 
            +
            VALUE parse_mime_parts(MimeEntity &me) {
         | 
| 262 | 
            +
                VALUE contents = rb_ary_new();
         | 
| 263 | 
            +
                MimeEntityList::const_iterator curr = me.body().parts().begin(), end = me.body().parts().end();
         | 
| 264 | 
            +
                while (curr != end) {
         | 
| 265 | 
            +
                    VALUE content = rb_hash_new();
         | 
| 266 | 
            +
                    MimeEntity *part = *curr;
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                    rb_hash_aset(content, SYM("type"), RBSTRING(part->header().contentType().str()));
         | 
| 269 | 
            +
                    if (part->header().contentType().isMultipart()) {
         | 
| 270 | 
            +
                        rb_hash_aset(content, SYM("content"), parse_mime_parts(*part));
         | 
| 271 | 
            +
                    }
         | 
| 272 | 
            +
                    else {
         | 
| 273 | 
            +
                        rb_hash_aset(content, SYM("content"),      RBSTRING(part->body()));
         | 
| 274 | 
            +
                        rb_hash_aset(content, SYM("encoding"),     RBSTRING(part->header().contentTransferEncoding().str()));
         | 
| 275 | 
            +
                        rb_hash_aset(content, SYM("disposition"),  RBSTRING(part->header().contentDisposition().str()));
         | 
| 276 | 
            +
                    }
         | 
| 277 | 
            +
                    rb_ary_push(contents, content);
         | 
| 278 | 
            +
                    curr++;
         | 
| 279 | 
            +
                }
         | 
| 280 | 
            +
                return contents;
         | 
| 281 | 
            +
            }
         | 
| 282 | 
            +
             | 
| 283 | 
            +
            VALUE rb_mimetic_parse(VALUE self, VALUE data) {
         | 
| 284 | 
            +
                if (NIL_P(data))
         | 
| 285 | 
            +
                    return Qnil;
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                VALUE errors  = Qnil;
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                try {
         | 
| 290 | 
            +
                    stringstream mime;
         | 
| 291 | 
            +
                    mime << CSTRING(data);
         | 
| 292 | 
            +
                    MimeEntity me;
         | 
| 293 | 
            +
                    me.load(mime);
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                    VALUE message = rb_hash_new();
         | 
| 296 | 
            +
             | 
| 297 | 
            +
                    rb_hash_aset(message, SYM("from"),    RBSTRING(me.header().from().str()));
         | 
| 298 | 
            +
                    rb_hash_aset(message, SYM("to"),      RBSTRING(me.header().to().str()));
         | 
| 299 | 
            +
                    rb_hash_aset(message, SYM("subject"), RBSTRING(me.header().subject()));
         | 
| 300 | 
            +
                    rb_hash_aset(message, SYM("cc"),      RBSTRING(me.header().cc().str()));
         | 
| 301 | 
            +
                    rb_hash_aset(message, SYM("bcc"),     RBSTRING(me.header().bcc().str()));
         | 
| 302 | 
            +
                    rb_hash_aset(message, SYM("replyto"), RBSTRING(me.header().replyto().str()));
         | 
| 303 | 
            +
             | 
| 304 | 
            +
                    rb_hash_aset(message, SYM("contents"), parse_mime_parts(me));
         | 
| 305 | 
            +
                    return message;
         | 
| 306 | 
            +
                }
         | 
| 307 | 
            +
                catch(exception &e) {
         | 
| 308 | 
            +
                    errors = rb_str_new2(e.what());
         | 
| 309 | 
            +
                }
         | 
| 310 | 
            +
                catch (...) {
         | 
| 311 | 
            +
                    errors = rb_str_new2("Unknown Error");
         | 
| 312 | 
            +
                }
         | 
| 313 | 
            +
             | 
| 314 | 
            +
                rb_raise(rb_eRuntimeError, "%s\n", CSTRING(errors));
         | 
| 257 315 | 
             
            }
         | 
| 258 316 |  | 
| 259 317 | 
             
            extern "C"  {
         | 
| 260 318 | 
             
                void Init_mimetic(void) {
         | 
| 261 319 | 
             
                    rb_mMimetic = rb_define_module("Mimetic");
         | 
| 262 320 | 
             
                    rb_define_module_function(rb_mMimetic, "build", VALUEFUNC(rb_mimetic_build), 1);
         | 
| 321 | 
            +
                    rb_define_module_function(rb_mMimetic, "parse", VALUEFUNC(rb_mimetic_parse), 1);
         | 
| 263 322 | 
             
                    rb_define_module_function(rb_mMimetic, "load_mime_types", VALUEFUNC(rb_load_mime_types), 1);
         | 
| 264 323 | 
             
                    rb_UTF8  = rb_str_new2("UTF-8");
         | 
| 265 324 | 
             
                    rb_ASCII = rb_str_new2("US-ASCII");
         | 
    
        data/lib/pony-express.rb
    CHANGED
    
    | @@ -3,8 +3,9 @@ begin; require 'smtp_tls'; rescue LoadError; end | |
| 3 3 | 
             
            require_relative '../ext/mimetic'
         | 
| 4 4 |  | 
| 5 5 | 
             
            module PonyExpress
         | 
| 6 | 
            -
              TRANSPORTS | 
| 7 | 
            -
              DEFAULT_SMTP_OPTIONS = { : | 
| 6 | 
            +
              TRANSPORTS           = [ :smtp, :sendmail ]
         | 
| 7 | 
            +
              DEFAULT_SMTP_OPTIONS = { host: 'localhost', port: '25', domain: 'localhost.localdomain' }
         | 
| 8 | 
            +
              @@sendmail_binary    = '/usr/sbin/sendmail'
         | 
| 8 9 |  | 
| 9 10 | 
             
              Mimetic.load_mime_types File.dirname(__FILE__) + "/../mime.types"
         | 
| 10 11 |  | 
| @@ -13,8 +14,34 @@ module PonyExpress | |
| 13 14 | 
             
                Mimetic.build(options)
         | 
| 14 15 | 
             
              end
         | 
| 15 16 |  | 
| 17 | 
            +
              # NOTE: parse is very much WIP, YMMV
         | 
| 18 | 
            +
              def parse content
         | 
| 19 | 
            +
                Mimetic.parse(content)
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              # Build and dispatch email in one go.
         | 
| 23 | 
            +
              #
         | 
| 24 | 
            +
              # @example mail.
         | 
| 25 | 
            +
              #  require "pony-express"
         | 
| 26 | 
            +
              #  PonyExpress.mail to: "burns@plant.local", from: "homer@home.local", via: "sendmail",
         | 
| 27 | 
            +
              #                   subject: "Hello Mr.Burns", text: "Can I have more donuts ?",
         | 
| 28 | 
            +
              #                   html: "<strong>More Dooooonuuuuts!</strong>",
         | 
| 29 | 
            +
              #                   attachments: [ "/home/homer/donuts.png" ],
         | 
| 30 | 
            +
              #                   headers: [{name: "X-FooBar", value: "test"}]
         | 
| 31 | 
            +
              #
         | 
| 32 | 
            +
              # @param  [String]  to            Email address.
         | 
| 33 | 
            +
              # @param  [String]  from          Email address.
         | 
| 34 | 
            +
              # @param  [String]  subject       Subject (will be converted to quoted printable if needed).
         | 
| 35 | 
            +
              # @param  [String]  text          Plain text content.
         | 
| 36 | 
            +
              # @param  [String]  cc            Email address (optional).
         | 
| 37 | 
            +
              # @param  [String]  bcc           Email address (optional).
         | 
| 38 | 
            +
              # @param  [String]  replyto       Email address (optional).
         | 
| 39 | 
            +
              # @param  [String]  html          HTML content (optional).
         | 
| 40 | 
            +
              # @param  [Array]   attachments   List of email attachments (optional).
         | 
| 41 | 
            +
              # @param  [Array]   headers       List of email headers (optional).
         | 
| 42 | 
            +
              # @return [TrueClass or FalseClass]
         | 
| 16 43 | 
             
              def mail options
         | 
| 17 | 
            -
                via | 
| 44 | 
            +
                via         = options.delete(:via)         || :smtp
         | 
| 18 45 | 
             
                via_options = options.delete(:via_options) || {}
         | 
| 19 46 |  | 
| 20 47 | 
             
                if TRANSPORTS.include? via
         | 
| @@ -27,22 +54,19 @@ module PonyExpress | |
| 27 54 | 
             
                end
         | 
| 28 55 | 
             
              end
         | 
| 29 56 |  | 
| 57 | 
            +
              def sendmail_binary= binary
         | 
| 58 | 
            +
                @@sendmail_binary = binary
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 30 61 | 
             
              def sendmail_binary
         | 
| 31 | 
            -
                 | 
| 32 | 
            -
                sendmail.empty? ? '/usr/sbin/sendmail' : sendmail
         | 
| 62 | 
            +
                @@sendmail_binary
         | 
| 33 63 | 
             
              end
         | 
| 34 64 |  | 
| 35 | 
            -
              def transport_via_sendmail content, options={}
         | 
| 36 | 
            -
                IO.popen('-', 'w | 
| 37 | 
            -
                  if pipe
         | 
| 38 | 
            -
                    pipe.write(content)
         | 
| 39 | 
            -
                  else
         | 
| 40 | 
            -
                    exec(sendmail_binary, "-t")
         | 
| 41 | 
            -
                  end
         | 
| 42 | 
            -
                end
         | 
| 65 | 
            +
              def transport_via_sendmail content, options = {}
         | 
| 66 | 
            +
                IO.popen([sendmail_binary, '-t'], 'w') {|io| io.write(content)}
         | 
| 43 67 | 
             
              end
         | 
| 44 68 |  | 
| 45 | 
            -
              def transport_via_smtp content, from, to, options={}
         | 
| 69 | 
            +
              def transport_via_smtp content, from, to, options = {}
         | 
| 46 70 | 
             
                o = DEFAULT_SMTP_OPTIONS.merge(options)
         | 
| 47 71 | 
             
                smtp = Net::SMTP.new(o[:host], o[:port])
         | 
| 48 72 | 
             
                if o[:tls]
         | 
| @@ -66,19 +90,37 @@ module PonyExpress | |
| 66 90 | 
             
                  @options = opt
         | 
| 67 91 | 
             
                end
         | 
| 68 92 |  | 
| 93 | 
            +
             | 
| 94 | 
            +
                # Add an option.
         | 
| 95 | 
            +
                #
         | 
| 96 | 
            +
                # @example add.
         | 
| 97 | 
            +
                #  require "pony-express"
         | 
| 98 | 
            +
                #  mail = PonyExpress::Mail.new
         | 
| 99 | 
            +
                #  mail.add to: "burns@plant.local"
         | 
| 69 100 | 
             
                def add opt
         | 
| 70 101 | 
             
                  @options.merge! opt
         | 
| 71 102 | 
             
                end
         | 
| 72 103 |  | 
| 104 | 
            +
                # Remove an option.
         | 
| 105 | 
            +
                #
         | 
| 106 | 
            +
                # @example remove.
         | 
| 107 | 
            +
                #  require "pony-express"
         | 
| 108 | 
            +
                #  mail = PonyExpress::Mail.new
         | 
| 109 | 
            +
                #  mail.add to: "burns@plant.local", cc: "smithers@plant.local"
         | 
| 110 | 
            +
                #  mail.remove :cc
         | 
| 73 111 | 
             
                def remove opt
         | 
| 74 112 | 
             
                  keys = opt.keys
         | 
| 75 113 | 
             
                  @options.reject! {|k, v| keys.include?(k) }
         | 
| 76 114 | 
             
                end
         | 
| 77 115 |  | 
| 116 | 
            +
                # Send the email via the selected transport.
         | 
| 117 | 
            +
                #
         | 
| 78 118 | 
             
                def dispatch
         | 
| 79 119 | 
             
                  mail(@options)
         | 
| 80 120 | 
             
                end
         | 
| 81 121 |  | 
| 122 | 
            +
                # Return the encoded email content.
         | 
| 123 | 
            +
                #
         | 
| 82 124 | 
             
                def content
         | 
| 83 125 | 
             
                  build(@options)
         | 
| 84 126 | 
             
                end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,12 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: pony-express
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash: 3
         | 
| 5 4 | 
             
              prerelease: false
         | 
| 6 5 | 
             
              segments: 
         | 
| 7 6 | 
             
              - 0
         | 
| 8 | 
            -
              -  | 
| 7 | 
            +
              - 8
         | 
| 9 8 | 
             
              - 0
         | 
| 10 | 
            -
              version: 0. | 
| 9 | 
            +
              version: 0.8.0
         | 
| 11 10 | 
             
            platform: ruby
         | 
| 12 11 | 
             
            authors: 
         | 
| 13 12 | 
             
            - Bharanee Rathna
         | 
| @@ -15,7 +14,7 @@ autorequire: | |
| 15 14 | 
             
            bindir: bin
         | 
| 16 15 | 
             
            cert_chain: []
         | 
| 17 16 |  | 
| 18 | 
            -
            date:  | 
| 17 | 
            +
            date: 2011-04-07 00:00:00 +10:00
         | 
| 19 18 | 
             
            default_executable: 
         | 
| 20 19 | 
             
            dependencies: []
         | 
| 21 20 |  | 
| @@ -51,7 +50,6 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 51 50 | 
             
              requirements: 
         | 
| 52 51 | 
             
              - - ">="
         | 
| 53 52 | 
             
                - !ruby/object:Gem::Version 
         | 
| 54 | 
            -
                  hash: 3
         | 
| 55 53 | 
             
                  segments: 
         | 
| 56 54 | 
             
                  - 0
         | 
| 57 55 | 
             
                  version: "0"
         | 
| @@ -60,7 +58,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 60 58 | 
             
              requirements: 
         | 
| 61 59 | 
             
              - - ">="
         | 
| 62 60 | 
             
                - !ruby/object:Gem::Version 
         | 
| 63 | 
            -
                  hash: 3
         | 
| 64 61 | 
             
                  segments: 
         | 
| 65 62 | 
             
                  - 0
         | 
| 66 63 | 
             
                  version: "0"
         |