plain-david 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 64f568f2fbb872d1fff35baa76c1aca6cc500783
4
+ data.tar.gz: d4743a120e6ee5939099884b7a6f3efce990b5a1
5
+ SHA512:
6
+ metadata.gz: 50cc8f751a1a7c4f96551d3ef3b21cfeb8ae5ffb8b6c9f0ff7e4302850672d8d85658cdf4645b3db4ed1414495016258c907a20994e2acd18a1745ba776c2ca2
7
+ data.tar.gz: 4ec7e5b0e52696f60daa561f4a99de99bb2e12b7b0a5cb9b07ec6fbb008d891ada7e6cf7bf0c51546d28258ec73d7ec3eef63f68ba5b07a7ed69c3ca3b27bf63
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright © 2013 Luca Spiller
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Plain David
2
+
3
+ Plain David automatically generatess a text part for your HTML emails.
4
+
5
+ ## Install
6
+
7
+ Add the gem to Rails' Gemfile
8
+
9
+ gem 'plain-david', github: 'lucaspiller/plain-david'
10
+
11
+ ## Usage
12
+
13
+ Nothing. Just send emails as normal, and a text part will be automatically generated. The email will be changed from HTML to multipart like magic!
14
+
15
+ It works seamlessly with css inliners like [Roadie](https://github.com/kandadaboggu/roadie).
16
+
17
+ ## Conversion Strategies
18
+
19
+ You can set your own conversion strategy. The default `MarkdownStrategy` converts the HTML to Markdown. If you want to use your own you just need to set the `strategy` option.
20
+
21
+ For example, in your `application.rb`:
22
+
23
+ ```ruby
24
+ class AwesomeStrategy
25
+ attr_accessor :html
26
+
27
+ def initialize(html)
28
+ @html = html
29
+ end
30
+
31
+ def convert!
32
+ @html.awesomize!
33
+ end
34
+ end
35
+
36
+ config.plain_david.strategy = AwesomeStrategy
37
+ ```
38
+
39
+ ## Contributing
40
+
41
+ * Fork the project.
42
+ * Make your feature addition or bug fix.
43
+ * Send me a pull request. Bonus points for topic branches.
44
+
45
+ ## License
46
+
47
+ MIT License. See `LICENSE` for details.
48
+
49
+ ## Copyright
50
+
51
+ Copyright (c) 2013 Luca Spiller.
@@ -0,0 +1 @@
1
+ require 'plain_david'
@@ -0,0 +1,52 @@
1
+ module PlainDavid
2
+ module ActionMailerExtensions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ if method_defined?(:collect_responses)
7
+ alias_method_chain :collect_responses, :text_part
8
+ else
9
+ alias_method_chain :collect_responses_and_parts_order, :text_part
10
+ end
11
+ end
12
+
13
+ protected
14
+
15
+ # Rails 4
16
+ def collect_responses_with_text_part(headers, &block)
17
+ responses = collect_responses_without_text_part(headers, &block)
18
+
19
+ html_part, text_part = detect_parts(responses)
20
+ if html_part && !text_part
21
+ text_body = generate_text_body(html_part[:body])
22
+ responses.insert 0, { content_type: "text/plain", body: text_body }
23
+ end
24
+
25
+ responses
26
+ end
27
+
28
+ # Rails 3
29
+ def collect_responses_and_parts_order_with_text_part(headers, &block)
30
+ responses, order = collect_responses_and_parts_order_without_text_part(headers, &block)
31
+
32
+ html_part, text_part = detect_parts(responses)
33
+ if html_part && !text_part
34
+ text_body = generate_text_body(html_part[:body])
35
+ responses.insert 0, { content_type: "text/plain", body: text_body }
36
+ order && order.insert(0, "text/plain")
37
+ end
38
+
39
+ [responses, order]
40
+ end
41
+
42
+ def detect_parts(responses)
43
+ html_part = responses.detect { |response| response[:content_type] == "text/html" }
44
+ text_part = responses.detect { |response| response[:content_type] == "text/plain" }
45
+ return html_part, text_part
46
+ end
47
+
48
+ def generate_text_body(html)
49
+ PlainDavid.current_strategy.new(html.to_str).convert!
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,16 @@
1
+ require 'action_mailer'
2
+ require 'plain_david'
3
+ require 'plain_david/action_mailer_extensions'
4
+
5
+ module PlainDavid
6
+ class Railtie < Rails::Railtie
7
+ config.plain_david = ActiveSupport::OrderedOptions.new
8
+ config.plain_david.strategy = PlainDavid::Strategies::MarkdownStrategy
9
+
10
+ initializer "plain_david.extend_action_mailer" do
11
+ ActiveSupport.on_load(:action_mailer) do
12
+ include PlainDavid::ActionMailerExtensions
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'html2markdown'
2
+
3
+ module PlainDavid
4
+ module Strategies
5
+ class MarkdownStrategy
6
+ attr_accessor :html
7
+
8
+ def initialize(html)
9
+ @html = html
10
+ end
11
+
12
+ def convert!
13
+ HTMLPage.new(:contents => html).markdown!
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,127 @@
1
+ # coding: utf-8
2
+ require 'htmlentities'
3
+
4
+ # Taken from premailer
5
+
6
+ # Support functions for Premailer
7
+ module PlainDavid
8
+ module Strategies
9
+ class PlainStrategy
10
+ attr_accessor :html
11
+
12
+ def initialize(html)
13
+ @html = html
14
+ end
15
+
16
+ def convert!
17
+ convert_to_text(html)
18
+ end
19
+
20
+ private
21
+ # Returns the text in UTF-8 format with all HTML tags removed
22
+ #
23
+ # TODO: add support for DL, OL
24
+ def convert_to_text(html, line_length = 65, from_charset = 'UTF-8')
25
+ txt = html
26
+
27
+ # decode HTML entities
28
+ he = HTMLEntities.new
29
+ txt = he.decode(txt)
30
+
31
+ # remove the head tag
32
+ txt.gsub!(/<head>.+?<\/head>/mi, "")
33
+ # remove style tags
34
+ txt.gsub!(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/mi, "")
35
+ # remove script tags
36
+ txt.gsub!(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/mi, "")
37
+
38
+ # replace image by their alt attribute
39
+ txt.gsub!(/<img.+?alt=\"([^\"]*)\"[^>]*\/>/i, '\1')
40
+
41
+ # replace image by their alt attribute
42
+ txt.gsub!(/<img.+?alt=\"([^\"]*)\"[^>]*\/>/i, '\1')
43
+ txt.gsub!(/<img.+?alt='([^\']*)\'[^>]*\/>/i, '\1')
44
+
45
+ # links
46
+ txt.gsub!(/<a.+?href=\"([^\"]*)\"[^>]*>(.+?)<\/a>/i) do |s|
47
+ $2.strip + ' ( ' + $1.strip + ' )'
48
+ end
49
+
50
+ txt.gsub!(/<a.+?href='([^\']*)\'[^>]*>(.+?)<\/a>/i) do |s|
51
+ $2.strip + ' ( ' + $1.strip + ' )'
52
+ end
53
+
54
+ # handle headings (H1-H6)
55
+ txt.gsub!(/(<\/h[1-6]>)/i, "\n\\1") # move closing tags to new lines
56
+ txt.gsub!(/[\s]*<h([1-6]+)[^>]*>[\s]*(.*)[\s]*<\/h[1-6]+>/i) do |s|
57
+ hlevel = $1.to_i
58
+
59
+ htext = $2
60
+ htext.gsub!(/<br[\s]*\/?>/i, "\n") # handle <br>s
61
+ htext.gsub!(/<\/?[^>]*>/i, '') # strip tags
62
+
63
+ # determine maximum line length
64
+ hlength = 0
65
+ htext.each_line { |l| llength = l.strip.length; hlength = llength if llength > hlength }
66
+ hlength = line_length if hlength > line_length
67
+
68
+ case hlevel
69
+ when 1 # H1, asterisks above and below
70
+ htext = ('*' * hlength) + "\n" + htext + "\n" + ('*' * hlength)
71
+ when 2 # H1, dashes above and below
72
+ htext = ('-' * hlength) + "\n" + htext + "\n" + ('-' * hlength)
73
+ else # H3-H6, dashes below
74
+ htext = htext + "\n" + ('-' * hlength)
75
+ end
76
+
77
+ "\n\n" + htext + "\n\n"
78
+ end
79
+
80
+ # wrap spans
81
+ txt.gsub!(/(<\/span>)[\s]+(<span)/mi, '\1 \2')
82
+
83
+ # lists -- TODO: should handle ordered lists
84
+ txt.gsub!(/[\s]*(<li[^>]*>)[\s]*/i, '* ')
85
+ # list not followed by a newline
86
+ txt.gsub!(/<\/li>[\s]*(?![\n])/i, "\n")
87
+
88
+ # paragraphs and line breaks
89
+ txt.gsub!(/<\/p>/i, "\n\n")
90
+ txt.gsub!(/<br[\/ ]*>/i, "\n")
91
+
92
+ # strip remaining tags
93
+ txt.gsub!(/<\/?[^>]*>/, '')
94
+
95
+ txt = word_wrap(txt, line_length)
96
+
97
+ # remove linefeeds (\r\n and \r -> \n)
98
+ txt.gsub!(/\r\n?/, "\n")
99
+
100
+ # strip extra spaces
101
+ txt.gsub!(/\302\240+/, " ") # non-breaking spaces -> spaces
102
+ txt.gsub!(/\n[ \t]+/, "\n") # space at start of lines
103
+ txt.gsub!(/[ \t]+\n/, "\n") # space at end of lines
104
+
105
+ # no more than two consecutive newlines
106
+ txt.gsub!(/[\n]{3,}/, "\n\n")
107
+
108
+ # no more than two consecutive spaces
109
+ txt.gsub!(/ {2,}/, " ")
110
+
111
+ # the word messes up the parens
112
+ txt.gsub!(/\([ \n](http[^)]+)[\n ]\)/) do |s|
113
+ "( " + $1 + " )"
114
+ end
115
+
116
+ txt.strip
117
+ end
118
+
119
+ # Taken from Rails' word_wrap helper (http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-word_wrap)
120
+ def word_wrap(txt, line_length)
121
+ txt.split("\n").collect do |line|
122
+ line.length > line_length ? line.gsub(/(.{1,#{line_length}})(\s+|$)/, "\\1\n").strip : line
123
+ end * "\n"
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,3 @@
1
+ module PlainDavid
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ require 'plain_david/version'
2
+ require 'plain_david/strategies/markdown_strategy'
3
+ require 'plain_david/strategies/plain_strategy'
4
+ require 'plain_david/railtie' if defined?(Rails)
5
+
6
+ module PlainDavid
7
+ class << self
8
+ def current_strategy
9
+ return config.strategy if config.strategy
10
+ Strategies::MarkdownStrategy
11
+ end
12
+
13
+ private
14
+
15
+ def config
16
+ Rails.application.config.plain_david
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'plain_david/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "plain-david"
8
+ gem.version = PlainDavid::VERSION
9
+ gem.authors = ["Luca Spiller"]
10
+ gem.email = ["luca@stackednotion.com"]
11
+ gem.description = %q{Auto email plain text part generator}
12
+ gem.summary = %q{Auto email plain text part generator}
13
+ gem.homepage = ""
14
+
15
+ gem.add_dependency 'html2markdown'
16
+ gem.add_dependency 'htmlentities'
17
+
18
+ gem.files = `git ls-files`.split($/)
19
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
+ gem.require_paths = ["lib"]
22
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: plain-david
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Luca Spiller
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: html2markdown
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: htmlentities
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Auto email plain text part generator
42
+ email:
43
+ - luca@stackednotion.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE
49
+ - README.md
50
+ - lib/plain-david.rb
51
+ - lib/plain_david.rb
52
+ - lib/plain_david/action_mailer_extensions.rb
53
+ - lib/plain_david/railtie.rb
54
+ - lib/plain_david/strategies/markdown_strategy.rb
55
+ - lib/plain_david/strategies/plain_strategy.rb
56
+ - lib/plain_david/version.rb
57
+ - plain-david.gemspec
58
+ homepage: ''
59
+ licenses: []
60
+ metadata: {}
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 2.0.7
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: Auto email plain text part generator
81
+ test_files: []