letter_opener_web 0.0.4 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +26 -13
- data/app/assets/stylesheets/letter_opener_web/application.css.erb +29 -5
- data/app/models/letter_opener_web/letter.rb +20 -6
- data/app/views/letter_opener_web/letters/index.html.erb +43 -48
- data/lib/letter_opener_web/delivery_method.rb +8 -0
- data/lib/letter_opener_web/engine.rb +5 -0
- data/lib/letter_opener_web/version.rb +1 -1
- data/spec/models/letter_opener_web/letter_spec.rb +14 -2
- metadata +7 -10
- data/test-app/server +0 -17
data/README.md
CHANGED
@@ -5,17 +5,9 @@ browsing sent emails.
|
|
5
5
|
|
6
6
|
## Installation
|
7
7
|
|
8
|
-
|
8
|
+
First add the gem to your development environment and run the `bundle` command to install it.
|
9
9
|
|
10
|
-
gem 'letter_opener_web'
|
11
|
-
|
12
|
-
And then execute:
|
13
|
-
|
14
|
-
$ bundle
|
15
|
-
|
16
|
-
Or install it yourself as:
|
17
|
-
|
18
|
-
$ gem install letter_opener_web
|
10
|
+
gem 'letter_opener_web', :group => :development
|
19
11
|
|
20
12
|
## Usage
|
21
13
|
|
@@ -29,10 +21,31 @@ Your::Application.routes.draw do
|
|
29
21
|
end
|
30
22
|
```
|
31
23
|
|
32
|
-
|
24
|
+
If you are using [Vagrant](http://vagrantup.com), you might want to skip
|
25
|
+
`letter_opener`'s `launchy` calls and avoid messages like these:
|
26
|
+
|
27
|
+
```terminal
|
28
|
+
12:33:42 web.1 | Failure in opening /vagrant/tmp/letter_opener/1358825621_ba83a22/rich.html
|
29
|
+
with options {}: Unable to find a browser command. If this is unexpected, Please rerun with
|
30
|
+
environment variable LAUNCHY_DEBUG=true or the '-d' commandline option and file a bug at
|
31
|
+
https://github.com/copiousfreetime/launchy/issues/new
|
32
|
+
```
|
33
|
+
|
34
|
+
In that case (or if you just want to browse mails using the web interface), you
|
35
|
+
can set `:letter_opener_web` as your delivery method on your
|
36
|
+
`config/environments/development.rb`:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
config.action_mailer.delivery_method = :letter_opener_web
|
40
|
+
|
41
|
+
# If not everyone on the team is using vagrant
|
42
|
+
config.action_mailer.delivery_method = ENV['USER'] == 'vagrant' ? :letter_opener_web : :letter_opener
|
43
|
+
```
|
44
|
+
|
45
|
+
## Acknowledgements
|
33
46
|
|
34
|
-
|
35
|
-
|
47
|
+
Special thanks to [@alexrothenberg](https://github.com/alexrothenberg) for some
|
48
|
+
ideas on [this pull request](https://github.com/ryanb/letter_opener/pull/12).
|
36
49
|
|
37
50
|
## Contributing
|
38
51
|
|
@@ -4,14 +4,38 @@
|
|
4
4
|
*= require_tree .
|
5
5
|
*/
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
body {
|
8
|
+
margin: 0
|
9
|
+
}
|
10
|
+
|
11
|
+
.col {
|
12
|
+
top: 0;
|
13
|
+
bottom: 0;
|
14
|
+
}
|
15
|
+
|
16
|
+
.left {
|
17
|
+
overflow-y: auto;
|
18
|
+
width: 400px;
|
19
|
+
position: absolute;
|
20
|
+
}
|
21
|
+
|
22
|
+
.right {
|
23
|
+
left: 400px;
|
24
|
+
right: 0;
|
25
|
+
position: fixed;
|
9
26
|
}
|
10
27
|
|
11
28
|
iframe {
|
12
|
-
width:
|
13
|
-
|
14
|
-
|
29
|
+
width: 100%;
|
30
|
+
height: 100%;
|
31
|
+
position: absolute;
|
32
|
+
border: solid 1px #ccc;
|
33
|
+
border-top: none;
|
34
|
+
}
|
35
|
+
|
36
|
+
h1 {
|
37
|
+
padding-right: 10px;
|
38
|
+
padding-left: 5px;
|
15
39
|
}
|
16
40
|
|
17
41
|
.letter-opener .active,
|
@@ -27,11 +27,11 @@ module LetterOpenerWeb
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def plain_text
|
30
|
-
@plain_text ||= read_file(:plain)
|
30
|
+
@plain_text ||= adjust_link_targets read_file(:plain)
|
31
31
|
end
|
32
32
|
|
33
33
|
def rich_text
|
34
|
-
@rich_text ||= read_file(:rich)
|
34
|
+
@rich_text ||= adjust_link_targets read_file(:rich)
|
35
35
|
end
|
36
36
|
|
37
37
|
def to_param
|
@@ -41,21 +41,35 @@ module LetterOpenerWeb
|
|
41
41
|
private
|
42
42
|
|
43
43
|
def read_file(style)
|
44
|
-
|
44
|
+
File.read("#{letters_location}/#{id}/#{style}.html")
|
45
|
+
end
|
45
46
|
|
47
|
+
def adjust_link_targets(contents)
|
46
48
|
# We cannot feed the whole file to an XML parser as some mails are
|
47
49
|
# "complete" (as in they have the whole <html> structure) and letter_opener
|
48
50
|
# prepends some information about the mail being sent, making REXML
|
49
51
|
# complain about it
|
50
|
-
contents.scan(/<a[^>]
|
51
|
-
|
52
|
+
contents.scan(/<a[^>]+>(?:.|\s)*?<\/a>/).each do |link|
|
53
|
+
fixed_link = fix_link_html(link)
|
54
|
+
xml = REXML::Document.new(fixed_link).root
|
52
55
|
unless xml.attributes['href'] =~ /(plain|rich).html/
|
53
56
|
xml.attributes['target'] = '_blank'
|
54
57
|
contents.gsub!(link, xml.to_s)
|
55
58
|
end
|
56
59
|
end
|
57
|
-
|
58
60
|
contents
|
59
61
|
end
|
62
|
+
|
63
|
+
def fix_link_html(link_html)
|
64
|
+
# REFACTOR: we need a better way of fixing the link inner html
|
65
|
+
link_html.dup.tap do |fixed_link|
|
66
|
+
fixed_link.gsub!('<br>', '<br/>')
|
67
|
+
fixed_link.scan(/<img(?:[^>]+?)>/).each do |img|
|
68
|
+
fixed_img = img.dup
|
69
|
+
fixed_img.gsub!(/>$/, '/>') unless img =~ /\/>$/
|
70
|
+
fixed_link.gsub!(img, fixed_img)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
60
74
|
end
|
61
75
|
end
|
@@ -1,49 +1,44 @@
|
|
1
|
-
<div class="
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
<
|
18
|
-
<
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
<
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
<div class="span7">
|
45
|
-
<h1>Content</h1>
|
46
|
-
<iframe name="mail" id="mail" src="<%= first_letter.present? ? letter_path(first_letter, style: 'rich') : 'about:blank' %>"></iframe>
|
47
|
-
</div>
|
48
|
-
</div>
|
1
|
+
<div class="col left">
|
2
|
+
<h1>
|
3
|
+
Letters
|
4
|
+
<span class="pull-right">
|
5
|
+
<%= link_to letters_path, class: 'btn refresh' do %>
|
6
|
+
<i class="icon-refresh"></i>
|
7
|
+
Refresh
|
8
|
+
<% end %>
|
9
|
+
<%= link_to clear_letters_path, method: 'delete', confirm: 'Are you sure?', class: 'btn btn-danger' do %>
|
10
|
+
<i class="icon-trash icon-white"></i>
|
11
|
+
Clear
|
12
|
+
<% end %>
|
13
|
+
</span>
|
14
|
+
</h1>
|
15
|
+
<table class="table table-hover letter-opener" data-letters-path="<%= letters_path %>">
|
16
|
+
<thead>
|
17
|
+
<tr>
|
18
|
+
<th>ID</th>
|
19
|
+
<th>Sent at</th>
|
20
|
+
</tr>
|
21
|
+
</thead>
|
22
|
+
<tbody>
|
23
|
+
<% if first_letter = @letters.shift %>
|
24
|
+
<tr class="active">
|
25
|
+
<td>
|
26
|
+
<%= link_to(first_letter.id, letter_path(first_letter, style: 'rich'), target: 'mail') %>
|
27
|
+
</td>
|
28
|
+
<td><%= first_letter.sent_at %></td>
|
29
|
+
</tr>
|
30
|
+
<% end %>
|
31
|
+
<% @letters.each do |letter| %>
|
32
|
+
<tr>
|
33
|
+
<td>
|
34
|
+
<%= link_to(letter.id, letter_path(letter, style: 'rich'), target: 'mail') %>
|
35
|
+
</td>
|
36
|
+
<td><%= letter.sent_at %></td>
|
37
|
+
</tr>
|
38
|
+
<% end %>
|
39
|
+
</tbody>
|
40
|
+
</table>
|
41
|
+
</div>
|
42
|
+
<div class="col right">
|
43
|
+
<iframe name="mail" id="mail" src="<%= first_letter.present? ? letter_path(first_letter, style: 'rich') : 'about:blank' %>"></iframe>
|
49
44
|
</div>
|
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'letter_opener'
|
2
|
+
require 'letter_opener_web/delivery_method'
|
2
3
|
|
3
4
|
module LetterOpenerWeb
|
4
5
|
class Engine < ::Rails::Engine
|
5
6
|
isolate_namespace LetterOpenerWeb
|
7
|
+
|
8
|
+
initializer "letter_opener_web.add_delivery_method" do
|
9
|
+
ActionMailer::Base.add_delivery_method :letter_opener_web, LetterOpenerWeb::DeliveryMethod, :location => Rails.root.join("tmp", "letter_opener")
|
10
|
+
end
|
6
11
|
end
|
7
12
|
end
|
@@ -3,6 +3,18 @@ require 'spec_helper'
|
|
3
3
|
describe LetterOpenerWeb::Letter do
|
4
4
|
let(:location) { File.expand_path('../../../tmp', __FILE__) }
|
5
5
|
|
6
|
+
def rich_text(mail_id)
|
7
|
+
<<-MAIL
|
8
|
+
Rich text for #{mail_id}
|
9
|
+
<!DOCTYPE html>
|
10
|
+
<a href='a-link.html'>
|
11
|
+
<img src='an-image.jpg'>
|
12
|
+
Link text
|
13
|
+
</a>
|
14
|
+
<a href='fooo.html'>Bar</a>
|
15
|
+
MAIL
|
16
|
+
end
|
17
|
+
|
6
18
|
before :each do
|
7
19
|
described_class.stub(:letters_location).and_return(location)
|
8
20
|
described_class.any_instance.stub(:letters_location).and_return(location)
|
@@ -10,7 +22,7 @@ describe LetterOpenerWeb::Letter do
|
|
10
22
|
['1111_1111', '2222_2222'].each do |folder|
|
11
23
|
FileUtils.mkdir_p("#{location}/#{folder}")
|
12
24
|
File.open("#{location}/#{folder}/plain.html", 'w') {|f| f.write("Plain text for #{folder}") }
|
13
|
-
File.open("#{location}/#{folder}/rich.html", 'w') {|f| f.write(
|
25
|
+
File.open("#{location}/#{folder}/rich.html", 'w') {|f| f.write(rich_text(folder)) }
|
14
26
|
FileUtils.mkdir_p("#{Rails.root.join('tmp', 'letter_opener')}/#{folder}")
|
15
27
|
File.open("#{Rails.root.join('tmp', 'letter_opener')}/#{folder}/rich.html", 'w') {|f| f.write("Rich text for #{folder}") }
|
16
28
|
end
|
@@ -27,7 +39,7 @@ describe LetterOpenerWeb::Letter do
|
|
27
39
|
it { should =~ /Rich text for 1111_1111/ }
|
28
40
|
|
29
41
|
it 'changes links to show up on a new window' do
|
30
|
-
subject.should include("<a href='a-link.html' target='_blank'
|
42
|
+
subject.should include("<a href='a-link.html' target='_blank'>\n <img src='an-image.jpg'/>\n Link text\n</a>")
|
31
43
|
end
|
32
44
|
end
|
33
45
|
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: letter_opener_web
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0.rc1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Fabio Rehm
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
prerelease: false
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- config/routes.rb
|
117
117
|
- letter_opener_web.gemspec
|
118
118
|
- lib/letter_opener_web.rb
|
119
|
+
- lib/letter_opener_web/delivery_method.rb
|
119
120
|
- lib/letter_opener_web/engine.rb
|
120
121
|
- lib/letter_opener_web/version.rb
|
121
122
|
- lib/tasks/letter_opener_web_tasks.rake
|
@@ -132,7 +133,6 @@ files:
|
|
132
133
|
- test-app/Gemfile
|
133
134
|
- test-app/boot.rb
|
134
135
|
- test-app/config.ru
|
135
|
-
- test-app/server
|
136
136
|
- vendor/assets/images/glyphicons-halflings-white.png
|
137
137
|
- vendor/assets/images/glyphicons-halflings.png
|
138
138
|
- vendor/assets/javascripts/bootstrap.min.js
|
@@ -151,16 +151,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
151
151
|
version: '0'
|
152
152
|
segments:
|
153
153
|
- 0
|
154
|
-
hash:
|
154
|
+
hash: 1643091472553674558
|
155
155
|
none: false
|
156
156
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
157
|
requirements:
|
158
|
-
- - ! '
|
158
|
+
- - ! '>'
|
159
159
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
161
|
-
segments:
|
162
|
-
- 0
|
163
|
-
hash: -2526827428005844609
|
160
|
+
version: 1.3.1
|
164
161
|
none: false
|
165
162
|
requirements: []
|
166
163
|
rubyforge_project:
|
data/test-app/server
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require './boot'
|
4
|
-
|
5
|
-
options = {
|
6
|
-
:environment => nil,
|
7
|
-
:pid => nil,
|
8
|
-
:Port => 3000,
|
9
|
-
:Host => "0.0.0.0",
|
10
|
-
:AccessLog => [],
|
11
|
-
:app => TinyRailsApp,
|
12
|
-
|
13
|
-
# TODO:
|
14
|
-
# :server => 'thin'
|
15
|
-
}
|
16
|
-
|
17
|
-
Rack::Server.start options
|