heroku-nav 0.1.24 → 0.2.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/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ heroku-nav (0.1.24)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ addressable (2.3.2)
10
+ bacon (1.2.0)
11
+ crack (0.3.2)
12
+ metaclass (0.0.1)
13
+ mocha (0.13.2)
14
+ metaclass (~> 0.0.1)
15
+ rack (1.5.1)
16
+ rack-protection (1.3.2)
17
+ rack
18
+ rack-test (0.6.2)
19
+ rack (>= 1.0)
20
+ rake (10.0.3)
21
+ sinatra (1.3.4)
22
+ rack (~> 1.4)
23
+ rack-protection (~> 1.3)
24
+ tilt (~> 1.3, >= 1.3.3)
25
+ tilt (1.3.3)
26
+ webmock (1.9.0)
27
+ addressable (>= 2.2.7)
28
+ crack (>= 0.1.7)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ bacon
35
+ heroku-nav!
36
+ mocha
37
+ rack-test
38
+ rake
39
+ sinatra
40
+ webmock
data/README.md CHANGED
@@ -23,6 +23,14 @@ And add the middleware like:
23
23
 
24
24
  config.middleware.use Heroku::Nav::Provider
25
25
 
26
+ By default the header will be inserted only in responses with status 200. You can insert it in responses with different status codes with the `:status` config param:
27
+
28
+ config.middleware.use Heroku::Nav::Provider, :status => [200, 404]
29
+
30
+ To don't display the header on a given request path you can use the `:except` config param:
31
+
32
+ config.middleware.use Heroku::Nav::Provider, :except => /admin/
33
+
26
34
 
27
35
  ## Meta #######################################################################
28
36
 
data/Rakefile CHANGED
@@ -1,28 +1,8 @@
1
+ require 'bundler/setup'
2
+
1
3
  task :default => :spec
2
4
 
3
5
  desc 'Run specs (with story style output)'
4
6
  task 'spec' do
5
7
  sh 'bacon -s spec/*_spec.rb'
6
8
  end
7
-
8
- begin
9
- require 'jeweler'
10
- Jeweler::Tasks.new do |gemspec|
11
- gemspec.name = "heroku-nav"
12
- gemspec.summary = ""
13
- gemspec.description = ""
14
- gemspec.homepage = "http://heroku.com"
15
- gemspec.authors = ["David Dollar", "Pedro Belo", "Raul Murciano", "Todd Matthews"]
16
- gemspec.email = ["david@heroku.com", "pedro@heroku.com", "raul@heroku.com", "todd@heroku.com"]
17
-
18
- gemspec.add_development_dependency(%q<baconmocha>, [">= 0"])
19
- gemspec.add_development_dependency(%q<sinatra>, [">= 0"])
20
- gemspec.add_development_dependency(%q<rack-test>, [">= 0"])
21
- gemspec.add_dependency(%q<rest-client>, [">= 1.0"])
22
- gemspec.add_dependency(%q<json>, [">= 0"])
23
-
24
- gemspec.version = '0.1.24'
25
- end
26
- rescue LoadError
27
- puts "Jeweler not available. Install it with: gem install jeweler"
28
- end
@@ -1,5 +1,5 @@
1
- require 'rest_client'
2
- require 'json'
1
+ require 'okjson'
2
+ require 'net/https'
3
3
  require 'timeout'
4
4
 
5
5
  module Heroku
@@ -33,15 +33,20 @@ module Heroku
33
33
  end
34
34
 
35
35
  class << self
36
- def fetch
37
- Timeout.timeout(10) do
36
+ def fetch(format = 'application/json')
37
+ uri = URI.parse(resource_url)
38
+ http = Net::HTTP.new(uri.host, uri.port)
39
+ http.use_ssl = true if uri.scheme == 'https'
40
+ request = Net::HTTP::Get.new(uri.request_uri)
41
+ request['Accept'] = format
42
+ response = Timeout.timeout(10) do
38
43
  retry_upto(10, :interval => 0.5) do
39
- raw = RestClient.get(resource_url, :accept => :json).to_s
40
- return JSON.parse(raw)
44
+ http.request(request)
41
45
  end
42
46
  end
47
+ format == 'application/json' ? OkJson.decode(response.body) : response.body
43
48
  rescue Exception => e
44
- STDERR.puts "Failed to fetch the Heroku #{resource}: #{e.class.name} - #{e.message}"
49
+ $stderr.puts "Failed to fetch the Heroku #{resource}: #{e.class.name} - #{e.message}"
45
50
  {}
46
51
  end
47
52
 
@@ -113,15 +118,9 @@ module Heroku
113
118
 
114
119
  class Provider < Base
115
120
  class << self
121
+
116
122
  def fetch
117
- Timeout.timeout(10) do
118
- retry_upto(10, :interval => 0.5) do
119
- RestClient.get(resource_url).to_s
120
- end
121
- end
122
- rescue => e
123
- STDERR.puts "Failed to fetch the Heroku #{resource}: #{e.class.name} - #{e.message}"
124
- {}
123
+ super('text/html')
125
124
  end
126
125
 
127
126
  def resource_url
@@ -149,4 +148,4 @@ module Heroku
149
148
  end
150
149
  end
151
150
  end
152
- end
151
+ end
@@ -0,0 +1,597 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright 2011, 2012 Keith Rarick
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ # See https://github.com/kr/okjson for updates.
24
+
25
+ require 'stringio'
26
+
27
+ # Some parts adapted from
28
+ # http://golang.org/src/pkg/json/decode.go and
29
+ # http://golang.org/src/pkg/utf8/utf8.go
30
+ module OkJson
31
+ Upstream = 'LTD7LBKLZWFF7OZK'
32
+ extend self
33
+
34
+
35
+ # Decodes a json document in string s and
36
+ # returns the corresponding ruby value.
37
+ # String s must be valid UTF-8. If you have
38
+ # a string in some other encoding, convert
39
+ # it first.
40
+ #
41
+ # String values in the resulting structure
42
+ # will be UTF-8.
43
+ def decode(s)
44
+ ts = lex(s)
45
+ v, ts = textparse(ts)
46
+ if ts.length > 0
47
+ raise Error, 'trailing garbage'
48
+ end
49
+ v
50
+ end
51
+
52
+
53
+ # Parses a "json text" in the sense of RFC 4627.
54
+ # Returns the parsed value and any trailing tokens.
55
+ # Note: this is almost the same as valparse,
56
+ # except that it does not accept atomic values.
57
+ def textparse(ts)
58
+ if ts.length < 0
59
+ raise Error, 'empty'
60
+ end
61
+
62
+ typ, _, val = ts[0]
63
+ case typ
64
+ when '{' then objparse(ts)
65
+ when '[' then arrparse(ts)
66
+ else
67
+ raise Error, "unexpected #{val.inspect}"
68
+ end
69
+ end
70
+
71
+
72
+ # Parses a "value" in the sense of RFC 4627.
73
+ # Returns the parsed value and any trailing tokens.
74
+ def valparse(ts)
75
+ if ts.length < 0
76
+ raise Error, 'empty'
77
+ end
78
+
79
+ typ, _, val = ts[0]
80
+ case typ
81
+ when '{' then objparse(ts)
82
+ when '[' then arrparse(ts)
83
+ when :val,:str then [val, ts[1..-1]]
84
+ else
85
+ raise Error, "unexpected #{val.inspect}"
86
+ end
87
+ end
88
+
89
+
90
+ # Parses an "object" in the sense of RFC 4627.
91
+ # Returns the parsed value and any trailing tokens.
92
+ def objparse(ts)
93
+ ts = eat('{', ts)
94
+ obj = {}
95
+
96
+ if ts[0][0] == '}'
97
+ return obj, ts[1..-1]
98
+ end
99
+
100
+ k, v, ts = pairparse(ts)
101
+ obj[k] = v
102
+
103
+ if ts[0][0] == '}'
104
+ return obj, ts[1..-1]
105
+ end
106
+
107
+ loop do
108
+ ts = eat(',', ts)
109
+
110
+ k, v, ts = pairparse(ts)
111
+ obj[k] = v
112
+
113
+ if ts[0][0] == '}'
114
+ return obj, ts[1..-1]
115
+ end
116
+ end
117
+ end
118
+
119
+
120
+ # Parses a "member" in the sense of RFC 4627.
121
+ # Returns the parsed values and any trailing tokens.
122
+ def pairparse(ts)
123
+ (typ, _, k), ts = ts[0], ts[1..-1]
124
+ if typ != :str
125
+ raise Error, "unexpected #{k.inspect}"
126
+ end
127
+ ts = eat(':', ts)
128
+ v, ts = valparse(ts)
129
+ [k, v, ts]
130
+ end
131
+
132
+
133
+ # Parses an "array" in the sense of RFC 4627.
134
+ # Returns the parsed value and any trailing tokens.
135
+ def arrparse(ts)
136
+ ts = eat('[', ts)
137
+ arr = []
138
+
139
+ if ts[0][0] == ']'
140
+ return arr, ts[1..-1]
141
+ end
142
+
143
+ v, ts = valparse(ts)
144
+ arr << v
145
+
146
+ if ts[0][0] == ']'
147
+ return arr, ts[1..-1]
148
+ end
149
+
150
+ loop do
151
+ ts = eat(',', ts)
152
+
153
+ v, ts = valparse(ts)
154
+ arr << v
155
+
156
+ if ts[0][0] == ']'
157
+ return arr, ts[1..-1]
158
+ end
159
+ end
160
+ end
161
+
162
+
163
+ def eat(typ, ts)
164
+ if ts[0][0] != typ
165
+ raise Error, "expected #{typ} (got #{ts[0].inspect})"
166
+ end
167
+ ts[1..-1]
168
+ end
169
+
170
+
171
+ # Scans s and returns a list of json tokens,
172
+ # excluding white space (as defined in RFC 4627).
173
+ def lex(s)
174
+ ts = []
175
+ while s.length > 0
176
+ typ, lexeme, val = tok(s)
177
+ if typ == nil
178
+ raise Error, "invalid character at #{s[0,10].inspect}"
179
+ end
180
+ if typ != :space
181
+ ts << [typ, lexeme, val]
182
+ end
183
+ s = s[lexeme.length..-1]
184
+ end
185
+ ts
186
+ end
187
+
188
+
189
+ # Scans the first token in s and
190
+ # returns a 3-element list, or nil
191
+ # if s does not begin with a valid token.
192
+ #
193
+ # The first list element is one of
194
+ # '{', '}', ':', ',', '[', ']',
195
+ # :val, :str, and :space.
196
+ #
197
+ # The second element is the lexeme.
198
+ #
199
+ # The third element is the value of the
200
+ # token for :val and :str, otherwise
201
+ # it is the lexeme.
202
+ def tok(s)
203
+ case s[0]
204
+ when ?{ then ['{', s[0,1], s[0,1]]
205
+ when ?} then ['}', s[0,1], s[0,1]]
206
+ when ?: then [':', s[0,1], s[0,1]]
207
+ when ?, then [',', s[0,1], s[0,1]]
208
+ when ?[ then ['[', s[0,1], s[0,1]]
209
+ when ?] then [']', s[0,1], s[0,1]]
210
+ when ?n then nulltok(s)
211
+ when ?t then truetok(s)
212
+ when ?f then falsetok(s)
213
+ when ?" then strtok(s)
214
+ when Spc then [:space, s[0,1], s[0,1]]
215
+ when ?\t then [:space, s[0,1], s[0,1]]
216
+ when ?\n then [:space, s[0,1], s[0,1]]
217
+ when ?\r then [:space, s[0,1], s[0,1]]
218
+ else numtok(s)
219
+ end
220
+ end
221
+
222
+
223
+ def nulltok(s); s[0,4] == 'null' ? [:val, 'null', nil] : [] end
224
+ def truetok(s); s[0,4] == 'true' ? [:val, 'true', true] : [] end
225
+ def falsetok(s); s[0,5] == 'false' ? [:val, 'false', false] : [] end
226
+
227
+
228
+ def numtok(s)
229
+ m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s)
230
+ if m && m.begin(0) == 0
231
+ if m[3] && !m[2]
232
+ [:val, m[0], Integer(m[1])*(10**Integer(m[3][1..-1]))]
233
+ elsif m[2]
234
+ [:val, m[0], Float(m[0])]
235
+ else
236
+ [:val, m[0], Integer(m[0])]
237
+ end
238
+ else
239
+ []
240
+ end
241
+ end
242
+
243
+
244
+ def strtok(s)
245
+ m = /"([^"\\]|\\["\/\\bfnrt]|\\u[0-9a-fA-F]{4})*"/.match(s)
246
+ if ! m
247
+ raise Error, "invalid string literal at #{abbrev(s)}"
248
+ end
249
+ [:str, m[0], unquote(m[0])]
250
+ end
251
+
252
+
253
+ def abbrev(s)
254
+ t = s[0,10]
255
+ p = t['`']
256
+ t = t[0,p] if p
257
+ t = t + '...' if t.length < s.length
258
+ '`' + t + '`'
259
+ end
260
+
261
+
262
+ # Converts a quoted json string literal q into a UTF-8-encoded string.
263
+ # The rules are different than for Ruby, so we cannot use eval.
264
+ # Unquote will raise an error if q contains control characters.
265
+ def unquote(q)
266
+ q = q[1...-1]
267
+ a = q.dup # allocate a big enough string
268
+ rubydoesenc = false
269
+ # In ruby >= 1.9, a[w] is a codepoint, not a byte.
270
+ if a.class.method_defined?(:force_encoding)
271
+ a.force_encoding('UTF-8')
272
+ rubydoesenc = true
273
+ end
274
+ r, w = 0, 0
275
+ while r < q.length
276
+ c = q[r]
277
+ case true
278
+ when c == ?\\
279
+ r += 1
280
+ if r >= q.length
281
+ raise Error, "string literal ends with a \"\\\": \"#{q}\""
282
+ end
283
+
284
+ case q[r]
285
+ when ?",?\\,?/,?'
286
+ a[w] = q[r]
287
+ r += 1
288
+ w += 1
289
+ when ?b,?f,?n,?r,?t
290
+ a[w] = Unesc[q[r]]
291
+ r += 1
292
+ w += 1
293
+ when ?u
294
+ r += 1
295
+ uchar = begin
296
+ hexdec4(q[r,4])
297
+ rescue RuntimeError => e
298
+ raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}"
299
+ end
300
+ r += 4
301
+ if surrogate? uchar
302
+ if q.length >= r+6
303
+ uchar1 = hexdec4(q[r+2,4])
304
+ uchar = subst(uchar, uchar1)
305
+ if uchar != Ucharerr
306
+ # A valid pair; consume.
307
+ r += 6
308
+ end
309
+ end
310
+ end
311
+ if rubydoesenc
312
+ a[w] = '' << uchar
313
+ w += 1
314
+ else
315
+ w += ucharenc(a, w, uchar)
316
+ end
317
+ else
318
+ raise Error, "invalid escape char #{q[r]} in \"#{q}\""
319
+ end
320
+ when c == ?", c < Spc
321
+ raise Error, "invalid character in string literal \"#{q}\""
322
+ else
323
+ # Copy anything else byte-for-byte.
324
+ # Valid UTF-8 will remain valid UTF-8.
325
+ # Invalid UTF-8 will remain invalid UTF-8.
326
+ # In ruby >= 1.9, c is a codepoint, not a byte,
327
+ # in which case this is still what we want.
328
+ a[w] = c
329
+ r += 1
330
+ w += 1
331
+ end
332
+ end
333
+ a[0,w]
334
+ end
335
+
336
+
337
+ # Encodes unicode character u as UTF-8
338
+ # bytes in string a at position i.
339
+ # Returns the number of bytes written.
340
+ def ucharenc(a, i, u)
341
+ case true
342
+ when u <= Uchar1max
343
+ a[i] = (u & 0xff).chr
344
+ 1
345
+ when u <= Uchar2max
346
+ a[i+0] = (Utag2 | ((u>>6)&0xff)).chr
347
+ a[i+1] = (Utagx | (u&Umaskx)).chr
348
+ 2
349
+ when u <= Uchar3max
350
+ a[i+0] = (Utag3 | ((u>>12)&0xff)).chr
351
+ a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr
352
+ a[i+2] = (Utagx | (u&Umaskx)).chr
353
+ 3
354
+ else
355
+ a[i+0] = (Utag4 | ((u>>18)&0xff)).chr
356
+ a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr
357
+ a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr
358
+ a[i+3] = (Utagx | (u&Umaskx)).chr
359
+ 4
360
+ end
361
+ end
362
+
363
+
364
+ def hexdec4(s)
365
+ if s.length != 4
366
+ raise Error, 'short'
367
+ end
368
+ (nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3])
369
+ end
370
+
371
+
372
+ def subst(u1, u2)
373
+ if Usurr1 <= u1 && u1 < Usurr2 && Usurr2 <= u2 && u2 < Usurr3
374
+ return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself
375
+ end
376
+ return Ucharerr
377
+ end
378
+
379
+
380
+ def surrogate?(u)
381
+ Usurr1 <= u && u < Usurr3
382
+ end
383
+
384
+
385
+ def nibble(c)
386
+ case true
387
+ when ?0 <= c && c <= ?9 then c.ord - ?0.ord
388
+ when ?a <= c && c <= ?z then c.ord - ?a.ord + 10
389
+ when ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
390
+ else
391
+ raise Error, "invalid hex code #{c}"
392
+ end
393
+ end
394
+
395
+
396
+ # Encodes x into a json text. It may contain only
397
+ # Array, Hash, String, Numeric, true, false, nil.
398
+ # (Note, this list excludes Symbol.)
399
+ # X itself must be an Array or a Hash.
400
+ # No other value can be encoded, and an error will
401
+ # be raised if x contains any other value, such as
402
+ # Nan, Infinity, Symbol, and Proc, or if a Hash key
403
+ # is not a String.
404
+ # Strings contained in x must be valid UTF-8.
405
+ def encode(x)
406
+ case x
407
+ when Hash then objenc(x)
408
+ when Array then arrenc(x)
409
+ else
410
+ raise Error, 'root value must be an Array or a Hash'
411
+ end
412
+ end
413
+
414
+
415
+ def valenc(x)
416
+ case x
417
+ when Hash then objenc(x)
418
+ when Array then arrenc(x)
419
+ when String then strenc(x)
420
+ when Numeric then numenc(x)
421
+ when true then "true"
422
+ when false then "false"
423
+ when nil then "null"
424
+ else
425
+ raise Error, "cannot encode #{x.class}: #{x.inspect}"
426
+ end
427
+ end
428
+
429
+
430
+ def objenc(x)
431
+ '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}'
432
+ end
433
+
434
+
435
+ def arrenc(a)
436
+ '[' + a.map{|x| valenc(x)}.join(',') + ']'
437
+ end
438
+
439
+
440
+ def keyenc(k)
441
+ case k
442
+ when String then strenc(k)
443
+ else
444
+ raise Error, "Hash key is not a string: #{k.inspect}"
445
+ end
446
+ end
447
+
448
+
449
+ def strenc(s)
450
+ t = StringIO.new
451
+ t.putc(?")
452
+ r = 0
453
+
454
+ # In ruby >= 1.9, s[r] is a codepoint, not a byte.
455
+ rubydoesenc = s.class.method_defined?(:encoding)
456
+
457
+ while r < s.length
458
+ case s[r]
459
+ when ?" then t.print('\\"')
460
+ when ?\\ then t.print('\\\\')
461
+ when ?\b then t.print('\\b')
462
+ when ?\f then t.print('\\f')
463
+ when ?\n then t.print('\\n')
464
+ when ?\r then t.print('\\r')
465
+ when ?\t then t.print('\\t')
466
+ else
467
+ c = s[r]
468
+ case true
469
+ when rubydoesenc
470
+ begin
471
+ c.ord # will raise an error if c is invalid UTF-8
472
+ t.write(c)
473
+ rescue
474
+ t.write(Ustrerr)
475
+ end
476
+ when Spc <= c && c <= ?~
477
+ t.putc(c)
478
+ else
479
+ n = ucharcopy(t, s, r) # ensure valid UTF-8 output
480
+ r += n - 1 # r is incremented below
481
+ end
482
+ end
483
+ r += 1
484
+ end
485
+ t.putc(?")
486
+ t.string
487
+ end
488
+
489
+
490
+ def numenc(x)
491
+ if ((x.nan? || x.infinite?) rescue false)
492
+ raise Error, "Numeric cannot be represented: #{x}"
493
+ end
494
+ "#{x}"
495
+ end
496
+
497
+
498
+ # Copies the valid UTF-8 bytes of a single character
499
+ # from string s at position i to I/O object t, and
500
+ # returns the number of bytes copied.
501
+ # If no valid UTF-8 char exists at position i,
502
+ # ucharcopy writes Ustrerr and returns 1.
503
+ def ucharcopy(t, s, i)
504
+ n = s.length - i
505
+ raise Utf8Error if n < 1
506
+
507
+ c0 = s[i].ord
508
+
509
+ # 1-byte, 7-bit sequence?
510
+ if c0 < Utagx
511
+ t.putc(c0)
512
+ return 1
513
+ end
514
+
515
+ raise Utf8Error if c0 < Utag2 # unexpected continuation byte?
516
+
517
+ raise Utf8Error if n < 2 # need continuation byte
518
+ c1 = s[i+1].ord
519
+ raise Utf8Error if c1 < Utagx || Utag2 <= c1
520
+
521
+ # 2-byte, 11-bit sequence?
522
+ if c0 < Utag3
523
+ raise Utf8Error if ((c0&Umask2)<<6 | (c1&Umaskx)) <= Uchar1max
524
+ t.putc(c0)
525
+ t.putc(c1)
526
+ return 2
527
+ end
528
+
529
+ # need second continuation byte
530
+ raise Utf8Error if n < 3
531
+
532
+ c2 = s[i+2].ord
533
+ raise Utf8Error if c2 < Utagx || Utag2 <= c2
534
+
535
+ # 3-byte, 16-bit sequence?
536
+ if c0 < Utag4
537
+ u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
538
+ raise Utf8Error if u <= Uchar2max
539
+ t.putc(c0)
540
+ t.putc(c1)
541
+ t.putc(c2)
542
+ return 3
543
+ end
544
+
545
+ # need third continuation byte
546
+ raise Utf8Error if n < 4
547
+ c3 = s[i+3].ord
548
+ raise Utf8Error if c3 < Utagx || Utag2 <= c3
549
+
550
+ # 4-byte, 21-bit sequence?
551
+ if c0 < Utag5
552
+ u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
553
+ raise Utf8Error if u <= Uchar3max
554
+ t.putc(c0)
555
+ t.putc(c1)
556
+ t.putc(c2)
557
+ t.putc(c3)
558
+ return 4
559
+ end
560
+
561
+ raise Utf8Error
562
+ rescue Utf8Error
563
+ t.write(Ustrerr)
564
+ return 1
565
+ end
566
+
567
+
568
+ class Utf8Error < ::StandardError
569
+ end
570
+
571
+
572
+ class Error < ::StandardError
573
+ end
574
+
575
+
576
+ Utagx = 0x80 # 1000 0000
577
+ Utag2 = 0xc0 # 1100 0000
578
+ Utag3 = 0xe0 # 1110 0000
579
+ Utag4 = 0xf0 # 1111 0000
580
+ Utag5 = 0xF8 # 1111 1000
581
+ Umaskx = 0x3f # 0011 1111
582
+ Umask2 = 0x1f # 0001 1111
583
+ Umask3 = 0x0f # 0000 1111
584
+ Umask4 = 0x07 # 0000 0111
585
+ Uchar1max = (1<<7) - 1
586
+ Uchar2max = (1<<11) - 1
587
+ Uchar3max = (1<<16) - 1
588
+ Ucharerr = 0xFFFD # unicode "replacement char"
589
+ Ustrerr = "\xef\xbf\xbd" # unicode "replacement char"
590
+ Usurrself = 0x10000
591
+ Usurr1 = 0xd800
592
+ Usurr2 = 0xdc00
593
+ Usurr3 = 0xe000
594
+
595
+ Spc = ' '[0]
596
+ Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t}
597
+ end
@@ -1,8 +1,13 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ require File.expand_path('../base', __FILE__)
2
2
 
3
3
  describe "Api" do
4
+
5
+ url = "https://nav.heroku.com/header"
6
+
4
7
  before do
5
- RestClient.stubs(:get).returns({ 'html' => '<!-- header -->' }.to_json)
8
+ WebMock.reset!
9
+ stub_request(:get, url).
10
+ to_return(:body => OkJson.encode({ 'html' => '<!-- header -->' }))
6
11
  end
7
12
 
8
13
  it "has a resource based on the class name" do
@@ -11,15 +16,18 @@ describe "Api" do
11
16
  end
12
17
 
13
18
  it "has a resource url based on the api url" do
14
- Heroku::Nav::Header.resource_url.should == 'https://nav.heroku.com/header'
19
+ Heroku::Nav::Header.resource_url.should == url
15
20
  end
16
21
 
17
22
  it "doesn't raise" do
18
- RestClient.stubs(:get).raises("error")
19
- lambda { Heroku::Nav::Header.fetch }.should.not.raise
23
+ stub_request(:get, url).to_timeout
24
+ stderr = wrap_stderr do
25
+ Heroku::Nav::Header.fetch.should == {}
26
+ end
27
+ stderr.should == "Failed to fetch the Heroku header: Timeout::Error - execution expired\n"
20
28
  end
21
29
 
22
30
  it "parses the JSON response, returning the html and css" do
23
31
  Heroku::Nav::Header.fetch.should == { 'html' => '<!-- header -->' }
24
32
  end
25
- end
33
+ end
@@ -1,9 +1,10 @@
1
1
  $: << File.dirname(__FILE__) + '/../lib'
2
2
 
3
- require 'rubygems'
4
3
  require 'heroku/nav'
5
4
  require 'sinatra/base'
6
- require 'baconmocha'
5
+ require 'bacon'
6
+ require 'mocha/api'
7
+ require 'webmock'
7
8
  require 'rack/test'
8
9
 
9
10
  class TestApp < Sinatra::Base
@@ -21,6 +22,10 @@ class TestApp < Sinatra::Base
21
22
  params[:body]
22
23
  end
23
24
 
25
+ get '/404' do
26
+ [404, "<html><body>404"]
27
+ end
28
+
24
29
  get '/alternate' do
25
30
  params[:body]
26
31
  end
@@ -33,13 +38,24 @@ end
33
38
 
34
39
  # tiny factory to help making a Sinatra::Base application.
35
40
  # whatever is passed in the block will get eval'ed into the class
36
- def make_app(&blk)
37
- handler = Class.new(TestApp)
38
- handler.class_eval(&blk)
39
- handler
40
- end
41
-
42
41
  # Make sure Rack::Test methods are available for all specs
43
42
  class Bacon::Context
44
- include Rack::Test::Methods
43
+ include ::Rack::Test::Methods
44
+ include ::Mocha::API
45
+ include ::WebMock::API
46
+
47
+ def make_app(&blk)
48
+ handler = Class.new(TestApp)
49
+ handler.class_eval(&blk)
50
+ handler
51
+ end
52
+
53
+ def wrap_stderr(&block)
54
+ original_stderr = $stderr
55
+ $stderr = StringIO.new
56
+ yield
57
+ str = $stderr.string
58
+ $stderr = original_stderr
59
+ str
60
+ end
45
61
  end
@@ -1,13 +1,31 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ require File.expand_path('../base', __FILE__)
2
2
 
3
3
  describe Heroku::Nav::Header do
4
+
4
5
  def app
5
6
  make_app { use Heroku::Nav::Header }
6
7
  end
7
8
 
9
+ def wrap_stderr(&block)
10
+ original_stderr = $stderr
11
+ $stderr = StringIO.new
12
+ yield
13
+ str = $stderr.string
14
+ $stderr = original_stderr
15
+ str
16
+ end
17
+
18
+ url = "https://nav.heroku.com/header"
19
+
20
+ before do
21
+ WebMock.reset!
22
+ end
23
+
8
24
  it "rescues exceptions" do
9
- RestClient.stubs(:get).raises(Timeout::Error)
10
- get '/', :body => '<html><body>hi'
25
+ stub_request(:get, url).to_timeout
26
+ wrap_stderr do
27
+ get '/', :body => '<html><body>hi'
28
+ end
11
29
  last_response.status.should.equal 200
12
30
  end
13
31
 
@@ -172,4 +190,4 @@ describe Heroku::Nav::Provider do
172
190
  end
173
191
  end
174
192
  end
175
- end
193
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heroku-nav
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.24
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,14 +9,15 @@ authors:
9
9
  - Pedro Belo
10
10
  - Raul Murciano
11
11
  - Todd Matthews
12
+ - Jonathan Dance
12
13
  autorequire:
13
14
  bindir: bin
14
15
  cert_chain: []
15
- date: 2011-10-10 00:00:00.000000000Z
16
+ date: 2013-02-05 00:00:00.000000000 Z
16
17
  dependencies:
17
18
  - !ruby/object:Gem::Dependency
18
- name: baconmocha
19
- requirement: &2154312380 !ruby/object:Gem::Requirement
19
+ name: rake
20
+ requirement: !ruby/object:Gem::Requirement
20
21
  none: false
21
22
  requirements:
22
23
  - - ! '>='
@@ -24,10 +25,15 @@ dependencies:
24
25
  version: '0'
25
26
  type: :development
26
27
  prerelease: false
27
- version_requirements: *2154312380
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
28
34
  - !ruby/object:Gem::Dependency
29
- name: sinatra
30
- requirement: &2154311900 !ruby/object:Gem::Requirement
35
+ name: bacon
36
+ requirement: !ruby/object:Gem::Requirement
31
37
  none: false
32
38
  requirements:
33
39
  - - ! '>='
@@ -35,10 +41,31 @@ dependencies:
35
41
  version: '0'
36
42
  type: :development
37
43
  prerelease: false
38
- version_requirements: *2154311900
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
39
50
  - !ruby/object:Gem::Dependency
40
- name: rack-test
41
- requirement: &2154311400 !ruby/object:Gem::Requirement
51
+ name: mocha
52
+ requirement: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ - !ruby/object:Gem::Dependency
67
+ name: sinatra
68
+ requirement: !ruby/object:Gem::Requirement
42
69
  none: false
43
70
  requirements:
44
71
  - - ! '>='
@@ -46,48 +73,66 @@ dependencies:
46
73
  version: '0'
47
74
  type: :development
48
75
  prerelease: false
49
- version_requirements: *2154311400
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
50
82
  - !ruby/object:Gem::Dependency
51
- name: rest-client
52
- requirement: &2154310880 !ruby/object:Gem::Requirement
83
+ name: rack-test
84
+ requirement: !ruby/object:Gem::Requirement
53
85
  none: false
54
86
  requirements:
55
87
  - - ! '>='
56
88
  - !ruby/object:Gem::Version
57
- version: '1.0'
58
- type: :runtime
89
+ version: '0'
90
+ type: :development
59
91
  prerelease: false
60
- version_requirements: *2154310880
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
61
98
  - !ruby/object:Gem::Dependency
62
- name: json
63
- requirement: &2154310220 !ruby/object:Gem::Requirement
99
+ name: webmock
100
+ requirement: !ruby/object:Gem::Requirement
64
101
  none: false
65
102
  requirements:
66
103
  - - ! '>='
67
104
  - !ruby/object:Gem::Version
68
105
  version: '0'
69
- type: :runtime
106
+ type: :development
70
107
  prerelease: false
71
- version_requirements: *2154310220
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
72
114
  description: ''
73
115
  email:
74
116
  - david@heroku.com
75
117
  - pedro@heroku.com
76
118
  - raul@heroku.com
77
119
  - todd@heroku.com
120
+ - jd@heroku.com
78
121
  executables: []
79
122
  extensions: []
80
123
  extra_rdoc_files:
81
124
  - README.md
82
125
  files:
83
- - README.md
84
- - Rakefile
85
- - heroku-nav.gemspec
86
126
  - lib/heroku/nav.rb
127
+ - lib/okjson.rb
87
128
  - spec/api_spec.rb
88
129
  - spec/base.rb
89
130
  - spec/nav_spec.rb
90
- homepage: http://heroku.com
131
+ - README.md
132
+ - Gemfile
133
+ - Gemfile.lock
134
+ - Rakefile
135
+ homepage: http://github.com/heroku/heroku-nav
91
136
  licenses: []
92
137
  post_install_message:
93
138
  rdoc_options: []
@@ -102,12 +147,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
102
147
  required_rubygems_version: !ruby/object:Gem::Requirement
103
148
  none: false
104
149
  requirements:
105
- - - ! '>='
150
+ - - '='
106
151
  - !ruby/object:Gem::Version
107
- version: '0'
152
+ version: 1.3.6
108
153
  requirements: []
109
154
  rubyforge_project:
110
- rubygems_version: 1.8.10
155
+ rubygems_version: 1.8.23
111
156
  signing_key:
112
157
  specification_version: 3
113
158
  summary: ''
@@ -115,3 +160,6 @@ test_files:
115
160
  - spec/api_spec.rb
116
161
  - spec/base.rb
117
162
  - spec/nav_spec.rb
163
+ - Gemfile
164
+ - Gemfile.lock
165
+ - Rakefile
@@ -1,62 +0,0 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
- # -*- encoding: utf-8 -*-
5
-
6
- Gem::Specification.new do |s|
7
- s.name = %q{heroku-nav}
8
- s.version = "0.1.24"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["David Dollar", "Pedro Belo", "Raul Murciano", "Todd Matthews"]
12
- s.date = %q{2011-10-10}
13
- s.description = %q{}
14
- s.email = ["david@heroku.com", "pedro@heroku.com", "raul@heroku.com", "todd@heroku.com"]
15
- s.extra_rdoc_files = [
16
- "README.md"
17
- ]
18
- s.files = [
19
- "README.md",
20
- "Rakefile",
21
- "heroku-nav.gemspec",
22
- "lib/heroku/nav.rb",
23
- "spec/api_spec.rb",
24
- "spec/base.rb",
25
- "spec/nav_spec.rb"
26
- ]
27
- s.homepage = %q{http://heroku.com}
28
- s.require_paths = ["lib"]
29
- s.rubygems_version = %q{1.3.6}
30
- s.summary = %q{}
31
- s.test_files = [
32
- "spec/api_spec.rb",
33
- "spec/base.rb",
34
- "spec/nav_spec.rb"
35
- ]
36
-
37
- if s.respond_to? :specification_version then
38
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
39
- s.specification_version = 3
40
-
41
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
42
- s.add_development_dependency(%q<baconmocha>, [">= 0"])
43
- s.add_development_dependency(%q<sinatra>, [">= 0"])
44
- s.add_development_dependency(%q<rack-test>, [">= 0"])
45
- s.add_runtime_dependency(%q<rest-client>, [">= 1.0"])
46
- s.add_runtime_dependency(%q<json>, [">= 0"])
47
- else
48
- s.add_dependency(%q<baconmocha>, [">= 0"])
49
- s.add_dependency(%q<sinatra>, [">= 0"])
50
- s.add_dependency(%q<rack-test>, [">= 0"])
51
- s.add_dependency(%q<rest-client>, [">= 1.0"])
52
- s.add_dependency(%q<json>, [">= 0"])
53
- end
54
- else
55
- s.add_dependency(%q<baconmocha>, [">= 0"])
56
- s.add_dependency(%q<sinatra>, [">= 0"])
57
- s.add_dependency(%q<rack-test>, [">= 0"])
58
- s.add_dependency(%q<rest-client>, [">= 1.0"])
59
- s.add_dependency(%q<json>, [">= 0"])
60
- end
61
- end
62
-