heroku-nav 0.1.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/.gitignore +1 -0
- data/Rakefile +28 -0
- data/heroku-nav.gemspec +60 -0
- data/lib/heroku/nav.rb +67 -0
- data/spec/api_spec.rb +25 -0
- data/spec/base.rb +45 -0
- data/spec/nav_spec.rb +60 -0
- metadata +113 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg/
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
task :default => :spec
|
2
|
+
|
3
|
+
desc 'Run specs (with story style output)'
|
4
|
+
task 'spec' do
|
5
|
+
sh 'bacon -s spec/*_spec.rb'
|
6
|
+
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.email = "pedro@heroku.com"
|
15
|
+
gemspec.homepage = "http://heroku.com"
|
16
|
+
gemspec.authors = ["Todd Matthews", "Pedro Belo"]
|
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.2.0"])
|
22
|
+
gemspec.add_dependency(%q<json>, [">= 0"])
|
23
|
+
|
24
|
+
gemspec.version = '0.1.0'
|
25
|
+
end
|
26
|
+
rescue LoadError
|
27
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
28
|
+
end
|
data/heroku-nav.gemspec
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{heroku-nav}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Todd Matthews", "Pedro Belo"]
|
12
|
+
s.date = %q{2010-02-24}
|
13
|
+
s.description = %q{}
|
14
|
+
s.email = %q{pedro@heroku.com}
|
15
|
+
s.files = [
|
16
|
+
".gitignore",
|
17
|
+
"Rakefile",
|
18
|
+
"heroku-nav.gemspec",
|
19
|
+
"lib/heroku/nav.rb",
|
20
|
+
"spec/api_spec.rb",
|
21
|
+
"spec/base.rb",
|
22
|
+
"spec/nav_spec.rb"
|
23
|
+
]
|
24
|
+
s.homepage = %q{http://heroku.com}
|
25
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
s.rubygems_version = %q{1.3.5}
|
28
|
+
s.summary = %q{}
|
29
|
+
s.test_files = [
|
30
|
+
"spec/api_spec.rb",
|
31
|
+
"spec/base.rb",
|
32
|
+
"spec/nav_spec.rb"
|
33
|
+
]
|
34
|
+
|
35
|
+
if s.respond_to? :specification_version then
|
36
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
37
|
+
s.specification_version = 3
|
38
|
+
|
39
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
40
|
+
s.add_development_dependency(%q<baconmocha>, [">= 0"])
|
41
|
+
s.add_development_dependency(%q<sinatra>, [">= 0"])
|
42
|
+
s.add_development_dependency(%q<rack-test>, [">= 0"])
|
43
|
+
s.add_runtime_dependency(%q<rest-client>, ["~> 1.2.0"])
|
44
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
45
|
+
else
|
46
|
+
s.add_dependency(%q<baconmocha>, [">= 0"])
|
47
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
48
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
49
|
+
s.add_dependency(%q<rest-client>, ["~> 1.2.0"])
|
50
|
+
s.add_dependency(%q<json>, [">= 0"])
|
51
|
+
end
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<baconmocha>, [">= 0"])
|
54
|
+
s.add_dependency(%q<sinatra>, [">= 0"])
|
55
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
56
|
+
s.add_dependency(%q<rest-client>, ["~> 1.2.0"])
|
57
|
+
s.add_dependency(%q<json>, [">= 0"])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/lib/heroku/nav.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Heroku
|
5
|
+
module Nav
|
6
|
+
class Base
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
refresh
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
@status, @headers, @body = @app.call(env)
|
14
|
+
insert! if can_insert?
|
15
|
+
[@status, @headers, @body]
|
16
|
+
end
|
17
|
+
|
18
|
+
def can_insert?
|
19
|
+
return unless @headers['Content-Type'] == 'text/html'
|
20
|
+
return unless @body.respond_to?(:[])
|
21
|
+
return unless @body[0] =~ /<body.*?>/i
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def refresh
|
26
|
+
@html, @css = fetch
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch
|
30
|
+
raw = RestClient.get(resource_url, :accept => :json)
|
31
|
+
attrs = JSON.parse(raw)
|
32
|
+
[attrs['html'], attrs['css']]
|
33
|
+
rescue => e
|
34
|
+
STDERR.puts "Failed to fetch the Heroku #{resource}: #{e.class.name} - #{e.message}"
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def resource
|
39
|
+
self.class.name.split('::').last.downcase
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource_url
|
43
|
+
[api_url, resource].join
|
44
|
+
end
|
45
|
+
|
46
|
+
def api_url
|
47
|
+
ENV['API_URL'] || "http://nav.heroku.com/"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Header < Base
|
52
|
+
def insert!
|
53
|
+
@body[0].gsub!(/(<head>)/i, "\\1<style type=\"text/css\">#{@css}</style>") if @css
|
54
|
+
@body[0].gsub!(/(<body.*?>\s*(<div .*?class=["'].*?container.*?["'].*?>)?)/i, "\\1#{@html}") if @html
|
55
|
+
@headers['Content-Length'] = @body[0].size.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Footer < Base
|
60
|
+
def insert!
|
61
|
+
@body[0].gsub!(/(<head>)/i, "\\1<style type=\"text/css\">#{@css}</style>") if @css
|
62
|
+
@body[0].gsub!(/(<\/body>)/i, "#{@html}\\1") if @html
|
63
|
+
@headers['Content-Length'] = @body[0].size.to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/spec/api_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
describe "Api" do
|
4
|
+
before do
|
5
|
+
RestClient.stubs(:get).returns({ :html => '<!-- header -->', :css => '#header' }.to_json)
|
6
|
+
@header = Heroku::Nav::Header.new(:app)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "has a resource based on the class name" do
|
10
|
+
@header.resource.should == 'header'
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has a resource url based on the api url" do
|
14
|
+
@header.resource_url.should == 'http://nav.heroku.com/header'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "doesn't raise" do
|
18
|
+
RestClient.stubs(:get).raises("error")
|
19
|
+
lambda { @header.fetch }.should.not.raise
|
20
|
+
end
|
21
|
+
|
22
|
+
it "parses the JSON response, returning the html and css" do
|
23
|
+
@header.fetch.should == ['<!-- header -->', '#header']
|
24
|
+
end
|
25
|
+
end
|
data/spec/base.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
$: << File.dirname(__FILE__) + '/../lib'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'heroku/nav'
|
5
|
+
require 'sinatra/base'
|
6
|
+
require 'baconmocha'
|
7
|
+
require 'rack/test'
|
8
|
+
|
9
|
+
class TestApp < Sinatra::Base
|
10
|
+
set :environment, :test
|
11
|
+
|
12
|
+
def self.body
|
13
|
+
@@body
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.body=(body)
|
17
|
+
@@body = body
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/' do
|
21
|
+
params[:body]
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/alternate' do
|
25
|
+
params[:body]
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/text' do
|
29
|
+
content_type 'text/plain'
|
30
|
+
params[:body]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# tiny factory to help making a Sinatra::Base application.
|
35
|
+
# 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
|
+
# Make sure Rack::Test methods are available for all specs
|
43
|
+
class Bacon::Context
|
44
|
+
include Rack::Test::Methods
|
45
|
+
end
|
data/spec/nav_spec.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/base'
|
2
|
+
|
3
|
+
class Heroku::Nav::Header
|
4
|
+
def fetch
|
5
|
+
['<!-- header -->', '#header']
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Heroku::Nav::Footer
|
10
|
+
def fetch
|
11
|
+
['<!-- footer -->', '#footer']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Heroku::Nav::Header do
|
16
|
+
def app
|
17
|
+
make_app { use Heroku::Nav::Header }
|
18
|
+
end
|
19
|
+
|
20
|
+
it "doesn't apply if content-type is not html" do
|
21
|
+
get '/text', :body => '<html><body>hi'
|
22
|
+
last_response.body.should.equal '<html><body>hi'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "adds the html right after the body" do
|
26
|
+
get '/', :body => '<html><body>hi'
|
27
|
+
last_response.body.should.equal '<html><body><!-- header -->hi'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "adds the html right after the body, even if it has properties" do
|
31
|
+
get '/', :body => '<html><body id="a" class="b">hi'
|
32
|
+
last_response.body.should.equal '<html><body id="a" class="b"><!-- header -->hi'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "adds the html right after the first div if class is container" do
|
36
|
+
get '/', :body => '<html><body><div class="container">hi</div>'
|
37
|
+
last_response.body.should.equal '<html><body><div class="container"><!-- header -->hi</div>'
|
38
|
+
end
|
39
|
+
|
40
|
+
it "adds the css right after the head" do
|
41
|
+
get '/', :body => '<html><head>... <body>'
|
42
|
+
last_response.body.should.equal '<html><head><style type="text/css">#header</style>... <body><!-- header -->'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe Heroku::Nav::Footer do
|
47
|
+
def app
|
48
|
+
make_app { use Heroku::Nav::Footer }
|
49
|
+
end
|
50
|
+
|
51
|
+
it "adds the html right before the body closing" do
|
52
|
+
get '/', :body => '<body>hi</body></html>'
|
53
|
+
last_response.body.should.equal '<body>hi<!-- footer --></body></html>'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "adds the css right after the head" do
|
57
|
+
get '/', :body => '<html><head>... <body>'
|
58
|
+
last_response.body.should.equal '<html><head><style type="text/css">#footer</style>... <body>'
|
59
|
+
end
|
60
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: heroku-nav
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Todd Matthews
|
8
|
+
- Pedro Belo
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-02-24 00:00:00 -08:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: baconmocha
|
18
|
+
type: :development
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: sinatra
|
28
|
+
type: :development
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rack-test
|
38
|
+
type: :development
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rest-client
|
48
|
+
type: :runtime
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.0
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: json
|
58
|
+
type: :runtime
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
description: ""
|
67
|
+
email: pedro@heroku.com
|
68
|
+
executables: []
|
69
|
+
|
70
|
+
extensions: []
|
71
|
+
|
72
|
+
extra_rdoc_files: []
|
73
|
+
|
74
|
+
files:
|
75
|
+
- .gitignore
|
76
|
+
- Rakefile
|
77
|
+
- heroku-nav.gemspec
|
78
|
+
- lib/heroku/nav.rb
|
79
|
+
- spec/api_spec.rb
|
80
|
+
- spec/base.rb
|
81
|
+
- spec/nav_spec.rb
|
82
|
+
has_rdoc: true
|
83
|
+
homepage: http://heroku.com
|
84
|
+
licenses: []
|
85
|
+
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options:
|
88
|
+
- --charset=UTF-8
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: "0"
|
96
|
+
version:
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: "0"
|
102
|
+
version:
|
103
|
+
requirements: []
|
104
|
+
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 1.3.5
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: ""
|
110
|
+
test_files:
|
111
|
+
- spec/api_spec.rb
|
112
|
+
- spec/base.rb
|
113
|
+
- spec/nav_spec.rb
|