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 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
@@ -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