stylo 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.document +5 -0
  2. data/.gitignore +25 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +18 -0
  5. data/Rakefile +46 -0
  6. data/TODO +4 -0
  7. data/cucumber.yml +2 -0
  8. data/features/fixtures/child.css +3 -0
  9. data/features/fixtures/child.js +3 -0
  10. data/features/fixtures/grand_parent.css +5 -0
  11. data/features/fixtures/grand_parent.js +5 -0
  12. data/features/fixtures/grand_parent_with_parent_with_child.css +11 -0
  13. data/features/fixtures/grand_parent_with_parent_with_child.js +11 -0
  14. data/features/fixtures/parent.css +5 -0
  15. data/features/fixtures/parent.js +5 -0
  16. data/features/fixtures/parent_with_child.css +7 -0
  17. data/features/fixtures/parent_with_child.js +7 -0
  18. data/features/fixtures/processed_sass_child.css +2 -0
  19. data/features/fixtures/processed_sass_which_uses_mixin.css +3 -0
  20. data/features/fixtures/sass_child.scss +4 -0
  21. data/features/fixtures/sass_mixins.scss +3 -0
  22. data/features/fixtures/sass_which_uses_mixin.scss +6 -0
  23. data/features/fixtures.rb +5 -0
  24. data/features/javascripts.feature +19 -0
  25. data/features/response_headers.feature +13 -0
  26. data/features/sass_integration.feature +13 -0
  27. data/features/step_definitions/stylo_steps.rb +24 -0
  28. data/features/stylesheets.feature +31 -0
  29. data/features/stylo_cannot_serve_asset.feature +5 -0
  30. data/features/support/env.rb +24 -0
  31. data/lib/stylo/asset_loader.rb +9 -0
  32. data/lib/stylo/combiner.rb +14 -0
  33. data/lib/stylo/config.rb +14 -0
  34. data/lib/stylo/pipeline_steps/caching.rb +11 -0
  35. data/lib/stylo/pipeline_steps/javascript.rb +15 -0
  36. data/lib/stylo/pipeline_steps/sass.rb +18 -0
  37. data/lib/stylo/pipeline_steps/stylesheet.rb +15 -0
  38. data/lib/stylo/processor.rb +47 -0
  39. data/lib/stylo/rack.rb +20 -0
  40. data/lib/stylo/railtie.rb +13 -0
  41. data/lib/stylo/response.rb +41 -0
  42. data/lib/stylo.rb +5 -0
  43. data/spec/asset_loader_spec.rb +16 -0
  44. data/spec/combiner_spec.rb +41 -0
  45. data/spec/pipeline_steps/caching_spec.rb +26 -0
  46. data/spec/pipeline_steps/javascript_spec.rb +84 -0
  47. data/spec/pipeline_steps/sass_spec.rb +94 -0
  48. data/spec/pipeline_steps/stylesheet_spec.rb +84 -0
  49. data/spec/rack_spec.rb +75 -0
  50. data/spec/spec_helper.rb +18 -0
  51. data/spec/stylo_spec_helpers.rb +30 -0
  52. data/stylo.gemspec +116 -0
  53. metadata +198 -0
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylo::Combiner do
4
+ let(:request_path_directory) { 'assets' }
5
+ let(:combiner) { Stylo::Combiner.new(request_path_directory, /require "(.*)";/) }
6
+
7
+ describe "when the content does not contain the require pattern" do
8
+ let(:content) { "This is some text." }
9
+
10
+ it "should return the content" do
11
+ combiner.process(content).should == content
12
+ end
13
+ end
14
+
15
+ describe "when the content contains the require pattern" do
16
+ let(:content) { 'require "some_other_file"; and then some other content' }
17
+
18
+ before(:each) do
19
+ Stylo::AssetLoader.stub(:load_content).with('assets/some_other_file').and_return('the required content.')
20
+ end
21
+
22
+ it "should load required content from the asset loader" do
23
+ Stylo::AssetLoader.should_receive(:load_content).with('assets/some_other_file').and_return('the required content.')
24
+
25
+ combiner.process(content)
26
+ end
27
+
28
+ it "should return the combined content" do
29
+ combiner.process(content).should == 'the required content. and then some other content'
30
+ end
31
+
32
+ describe "and the required content has the require pattern" do
33
+ it "should combine the content recursively" do
34
+ Stylo::AssetLoader.stub(:load_content).with('assets/some_other_file').and_return('require "yet_another_file"; the required content.')
35
+ Stylo::AssetLoader.stub(:load_content).with('assets/yet_another_file').and_return('the other required content.')
36
+
37
+ combiner.process(content).should == "the other required content. the required content. and then some other content"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylo::PipelineSteps::Caching do
4
+ describe "call" do
5
+ describe "when no response has been set" do
6
+ it "should not add a cache header" do
7
+ response = Stylo::Response.new('stylesheets/test.css')
8
+
9
+ Stylo::PipelineSteps::Caching.new().call(response)
10
+
11
+ response.headers.should be_empty
12
+ end
13
+ end
14
+
15
+ describe "when a response has been set" do
16
+ it "should add a cache header" do
17
+ response = Stylo::Response.new('stylesheets/test.css')
18
+ response.set_body('alert ("hello");', :javascript)
19
+
20
+ Stylo::PipelineSteps::Caching.new().call(response)
21
+
22
+ response.headers['Cache-Control'].should == 'public, max-age=86400'
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylo::PipelineSteps::Javascript do
4
+ let(:step) { Stylo::PipelineSteps::Javascript.new }
5
+
6
+ describe "when the response has not already been set" do
7
+ describe "and the request is not for a javascript asset" do
8
+ it "should not set the response" do
9
+ response = Stylo::Response.new('stylesheets/test.css')
10
+ Stylo::AssetLoader.stub(:load_content).with(response.path).and_return('some-content')
11
+
12
+ step.call(response)
13
+
14
+ response.has_content?.should be_false
15
+ end
16
+ end
17
+
18
+ describe "and the request is for a javascript asset" do
19
+ let(:response) { Stylo::Response.new('javascripts/test.js') }
20
+
21
+ before(:each) do
22
+
23
+ end
24
+
25
+ it "should ask the asset loader to load the javascript content" do
26
+ Stylo::AssetLoader.should_receive(:load_content).with(response.path).and_return(nil)
27
+
28
+ step.call(response)
29
+ end
30
+
31
+ describe "and the asset does not exist" do
32
+ it "should not set the response" do
33
+ Stylo::AssetLoader.stub(:load_content).and_return(nil)
34
+
35
+ step.call(response)
36
+
37
+ response.has_content?.should be_false
38
+ end
39
+ end
40
+
41
+ describe "and the asset exists" do
42
+ let(:javascript_content) { '///require "child.js";' }
43
+ let(:combined_javascript_content) { "alert('hello world');" }
44
+ let(:combiner) { mock(:combiner) }
45
+
46
+ before(:each) do
47
+ Stylo::AssetLoader.stub(:load_content).and_return(javascript_content)
48
+ Stylo::Combiner.stub(:new).with('javascripts', /\/\/\/require "(.*)";/).and_return(combiner)
49
+ combiner.stub(:process).with(javascript_content).and_return(combined_javascript_content)
50
+ end
51
+
52
+ it "should tell the combiner to process the javascript content" do
53
+ Stylo::Combiner.should_receive(:new).with('javascripts', /\/\/\/require "(.*)";/).and_return(combiner)
54
+ combiner.should_receive(:process).with(javascript_content).and_return(combined_javascript_content)
55
+
56
+ step.call(response)
57
+ end
58
+
59
+ it "should set the body of the response to the combined javascript content" do
60
+ step.call(response)
61
+
62
+ response.body.should == combined_javascript_content
63
+ end
64
+
65
+ it "should set the content type to text/javascript" do
66
+ step.call(response)
67
+
68
+ response.headers['Content-Type'].should == 'text/javascript'
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "when the response has already been set" do
75
+ it "should not alter the response" do
76
+ response = Stylo::Response.new('javascripts/test.js')
77
+ response.set_body('some-content', :css)
78
+
79
+ step.call(response)
80
+
81
+ response.body.should == 'some-content'
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylo::PipelineSteps::Sass do
4
+ let(:step) { Stylo::PipelineSteps::Sass.new }
5
+
6
+ describe "when the response has not already been set" do
7
+ describe "and the request is not for a stylesheet" do
8
+ it "should not set the response" do
9
+ response = Stylo::Response.new('javascripts/test.js')
10
+ Stylo::AssetLoader.stub(:load_content).with(response.path).and_return('some-content')
11
+
12
+ step.call(response)
13
+
14
+ response.has_content?.should be_false
15
+ end
16
+ end
17
+
18
+ describe "and the request is for a stylesheet" do
19
+ let(:requested_path) { 'stylesheets/test.css' }
20
+ let(:sass_path) { 'stylesheets/test.scss' }
21
+ let(:response) { Stylo::Response.new(requested_path) }
22
+
23
+ it "should ask the asset loader to load the sass stylesheet content" do
24
+ Stylo::AssetLoader.should_receive(:load_content).with(sass_path).and_return(nil)
25
+
26
+ step.call(response)
27
+ end
28
+
29
+ describe "and the asset does not exist" do
30
+ it "should not set the response" do
31
+ Stylo::AssetLoader.stub(:load_content).and_return(nil)
32
+
33
+ step.call(response)
34
+
35
+ response.has_content?.should be_false
36
+ end
37
+ end
38
+
39
+ describe "and the asset exists" do
40
+ let(:stylesheet_content) { "html { color: $color; }" }
41
+ let(:combined_stylesheet_content) { "html { color: $red; }" }
42
+ let(:processed_content) { "html { color: red; }" }
43
+ let(:sass_engine) { mock(:sass_engine) }
44
+ let(:combiner) { mock(:combiner) }
45
+
46
+ before(:each) do
47
+ Stylo::AssetLoader.stub(:load_content).and_return(stylesheet_content)
48
+ Stylo::Combiner.stub(:new).with('stylesheets', /@import "(.*)";/).and_return(combiner)
49
+ combiner.stub(:process).with(stylesheet_content).and_return(combined_stylesheet_content)
50
+ ::Sass::Engine.stub(:new).with(combined_stylesheet_content, {:syntax => :scss}).and_return(sass_engine)
51
+ sass_engine.stub(:render).and_return(processed_content)
52
+ end
53
+
54
+ it "should tell the combiner to process the stylesheet content" do
55
+ Stylo::Combiner.should_receive(:new).with('stylesheets', /@import "(.*)";/).and_return(combiner)
56
+ combiner.should_receive(:process).with(stylesheet_content).and_return(combined_stylesheet_content)
57
+
58
+ step.call(response)
59
+ end
60
+
61
+ it "should run the combined content through sass" do
62
+ ::Sass::Engine.should_receive(:new).with(combined_stylesheet_content, {:syntax => :scss}).and_return(sass_engine)
63
+ sass_engine.should_receive(:render).and_return(processed_content)
64
+
65
+ step.call(response)
66
+ end
67
+
68
+ it "should set the body of the response to the combined stylesheet content" do
69
+ step.call(response)
70
+
71
+ response.body.should == processed_content
72
+ end
73
+
74
+ it "should set the content type to text/css" do
75
+ step.call(response)
76
+
77
+ response.headers['Content-Type'].should == 'text/css'
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "when the response has already been set" do
84
+ it "should not alter the response" do
85
+ response = Stylo::Response.new('stylesheets/test.css')
86
+ response.set_body('some-content', :css)
87
+
88
+ step.call(response)
89
+
90
+ response.body.should == 'some-content'
91
+ end
92
+ end
93
+
94
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylo::PipelineSteps::Stylesheet do
4
+ let(:step) { Stylo::PipelineSteps::Stylesheet.new }
5
+
6
+ describe "when the response has not already been set" do
7
+ describe "and the request is not for a stylesheet" do
8
+ it "should not set the response" do
9
+ response = Stylo::Response.new('javascripts/test.js')
10
+ Stylo::AssetLoader.stub(:load_content).with(response.path).and_return('some-content')
11
+
12
+ step.call(response)
13
+
14
+ response.has_content?.should be_false
15
+ end
16
+ end
17
+
18
+ describe "and the request is for a stylesheet" do
19
+ let(:response) { Stylo::Response.new('stylesheets/test.css') }
20
+
21
+ before(:each) do
22
+
23
+ end
24
+
25
+ it "should ask the asset loader to load the stylesheet content" do
26
+ Stylo::AssetLoader.should_receive(:load_content).with(response.path).and_return(nil)
27
+
28
+ step.call(response)
29
+ end
30
+
31
+ describe "and the asset does not exist" do
32
+ it "should not set the response" do
33
+ Stylo::AssetLoader.stub(:load_content).and_return(nil)
34
+
35
+ step.call(response)
36
+
37
+ response.has_content?.should be_false
38
+ end
39
+ end
40
+
41
+ describe "and the asset exists" do
42
+ let(:stylesheet_content) { "html { color: $color; }" }
43
+ let(:combined_stylesheet_content) { "html { color: red; }" }
44
+ let(:combiner) { mock(:combiner) }
45
+
46
+ before(:each) do
47
+ Stylo::AssetLoader.stub(:load_content).and_return(stylesheet_content)
48
+ Stylo::Combiner.stub(:new).with('stylesheets', /@import "(.*)";/).and_return(combiner)
49
+ combiner.stub(:process).with(stylesheet_content).and_return(combined_stylesheet_content)
50
+ end
51
+
52
+ it "should tell the combiner to process the stylesheet content" do
53
+ Stylo::Combiner.should_receive(:new).with('stylesheets', /@import "(.*)";/).and_return(combiner)
54
+ combiner.should_receive(:process).with(stylesheet_content).and_return(combined_stylesheet_content)
55
+
56
+ step.call(response)
57
+ end
58
+
59
+ it "should set the body of the response to the combined stylesheet content" do
60
+ step.call(response)
61
+
62
+ response.body.should == combined_stylesheet_content
63
+ end
64
+
65
+ it "should set the content type to text/css" do
66
+ step.call(response)
67
+
68
+ response.headers['Content-Type'].should == 'text/css'
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "when the response has already been set" do
75
+ it "should not alter the response" do
76
+ response = Stylo::Response.new('stylesheets/test.css')
77
+ response.set_body('some-content', :css)
78
+
79
+ step.call(response)
80
+
81
+ response.body.should == 'some-content'
82
+ end
83
+ end
84
+ end
data/spec/rack_spec.rb ADDED
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Stylo::Rack do
4
+ let(:app) { mock(:app) }
5
+ let(:env) { {} }
6
+
7
+ describe "call" do
8
+ let(:pipeline_steps) { [] }
9
+
10
+ before(:each) do
11
+ app.stub(:call)
12
+
13
+ Stylo::Config.stub(:pipeline).and_return(pipeline_steps)
14
+ end
15
+
16
+ it "should call each pipeline step" do
17
+ 3.times do |i|
18
+ step = mock("pipeline step #{i}")
19
+ step.should_receive(:call).with(instance_of(Stylo::Response))
20
+ pipeline_steps << step
21
+ end
22
+
23
+ Stylo::Rack.new(app).call(env)
24
+ end
25
+
26
+ describe "when no step has been able to deal with the request" do
27
+ it "should call back into the app" do
28
+ app.should_receive(:call).with(env)
29
+
30
+ Stylo::Rack.new(app).call(env)
31
+ end
32
+ end
33
+
34
+ describe "when a step has been able to deal with the request" do
35
+ let(:response) { mock(:response, :has_content? => true) }
36
+
37
+ before(:each) do
38
+ Stylo::Response.stub(:new).and_return(response)
39
+ end
40
+
41
+ it "should not call back into the app" do
42
+ app.should_not_receive(:call).with(env)
43
+ response.stub(:build)
44
+
45
+ Stylo::Rack.new(app).call(env)
46
+ end
47
+
48
+ it "should return the built response" do
49
+ response.stub(:build).and_return([200, {}, "some-content"])
50
+
51
+ Stylo::Rack.new(app).call(env).should == response.build
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def status_code(response)
58
+ response[0]
59
+ end
60
+
61
+ def content_type(response)
62
+ response[1]['Content-Type']
63
+ end
64
+
65
+ def content_length(response)
66
+ response[1]['Content-Length']
67
+ end
68
+
69
+ def cache_control(response)
70
+ response[1]['Cache-Control']
71
+ end
72
+
73
+ def content(response)
74
+ response[2]
75
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'stylo'
7
+ require 'stylo_spec_helpers'
8
+
9
+ RSpec.configure do |config|
10
+ config.include StyloSpecHelpers
11
+ config.include FileUtils
12
+
13
+ config.before(:each) do
14
+ reset_paths
15
+ end
16
+ end
17
+
18
+
@@ -0,0 +1,30 @@
1
+ module StyloSpecHelpers
2
+ def temp_path(path = '')
3
+ File.expand_path(File.join(File.dirname(__FILE__), '../tmp', path))
4
+ end
5
+
6
+ def fixture_path(path)
7
+ File.expand_path(File.join(File.dirname(__FILE__), '../features/fixtures', path))
8
+ end
9
+
10
+ def load_fixture(path)
11
+ File.read fixture_path(path)
12
+ end
13
+
14
+ def write_content(path, content)
15
+ File.open(path, 'w') do |f|
16
+ f.write(content)
17
+ end
18
+ end
19
+
20
+ def reset_paths
21
+ @stylesheets_path = temp_path('stylesheets')
22
+ @javascripts_path = temp_path('javascripts')
23
+ [@stylesheets_path, @javascripts_path].each do |path|
24
+ rm_rf path
25
+ mkdir_p path
26
+ end
27
+
28
+ Stylo::Config.asset_location = temp_path()
29
+ end
30
+ end
data/stylo.gemspec ADDED
@@ -0,0 +1,116 @@
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{stylo}
8
+ s.version = "0.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["mwagg"]
12
+ s.date = %q{2010-10-18}
13
+ s.description = %q{Server side stylesheet combining for readonly hosting environments}
14
+ s.email = %q{michael@guerillatactics.co.uk}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc",
18
+ "TODO"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "TODO",
27
+ "cucumber.yml",
28
+ "features/fixtures.rb",
29
+ "features/fixtures/child.css",
30
+ "features/fixtures/child.js",
31
+ "features/fixtures/grand_parent.css",
32
+ "features/fixtures/grand_parent.js",
33
+ "features/fixtures/grand_parent_with_parent_with_child.css",
34
+ "features/fixtures/grand_parent_with_parent_with_child.js",
35
+ "features/fixtures/parent.css",
36
+ "features/fixtures/parent.js",
37
+ "features/fixtures/parent_with_child.css",
38
+ "features/fixtures/parent_with_child.js",
39
+ "features/fixtures/processed_sass_child.css",
40
+ "features/fixtures/processed_sass_which_uses_mixin.css",
41
+ "features/fixtures/sass_child.scss",
42
+ "features/fixtures/sass_mixins.scss",
43
+ "features/fixtures/sass_which_uses_mixin.scss",
44
+ "features/javascripts.feature",
45
+ "features/response_headers.feature",
46
+ "features/sass_integration.feature",
47
+ "features/step_definitions/stylo_steps.rb",
48
+ "features/stylesheets.feature",
49
+ "features/stylo_cannot_serve_asset.feature",
50
+ "features/support/env.rb",
51
+ "lib/stylo.rb",
52
+ "lib/stylo/asset_loader.rb",
53
+ "lib/stylo/combiner.rb",
54
+ "lib/stylo/config.rb",
55
+ "lib/stylo/pipeline_steps/caching.rb",
56
+ "lib/stylo/pipeline_steps/javascript.rb",
57
+ "lib/stylo/pipeline_steps/sass.rb",
58
+ "lib/stylo/pipeline_steps/stylesheet.rb",
59
+ "lib/stylo/processor.rb",
60
+ "lib/stylo/rack.rb",
61
+ "lib/stylo/railtie.rb",
62
+ "lib/stylo/response.rb",
63
+ "spec/asset_loader_spec.rb",
64
+ "spec/combiner_spec.rb",
65
+ "spec/pipeline_steps/caching_spec.rb",
66
+ "spec/pipeline_steps/javascript_spec.rb",
67
+ "spec/pipeline_steps/sass_spec.rb",
68
+ "spec/pipeline_steps/stylesheet_spec.rb",
69
+ "spec/rack_spec.rb",
70
+ "spec/spec_helper.rb",
71
+ "spec/stylo_spec_helpers.rb",
72
+ "stylo.gemspec"
73
+ ]
74
+ s.homepage = %q{http://github.com/mwagg/stylo}
75
+ s.rdoc_options = ["--charset=UTF-8"]
76
+ s.require_paths = ["lib"]
77
+ s.rubygems_version = %q{1.3.7}
78
+ s.summary = %q{Server side stylesheet combining for readonly hosting environments}
79
+ s.test_files = [
80
+ "spec/asset_loader_spec.rb",
81
+ "spec/combiner_spec.rb",
82
+ "spec/pipeline_steps/caching_spec.rb",
83
+ "spec/pipeline_steps/javascript_spec.rb",
84
+ "spec/pipeline_steps/sass_spec.rb",
85
+ "spec/pipeline_steps/stylesheet_spec.rb",
86
+ "spec/rack_spec.rb",
87
+ "spec/spec_helper.rb",
88
+ "spec/stylo_spec_helpers.rb"
89
+ ]
90
+
91
+ if s.respond_to? :specification_version then
92
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
93
+ s.specification_version = 3
94
+
95
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
96
+ s.add_development_dependency(%q<rspec>, [">= 0"])
97
+ s.add_development_dependency(%q<cucumber>, [">= 0"])
98
+ s.add_development_dependency(%q<rack-test>, [">= 0"])
99
+ s.add_development_dependency(%q<sinatra>, [">= 0"])
100
+ s.add_runtime_dependency(%q<haml>, [">= 3.0.21"])
101
+ else
102
+ s.add_dependency(%q<rspec>, [">= 0"])
103
+ s.add_dependency(%q<cucumber>, [">= 0"])
104
+ s.add_dependency(%q<rack-test>, [">= 0"])
105
+ s.add_dependency(%q<sinatra>, [">= 0"])
106
+ s.add_dependency(%q<haml>, [">= 3.0.21"])
107
+ end
108
+ else
109
+ s.add_dependency(%q<rspec>, [">= 0"])
110
+ s.add_dependency(%q<cucumber>, [">= 0"])
111
+ s.add_dependency(%q<rack-test>, [">= 0"])
112
+ s.add_dependency(%q<sinatra>, [">= 0"])
113
+ s.add_dependency(%q<haml>, [">= 3.0.21"])
114
+ end
115
+ end
116
+