mailcatcher 0.5.6 → 0.5.7

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.md CHANGED
@@ -20,6 +20,7 @@ MailCatcher runs a super simple SMTP server which catches any message sent to it
20
20
  * Runs as a daemon run in the background.
21
21
  * Sendmail-analogue command, `catchmail`, makes [using mailcatcher from PHP][withphp] a lot easier.
22
22
  * Written super-simply in EventMachine, easy to dig in and change.
23
+ * Keyboard navigation between messages
23
24
 
24
25
  ## How
25
26
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.6
1
+ 0.5.7
@@ -15,6 +15,10 @@ module MailCatcher
15
15
 
16
16
  module_function
17
17
 
18
+ def which command
19
+ IO.popen(["which", command], "r", :err => :close).read.chomp.presence
20
+ end
21
+
18
22
  def mac?
19
23
  RbConfig::CONFIG['host_os'] =~ /darwin/
20
24
  end
@@ -28,17 +32,23 @@ module_function
28
32
  end
29
33
 
30
34
  def growlnotify?
31
- system "which", "-s", "growlnotify"
35
+ which "growlnotify"
32
36
  end
33
37
 
34
- def growlframework?
35
- macruby? and
36
- # TODO: Look for growl framework accessible
37
- false
38
+ def growl?
39
+ growlnotify?
38
40
  end
39
41
 
40
- def growl?
41
- growlnotify? or growlframework?
42
+ def browse?
43
+ windows? or which "open"
44
+ end
45
+
46
+ def browse url
47
+ if windows?
48
+ system "start", "/b", url
49
+ elsif which "open"
50
+ system "open", url
51
+ end
42
52
  end
43
53
 
44
54
  @@defaults = {
@@ -49,6 +59,7 @@ module_function
49
59
  :verbose => false,
50
60
  :daemon => !windows?,
51
61
  :growl => growlnotify?,
62
+ :browse => false,
52
63
  }
53
64
 
54
65
  def parse! arguments=ARGV, defaults=@@defaults
@@ -96,6 +107,12 @@ module_function
96
107
  end
97
108
  end
98
109
 
110
+ if browse?
111
+ parser.on('-b', '--browse', 'Open web browser') do
112
+ options[:browse] = true
113
+ end
114
+ end
115
+
99
116
  parser.on('-v', '--verbose', 'Be more verbose') do
100
117
  options[:verbose] = true
101
118
  end
@@ -123,19 +140,27 @@ module_function
123
140
  # Get our lion on if asked
124
141
  MailCatcher::Growl.start if options[:growl]
125
142
 
126
- # TODO: DRY this up
143
+ smtp_url = "smtp://#{options[:smtp_ip]}:#{options[:smtp_port]}"
144
+ http_url = "http://#{options[:http_ip]}:#{options[:http_port]}"
127
145
 
128
146
  # Set up an SMTP server to run within EventMachine
129
147
  rescue_port options[:smtp_port] do
130
148
  EventMachine.start_server options[:smtp_ip], options[:smtp_port], Smtp
131
- puts "==> smtp://#{options[:smtp_ip]}:#{options[:smtp_port]}"
149
+ puts "==> #{smtp_url}"
132
150
  end
133
151
 
134
152
  # Let Thin set itself up inside our EventMachine loop
135
153
  # (Skinny/WebSockets just works on the inside)
136
154
  rescue_port options[:http_port] do
137
155
  Thin::Server.start options[:http_ip], options[:http_port], Web
138
- puts "==> http://#{options[:http_ip]}:#{options[:http_port]}"
156
+ puts "==> #{http_url}"
157
+ end
158
+
159
+ # Open the web browser before detatching console
160
+ if options[:browse]
161
+ EventMachine.next_tick do
162
+ browse http_url
163
+ end
139
164
  end
140
165
 
141
166
  # Daemonize, if we should, but only after the servers have started.
@@ -1,6 +1,17 @@
1
1
  require 'eventmachine'
2
2
 
3
3
  class MailCatcher::Smtp < EventMachine::Protocols::SmtpServer
4
+ # We override EM's mail from processing to allow multiple mail-from commands
5
+ # per [RFC 2821](http://tools.ietf.org/html/rfc2821#section-4.1.1.2)
6
+ def process_mail_from sender
7
+ if @state.include? :mail_from
8
+ @state -= [:mail_from, :rcpt, :data]
9
+ receive_reset
10
+ end
11
+
12
+ super
13
+ end
14
+
4
15
  def current_message
5
16
  @current_message ||= {}
6
17
  end
@@ -1,36 +1,37 @@
1
1
  (function() {
2
2
  var MailCatcher;
3
-
3
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
4
4
  jQuery.expr[':'].icontains = function(a, i, m) {
5
5
  var _ref, _ref2;
6
6
  return ((_ref = (_ref2 = a.textContent) != null ? _ref2 : a.innerText) != null ? _ref : "").toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
7
7
  };
8
-
9
8
  MailCatcher = (function() {
10
-
11
9
  function MailCatcher() {
12
- var _this = this;
13
- $('#messages tr').live('click', function(e) {
10
+ this.nextTab = __bind(this.nextTab, this);;
11
+ this.previousTab = __bind(this.previousTab, this);;
12
+ this.openTab = __bind(this.openTab, this);;
13
+ this.selectedTab = __bind(this.selectedTab, this);;
14
+ this.getTab = __bind(this.getTab, this);; $('#messages tr').live('click', __bind(function(e) {
14
15
  e.preventDefault();
15
- return _this.loadMessage($(e.currentTarget).attr('data-message-id'));
16
- });
17
- $('input[name=search]').keyup(function(e) {
16
+ return this.loadMessage($(e.currentTarget).attr('data-message-id'));
17
+ }, this));
18
+ $('input[name=search]').keyup(__bind(function(e) {
18
19
  var query;
19
20
  query = $.trim($(e.currentTarget).val());
20
21
  if (query) {
21
- return _this.searchMessages(query);
22
+ return this.searchMessages(query);
22
23
  } else {
23
- return _this.clearSearch();
24
+ return this.clearSearch();
24
25
  }
25
- });
26
- $('#message .views .format.tab a').live('click', function(e) {
26
+ }, this));
27
+ $('#message .views .format.tab a').live('click', __bind(function(e) {
27
28
  e.preventDefault();
28
- return _this.loadMessageBody(_this.selectedMessage(), $($(e.currentTarget).parent('li')).data('message-format'));
29
- });
30
- $('#message .views .analysis.tab a').live('click', function(e) {
29
+ return this.loadMessageBody(this.selectedMessage(), $($(e.currentTarget).parent('li')).data('message-format'));
30
+ }, this));
31
+ $('#message .views .analysis.tab a').live('click', __bind(function(e) {
31
32
  e.preventDefault();
32
- return _this.loadMessageAnalysis(_this.selectedMessage());
33
- });
33
+ return this.loadMessageAnalysis(this.selectedMessage());
34
+ }, this));
34
35
  $('#resizer').live({
35
36
  mousedown: function(e) {
36
37
  var events;
@@ -49,7 +50,7 @@
49
50
  });
50
51
  }
51
52
  });
52
- $('nav.app .clear a').live('click', function(e) {
53
+ $('nav.app .clear a').live('click', __bind(function(e) {
53
54
  e.preventDefault();
54
55
  if (confirm("You will lose all your received messages.\n\nAre you sure you want to clear all messages?")) {
55
56
  return $.ajax({
@@ -65,8 +66,8 @@
65
66
  }
66
67
  });
67
68
  }
68
- });
69
- $('nav.app .quit a').live('click', function(e) {
69
+ }, this));
70
+ $('nav.app .quit a').live('click', __bind(function(e) {
70
71
  e.preventDefault();
71
72
  if (confirm("You will lose all your received messages.\n\nAre you sure you want to quit?")) {
72
73
  return $.ajax({
@@ -79,42 +80,106 @@
79
80
  }
80
81
  });
81
82
  }
82
- });
83
+ }, this));
84
+ key('up', __bind(function() {
85
+ var id;
86
+ id = this.selectedMessage() || 1;
87
+ if (id > 1) {
88
+ id -= 1;
89
+ }
90
+ return this.loadMessage(id);
91
+ }, this));
92
+ key('down', __bind(function() {
93
+ var id;
94
+ id = this.selectedMessage() || this.messagesCount();
95
+ if (id < this.messagesCount()) {
96
+ id += 1;
97
+ }
98
+ return this.loadMessage(id);
99
+ }, this));
100
+ key('⌘+up, ctrl+up', __bind(function() {
101
+ return this.loadMessage(1);
102
+ }, this));
103
+ key('⌘+down, ctrl+down', __bind(function() {
104
+ return this.loadMessage(this.messagesCount());
105
+ }, this));
106
+ key('left', __bind(function() {
107
+ return this.openTab(this.previousTab());
108
+ }, this));
109
+ key('right', __bind(function() {
110
+ return this.openTab(this.nextTab());
111
+ }, this));
83
112
  this.refresh();
84
113
  this.subscribe();
85
114
  }
86
-
87
115
  MailCatcher.prototype.parseDateRegexp = /^(\d{4})[-\/\\](\d{2})[-\/\\](\d{2})(?:\s+|T)(\d{2})[:-](\d{2})[:-](\d{2})(?:([ +-]\d{2}:\d{2}|\s*\S+|Z?))?$/;
88
-
89
116
  MailCatcher.prototype.parseDate = function(date) {
90
117
  var match;
91
118
  if (match = this.parseDateRegexp.exec(date)) {
92
119
  return new Date(match[1], match[2] - 1, match[3], match[4], match[5], match[6], 0);
93
120
  }
94
121
  };
95
-
96
122
  MailCatcher.prototype.offsetTimeZone = function(date) {
97
123
  var offset;
98
124
  offset = Date.now().getTimezoneOffset() * 60000;
99
125
  date.setTime(date.getTime() - offset);
100
126
  return date;
101
127
  };
102
-
103
128
  MailCatcher.prototype.formatDate = function(date) {
104
- if (typeof date === "string") date && (date = this.parseDate(date));
129
+ if (typeof date === "string") {
130
+ date && (date = this.parseDate(date));
131
+ }
105
132
  date && (date = this.offsetTimeZone(date));
106
133
  return date && (date = date.toString("dddd, d MMM yyyy h:mm:ss tt"));
107
134
  };
108
-
135
+ MailCatcher.prototype.messagesCount = function() {
136
+ return $('#messages tr').length - 1;
137
+ };
138
+ MailCatcher.prototype.tabs = function() {
139
+ return $('#message ul').children('.tab');
140
+ };
141
+ MailCatcher.prototype.getTab = function(i) {
142
+ return $(this.tabs()[i]);
143
+ };
144
+ MailCatcher.prototype.selectedTab = function() {
145
+ return this.tabs().index($('#message li.tab.selected'));
146
+ };
147
+ MailCatcher.prototype.openTab = function(i) {
148
+ return this.getTab(i).children('a').click();
149
+ };
150
+ MailCatcher.prototype.previousTab = function(tab) {
151
+ var i;
152
+ i = tab || tab === 0 ? tab : this.selectedTab() - 1;
153
+ if (i < 0) {
154
+ i = this.tabs().length - 1;
155
+ }
156
+ if (this.getTab(i).is(":visible")) {
157
+ return i;
158
+ } else {
159
+ return this.previousTab(i - 1);
160
+ }
161
+ };
162
+ MailCatcher.prototype.nextTab = function(tab) {
163
+ var i;
164
+ i = tab ? tab : this.selectedTab() + 1;
165
+ if (i > this.tabs().length - 1) {
166
+ i = 0;
167
+ }
168
+ if (this.getTab(i).is(":visible")) {
169
+ return i;
170
+ } else {
171
+ return this.nextTab(i + 1);
172
+ }
173
+ };
109
174
  MailCatcher.prototype.haveMessage = function(message) {
110
- if (message.id != null) message = message.id;
175
+ if (message.id != null) {
176
+ message = message.id;
177
+ }
111
178
  return $("#messages tbody tr[data-message-id=\"" + message + "\"]").length > 0;
112
179
  };
113
-
114
180
  MailCatcher.prototype.selectedMessage = function() {
115
181
  return $('#messages tr.selected').data('message-id');
116
182
  };
117
-
118
183
  MailCatcher.prototype.searchMessages = function(query) {
119
184
  var $rows, selector, token;
120
185
  selector = ((function() {
@@ -131,25 +196,23 @@
131
196
  $rows.not(selector).hide();
132
197
  return $rows.filter(selector).show();
133
198
  };
134
-
135
199
  MailCatcher.prototype.clearSearch = function() {
136
200
  return $('#messages tbody tr').show();
137
201
  };
138
-
139
202
  MailCatcher.prototype.addMessage = function(message) {
140
203
  return $('#messages tbody').append($('<tr />').attr('data-message-id', message.id.toString()).append($('<td/>').text(message.sender || "No sender").toggleClass("blank", !message.sender)).append($('<td/>').text((message.recipients || []).join(', ') || "No receipients").toggleClass("blank", !message.recipients.length)).append($('<td/>').text(message.subject || "No subject").toggleClass("blank", !message.subject)).append($('<td/>').text(this.formatDate(message.created_at))));
141
204
  };
142
-
143
205
  MailCatcher.prototype.loadMessage = function(id) {
144
- var _this = this;
145
- if ((id != null ? id.id : void 0) != null) id = id.id;
206
+ if ((id != null ? id.id : void 0) != null) {
207
+ id = id.id;
208
+ }
146
209
  id || (id = $('#messages tr.selected').attr('data-message-id'));
147
210
  if (id != null) {
148
211
  $('#messages tbody tr:not([data-message-id="' + id + '"])').removeClass('selected');
149
212
  $('#messages tbody tr[data-message-id="' + id + '"]').addClass('selected');
150
- return $.getJSON('/messages/' + id + '.json', function(message) {
213
+ return $.getJSON('/messages/' + id + '.json', __bind(function(message) {
151
214
  var $ul;
152
- $('#message .metadata dd.created_at').text(_this.formatDate(message.created_at));
215
+ $('#message .metadata dd.created_at').text(this.formatDate(message.created_at));
153
216
  $('#message .metadata dd.from').text(message.sender);
154
217
  $('#message .metadata dd.to').text((message.recipients || []).join(', '));
155
218
  $('#message .metadata dd.subject').text(message.subject);
@@ -179,14 +242,13 @@
179
242
  }
180
243
  $('#message .views .download a').attr('href', "/messages/" + id + ".eml");
181
244
  if ($('#message .views .tab.analysis.selected').length) {
182
- return _this.loadMessageAnalysis();
245
+ return this.loadMessageAnalysis();
183
246
  } else {
184
- return _this.loadMessageBody();
247
+ return this.loadMessageBody();
185
248
  }
186
- });
249
+ }, this));
187
250
  }
188
251
  };
189
-
190
252
  MailCatcher.prototype.loadMessageBody = function(id, format) {
191
253
  id || (id = this.selectedMessage());
192
254
  format || (format = $('#message .views .tab.format.selected').attr('data-message-format'));
@@ -197,7 +259,6 @@
197
259
  return $('#message iframe').attr("src", "/messages/" + id + "." + format);
198
260
  }
199
261
  };
200
-
201
262
  MailCatcher.prototype.loadMessageAnalysis = function(id) {
202
263
  var $form, $iframe;
203
264
  id || (id = this.selectedMessage());
@@ -212,49 +273,40 @@
212
273
  });
213
274
  }
214
275
  };
215
-
216
276
  MailCatcher.prototype.refresh = function() {
217
- var _this = this;
218
- return $.getJSON('/messages', function(messages) {
219
- return $.each(messages, function(i, message) {
220
- if (!_this.haveMessage(message)) return _this.addMessage(message);
221
- });
222
- });
277
+ return $.getJSON('/messages', __bind(function(messages) {
278
+ return $.each(messages, __bind(function(i, message) {
279
+ if (!this.haveMessage(message)) {
280
+ return this.addMessage(message);
281
+ }
282
+ }, this));
283
+ }, this));
223
284
  };
224
-
225
285
  MailCatcher.prototype.subscribe = function() {
226
- if (typeof WebSocket !== "undefined" && WebSocket !== null) {
286
+ if (typeof WebSocket != "undefined" && WebSocket !== null) {
227
287
  return this.subscribeWebSocket();
228
288
  } else {
229
289
  return this.subscribePoll();
230
290
  }
231
291
  };
232
-
233
292
  MailCatcher.prototype.subscribeWebSocket = function() {
234
- var secure,
235
- _this = this;
293
+ var secure;
236
294
  secure = window.location.scheme === 'https';
237
295
  this.websocket = new WebSocket("" + (secure ? 'wss' : 'ws') + "://" + window.location.host + "/messages");
238
- return this.websocket.onmessage = function(event) {
239
- return _this.addMessage($.parseJSON(event.data));
240
- };
296
+ return this.websocket.onmessage = __bind(function(event) {
297
+ return this.addMessage($.parseJSON(event.data));
298
+ }, this);
241
299
  };
242
-
243
300
  MailCatcher.prototype.subscribePoll = function() {
244
- var _this = this;
245
301
  if (this.refreshInterval == null) {
246
- return this.refreshInterval = setInterval((function() {
247
- return _this.refresh();
248
- }), 1000);
302
+ return this.refreshInterval = setInterval((__bind(function() {
303
+ return this.refresh();
304
+ }, this)), 1000);
249
305
  }
250
306
  };
251
-
252
307
  return MailCatcher;
253
-
254
308
  })();
255
-
256
309
  $(function() {
257
310
  return window.MailCatcher = new MailCatcher;
258
311
  });
259
-
260
312
  }).call(this);
@@ -0,0 +1,4 @@
1
+ // keymaster.js
2
+ // (c) 2011 Thomas Fuchs
3
+ // keymaster.js may be freely distributed under the MIT license.
4
+ (function(a){function h(a,b){var c=a.length;while(c--)if(a[c]===b)return c;return-1}function i(a){var b,g,i,j,k;b=a.keyCode;if(b==93||b==224)b=91;if(b in d){d[b]=!0;for(i in f)f[i]==b&&(l[i]=!0);return}if(!l.filter.call(this,a))return;if(!(b in c))return;for(j=0;j<c[b].length;j++){g=c[b][j];if(g.scope==e||g.scope=="all"){k=g.mods.length>0;for(i in d)if(!d[i]&&h(g.mods,+i)>-1||d[i]&&h(g.mods,+i)==-1)k=!1;(g.mods.length==0&&!d[16]&&!d[18]&&!d[17]&&!d[91]||k)&&g.method(a,g)===!1&&(a.preventDefault?a.preventDefault():a.returnValue=!1,a.stopPropagation&&a.stopPropagation(),a.cancelBubble&&(a.cancelBubble=!0))}}}function j(a){var b=a.keyCode,c;if(b==93||b==224)b=91;if(b in d){d[b]=!1;for(c in f)f[c]==b&&(l[c]=!1)}}function k(){for(b in d)d[b]=!1;for(b in f)l[b]=!1}function l(a,b,d){var e,h,i,j;d===undefined&&(d=b,b="all"),a=a.replace(/\s/g,""),e=a.split(","),e[e.length-1]==""&&(e[e.length-2]+=",");for(i=0;i<e.length;i++){h=[],a=e[i].split("+");if(a.length>1){h=a.slice(0,a.length-1);for(j=0;j<h.length;j++)h[j]=f[h[j]];a=[a[a.length-1]]}a=a[0],a=g[a]||a.toUpperCase().charCodeAt(0),a in c||(c[a]=[]),c[a].push({shortcut:e[i],scope:b,method:d,key:e[i],mods:h})}}function m(a){var b=(a.target||a.srcElement).tagName;return b!="INPUT"&&b!="SELECT"&&b!="TEXTAREA"}function n(a){e=a||"all"}function o(){return e||"all"}function p(a){var b,d,e;for(b in c){d=c[b];for(e=0;e<d.length;)d[e].scope===a?d.splice(e,1):e++}}function q(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,function(){c(window.event)})}var b,c={},d={16:!1,18:!1,17:!1,91:!1},e="all",f={"⇧":16,shift:16,"⌥":18,alt:18,option:18,"⌃":17,ctrl:17,control:17,"⌘":91,command:91},g={backspace:8,tab:9,clear:12,enter:13,"return":13,esc:27,escape:27,space:32,left:37,up:38,right:39,down:40,del:46,"delete":46,home:36,end:35,pageup:33,pagedown:34,",":188,".":190,"/":191,"`":192,"-":189,"=":187,";":186,"'":222,"[":219,"]":221,"\\":220};for(b=1;b<20;b++)f["f"+b]=111+b;for(b in f)l[b]=!1;q(document,"keydown",i),q(document,"keyup",j),q(window,"focus",k),a.key=l,a.key.setScope=n,a.key.getScope=o,a.key.deleteScope=p,a.key.filter=m,typeof module!="undefined"&&(module.exports=key)})(this);
@@ -8,6 +8,7 @@
8
8
  %script{:src => "/javascripts/xslt-3.2.js"}
9
9
  %script{:src => "/javascripts/date.js"}
10
10
  %script{:src => "/javascripts/flexie.min.js"}
11
+ %script{:src => "/javascripts/keymaster.min.js"}
11
12
  %script{:src => "/javascripts/application.js"}
12
13
  %body
13
14
  %header
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailcatcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.5.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-22 00:00:00.000000000 Z
12
+ date: 2012-06-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70178931095400 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70178931095400
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: eventmachine
27
- requirement: &70178931094740 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ~>
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0.12'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70178931094740
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '0.12'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: haml
38
- requirement: &70178931093560 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ~>
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '3.1'
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *70178931093560
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.1'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: mail
49
- requirement: &70178931092220 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ~>
@@ -54,10 +69,15 @@ dependencies:
54
69
  version: '2.3'
55
70
  type: :runtime
56
71
  prerelease: false
57
- version_requirements: *70178931092220
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '2.3'
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: sinatra
60
- requirement: &70178931091100 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ~>
@@ -65,10 +85,15 @@ dependencies:
65
85
  version: '1.2'
66
86
  type: :runtime
67
87
  prerelease: false
68
- version_requirements: *70178931091100
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.2'
69
94
  - !ruby/object:Gem::Dependency
70
95
  name: skinny
71
- requirement: &70178931089960 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
72
97
  none: false
73
98
  requirements:
74
99
  - - ~>
@@ -76,10 +101,15 @@ dependencies:
76
101
  version: '0.2'
77
102
  type: :runtime
78
103
  prerelease: false
79
- version_requirements: *70178931089960
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: '0.2'
80
110
  - !ruby/object:Gem::Dependency
81
111
  name: sqlite3
82
- requirement: &70178931089260 !ruby/object:Gem::Requirement
112
+ requirement: !ruby/object:Gem::Requirement
83
113
  none: false
84
114
  requirements:
85
115
  - - ~>
@@ -87,10 +117,15 @@ dependencies:
87
117
  version: '1.3'
88
118
  type: :runtime
89
119
  prerelease: false
90
- version_requirements: *70178931089260
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '1.3'
91
126
  - !ruby/object:Gem::Dependency
92
127
  name: thin
93
- requirement: &70178931088420 !ruby/object:Gem::Requirement
128
+ requirement: !ruby/object:Gem::Requirement
94
129
  none: false
95
130
  requirements:
96
131
  - - ~>
@@ -98,10 +133,15 @@ dependencies:
98
133
  version: '1.2'
99
134
  type: :runtime
100
135
  prerelease: false
101
- version_requirements: *70178931088420
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: '1.2'
102
142
  - !ruby/object:Gem::Dependency
103
143
  name: coffee-script
104
- requirement: &70178931087740 !ruby/object:Gem::Requirement
144
+ requirement: !ruby/object:Gem::Requirement
105
145
  none: false
106
146
  requirements:
107
147
  - - ~>
@@ -109,10 +149,15 @@ dependencies:
109
149
  version: '2.2'
110
150
  type: :development
111
151
  prerelease: false
112
- version_requirements: *70178931087740
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: '2.2'
113
158
  - !ruby/object:Gem::Dependency
114
159
  name: compass
115
- requirement: &70178931087000 !ruby/object:Gem::Requirement
160
+ requirement: !ruby/object:Gem::Requirement
116
161
  none: false
117
162
  requirements:
118
163
  - - ~>
@@ -120,10 +165,15 @@ dependencies:
120
165
  version: 0.11.1
121
166
  type: :development
122
167
  prerelease: false
123
- version_requirements: *70178931087000
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 0.11.1
124
174
  - !ruby/object:Gem::Dependency
125
175
  name: rake
126
- requirement: &70178931086540 !ruby/object:Gem::Requirement
176
+ requirement: !ruby/object:Gem::Requirement
127
177
  none: false
128
178
  requirements:
129
179
  - - ! '>='
@@ -131,10 +181,15 @@ dependencies:
131
181
  version: '0'
132
182
  type: :development
133
183
  prerelease: false
134
- version_requirements: *70178931086540
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
135
190
  - !ruby/object:Gem::Dependency
136
191
  name: rdoc
137
- requirement: &70178931085980 !ruby/object:Gem::Requirement
192
+ requirement: !ruby/object:Gem::Requirement
138
193
  none: false
139
194
  requirements:
140
195
  - - ! '>='
@@ -142,10 +197,15 @@ dependencies:
142
197
  version: '0'
143
198
  type: :development
144
199
  prerelease: false
145
- version_requirements: *70178931085980
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
146
206
  - !ruby/object:Gem::Dependency
147
207
  name: sass
148
- requirement: &70178931085200 !ruby/object:Gem::Requirement
208
+ requirement: !ruby/object:Gem::Requirement
149
209
  none: false
150
210
  requirements:
151
211
  - - ~>
@@ -153,7 +213,12 @@ dependencies:
153
213
  version: '3.1'
154
214
  type: :development
155
215
  prerelease: false
156
- version_requirements: *70178931085200
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ~>
220
+ - !ruby/object:Gem::Version
221
+ version: '3.1'
157
222
  description: ! " MailCatcher runs a super simple SMTP server which catches any\n
158
223
  \ message sent to it to display in a web interface. Run\n mailcatcher, set
159
224
  your favourite app to deliver to\n smtp://127.0.0.1:1025 instead of your default
@@ -184,6 +249,7 @@ files:
184
249
  - public/javascripts/date.js
185
250
  - public/javascripts/flexie.min.js
186
251
  - public/javascripts/jquery.js
252
+ - public/javascripts/keymaster.min.js
187
253
  - public/javascripts/modernizr.js
188
254
  - public/javascripts/xslt-3.2.js
189
255
  - public/stylesheets/application.css
@@ -209,9 +275,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
209
275
  version: '0'
210
276
  requirements: []
211
277
  rubyforge_project:
212
- rubygems_version: 1.8.11
278
+ rubygems_version: 1.8.23
213
279
  signing_key:
214
280
  specification_version: 3
215
281
  summary: Runs an SMTP server, catches and displays email in a web interface.
216
282
  test_files: []
217
- has_rdoc: