mailcatcher 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
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: