jellyfish 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGES.md +23 -0
  2. data/Gemfile +7 -0
  3. data/README.md +2 -2
  4. data/example/config.ru +11 -0
  5. data/jellyfish.gemspec +48 -3
  6. data/lib/jellyfish.rb +21 -25
  7. data/lib/jellyfish/test.rb +22 -0
  8. data/lib/jellyfish/version.rb +1 -1
  9. data/sinatra/builder_test.rb +95 -0
  10. data/sinatra/coffee_test.rb +92 -0
  11. data/sinatra/contest.rb +98 -0
  12. data/sinatra/creole_test.rb +65 -0
  13. data/sinatra/delegator_test.rb +162 -0
  14. data/sinatra/encoding_test.rb +20 -0
  15. data/sinatra/erb_test.rb +104 -0
  16. data/sinatra/extensions_test.rb +100 -0
  17. data/sinatra/filter_test.rb +428 -0
  18. data/sinatra/haml_test.rb +101 -0
  19. data/sinatra/helper.rb +123 -0
  20. data/sinatra/helpers_test.rb +1783 -0
  21. data/sinatra/integration/app.rb +62 -0
  22. data/sinatra/integration_helper.rb +214 -0
  23. data/sinatra/integration_test.rb +85 -0
  24. data/sinatra/less_test.rb +67 -0
  25. data/sinatra/liquid_test.rb +59 -0
  26. data/sinatra/mapped_error_test.rb +259 -0
  27. data/sinatra/markaby_test.rb +80 -0
  28. data/sinatra/markdown_test.rb +81 -0
  29. data/sinatra/middleware_test.rb +68 -0
  30. data/sinatra/nokogiri_test.rb +69 -0
  31. data/sinatra/rack_test.rb +45 -0
  32. data/sinatra/radius_test.rb +59 -0
  33. data/sinatra/rdoc_test.rb +66 -0
  34. data/sinatra/readme_test.rb +136 -0
  35. data/sinatra/request_test.rb +45 -0
  36. data/sinatra/response_test.rb +61 -0
  37. data/sinatra/result_test.rb +98 -0
  38. data/sinatra/route_added_hook_test.rb +59 -0
  39. data/sinatra/routing_test.rb +1104 -0
  40. data/sinatra/sass_test.rb +116 -0
  41. data/sinatra/scss_test.rb +89 -0
  42. data/sinatra/server_test.rb +48 -0
  43. data/sinatra/settings_test.rb +538 -0
  44. data/sinatra/sinatra_test.rb +17 -0
  45. data/sinatra/slim_test.rb +88 -0
  46. data/sinatra/static_test.rb +178 -0
  47. data/sinatra/streaming_test.rb +140 -0
  48. data/sinatra/templates_test.rb +298 -0
  49. data/sinatra/textile_test.rb +65 -0
  50. data/test/sinatra/test_base.rb +123 -0
  51. metadata +48 -3
@@ -0,0 +1,62 @@
1
+ $stderr.puts "loading"
2
+ require 'sinatra'
3
+
4
+ configure do
5
+ set :foo, :bar
6
+ end
7
+
8
+ get '/app_file' do
9
+ content_type :txt
10
+ settings.app_file
11
+ end
12
+
13
+ get '/ping' do
14
+ 'pong'
15
+ end
16
+
17
+ get '/stream' do
18
+ stream do |out|
19
+ sleep 0.1
20
+ out << "a"
21
+ sleep 1.2
22
+ out << "b"
23
+ end
24
+ end
25
+
26
+ get '/mainonly' do
27
+ object = Object.new
28
+ begin
29
+ object.send(:get, '/foo') { }
30
+ 'false'
31
+ rescue NameError
32
+ 'true'
33
+ end
34
+ end
35
+
36
+ set :out, nil
37
+ get '/async' do
38
+ stream(:keep_open) { |o| (settings.out = o) << "hi!" }
39
+ end
40
+
41
+ get '/send' do
42
+ settings.out << params[:msg] if params[:msg]
43
+ settings.out.close if params[:close]
44
+ "ok"
45
+ end
46
+
47
+ class Subclass < Sinatra::Base
48
+ set :out, nil
49
+ get '/subclass/async' do
50
+ stream(:keep_open) { |o| (settings.out = o) << "hi!" }
51
+ end
52
+
53
+ get '/subclass/send' do
54
+ settings.out << params[:msg] if params[:msg]
55
+ settings.out.close if params[:close]
56
+ "ok"
57
+ end
58
+ end
59
+
60
+ use Subclass
61
+
62
+ $stderr.puts "starting"
@@ -0,0 +1,214 @@
1
+ require 'sinatra/base'
2
+ require 'rbconfig'
3
+ require 'open-uri'
4
+ require 'net/http'
5
+ require 'timeout'
6
+
7
+ module IntegrationHelper
8
+ class BaseServer
9
+ extend Enumerable
10
+ attr_accessor :server, :port, :pipe
11
+ alias name server
12
+
13
+ def self.all
14
+ @all ||= []
15
+ end
16
+
17
+ def self.each(&block)
18
+ all.each(&block)
19
+ end
20
+
21
+ def self.run(server, port)
22
+ new(server, port).run
23
+ end
24
+
25
+ def app_file
26
+ File.expand_path('../integration/app.rb', __FILE__)
27
+ end
28
+
29
+ def environment
30
+ "development"
31
+ end
32
+
33
+ def initialize(server, port)
34
+ @installed, @pipe, @server, @port = nil, nil, server, port
35
+ Server.all << self
36
+ end
37
+
38
+ def run
39
+ return unless installed?
40
+ kill
41
+ @log = ""
42
+ @pipe = IO.popen(command)
43
+ @started = Time.now
44
+ warn "#{server} up and running on port #{port}" if ping
45
+ at_exit { kill }
46
+ end
47
+
48
+ def ping(timeout = 30)
49
+ loop do
50
+ return if alive?
51
+ if Time.now - @started > timeout
52
+ $stderr.puts command, log
53
+ fail "timeout"
54
+ else
55
+ sleep 0.1
56
+ end
57
+ end
58
+ end
59
+
60
+ def alive?
61
+ 3.times { get('/ping') }
62
+ true
63
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET, EOFError, SystemCallError, OpenURI::HTTPError, Timeout::Error => error
64
+ false
65
+ end
66
+
67
+ def get_stream(url = "/stream", &block)
68
+ Net::HTTP.start '127.0.0.1', port do |http|
69
+ request = Net::HTTP::Get.new url
70
+ http.request request do |response|
71
+ response.read_body(&block)
72
+ end
73
+ end
74
+ end
75
+
76
+ def get(url)
77
+ Timeout.timeout(1) { open("http://127.0.0.1:#{port}#{url}").read }
78
+ end
79
+
80
+ def log
81
+ @log ||= ""
82
+ loop { @log << @pipe.read_nonblock(1) }
83
+ rescue Exception
84
+ @log
85
+ end
86
+
87
+ def installed?
88
+ return @installed unless @installed.nil?
89
+ require server
90
+ @installed = true
91
+ rescue LoadError
92
+ warn "#{server} is not installed, skipping integration tests"
93
+ @installed = false
94
+ end
95
+
96
+ def command
97
+ @command ||= begin
98
+ cmd = ["RACK_ENV=#{environment}", "exec"]
99
+ if RbConfig.respond_to? :ruby
100
+ cmd << RbConfig.ruby.inspect
101
+ else
102
+ file, dir = RbConfig::CONFIG.values_at('ruby_install_name', 'bindir')
103
+ cmd << File.expand_path(file, dir).inspect
104
+ end
105
+ cmd << "-w" unless thin?
106
+ cmd << "-I" << File.expand_path('../../lib', __FILE__).inspect
107
+ cmd << app_file.inspect << '-s' << server << '-o' << '127.0.0.1' << '-p' << port
108
+ cmd << "-e" << environment.to_s << '2>&1'
109
+ cmd.join " "
110
+ end
111
+ end
112
+
113
+ def kill
114
+ return unless pipe
115
+ Process.kill("KILL", pipe.pid)
116
+ rescue NotImplementedError
117
+ system "kill -9 #{pipe.pid}"
118
+ rescue Errno::ESRCH
119
+ end
120
+
121
+ def webrick?
122
+ name.to_s == "webrick"
123
+ end
124
+
125
+ def thin?
126
+ name.to_s == "thin"
127
+ end
128
+
129
+ def warnings
130
+ log.scan(%r[(?:\(eval|lib/sinatra).*warning:.*$])
131
+ end
132
+
133
+ def run_test(target, &block)
134
+ retries ||= 3
135
+ target.server = self
136
+ run unless alive?
137
+ target.instance_eval(&block)
138
+ rescue Exception => error
139
+ retries -= 1
140
+ kill
141
+ retries < 0 ? retry : raise(error)
142
+ end
143
+ end
144
+
145
+ if RUBY_ENGINE == "jruby"
146
+ class JRubyServer < BaseServer
147
+ def start_vm
148
+ require 'java'
149
+ # Create a new container, set load paths and env
150
+ # SINGLETHREAD means create a new runtime
151
+ vm = org.jruby.embed.ScriptingContainer.new(org.jruby.embed.LocalContextScope::SINGLETHREAD)
152
+ vm.load_paths = [File.expand_path('../../lib', __FILE__)]
153
+ vm.environment = ENV.merge('RACK_ENV' => environment.to_s)
154
+
155
+ # This ensures processing of RUBYOPT which activates Bundler
156
+ vm.provider.ruby_instance_config.process_arguments []
157
+ vm.argv = ['-s', server.to_s, '-o', '127.0.0.1', '-p', port.to_s, '-e', environment.to_s]
158
+
159
+ # Set stdout/stderr so we can retrieve log
160
+ @pipe = java.io.ByteArrayOutputStream.new
161
+ vm.output = java.io.PrintStream.new(@pipe)
162
+ vm.error = java.io.PrintStream.new(@pipe)
163
+
164
+ Thread.new do
165
+ # Hack to ensure that Kernel#caller has the same info as
166
+ # when run from command-line, for Sintra::Application.app_file.
167
+ # Also, line numbers are zero-based in JRuby's parser
168
+ vm.provider.runtime.current_context.set_file_and_line(app_file, 0)
169
+ # Run the app
170
+ vm.run_scriptlet org.jruby.embed.PathType::ABSOLUTE, app_file
171
+ # terminate launches at_exit hooks which start server
172
+ vm.terminate
173
+ end
174
+ end
175
+
176
+ def run
177
+ return unless installed?
178
+ kill
179
+ @thread = start_vm
180
+ @started = Time.now
181
+ warn "#{server} up and running on port #{port}" if ping
182
+ at_exit { kill }
183
+ end
184
+
185
+ def log
186
+ String.from_java_bytes @pipe.to_byte_array
187
+ end
188
+
189
+ def kill
190
+ @thread.kill if @thread
191
+ @thread = nil
192
+ end
193
+ end
194
+ Server = JRubyServer
195
+ else
196
+ Server = BaseServer
197
+ end
198
+
199
+ def it(message, &block)
200
+ Server.each do |server|
201
+ next unless server.installed?
202
+ super("with #{server.name}: #{message}") { server.run_test(self, &block) }
203
+ end
204
+ end
205
+
206
+ def self.extend_object(obj)
207
+ super
208
+
209
+ base_port = 5000 + Process.pid % 100
210
+ Sinatra::Base.server.each_with_index do |server, index|
211
+ Server.run(server, 5000+index)
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,85 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+ require File.expand_path('../integration_helper', __FILE__)
3
+
4
+ # These tests start a real server and talk to it over TCP.
5
+ # Every test runs with every detected server.
6
+ #
7
+ # See test/integration/app.rb for the code of the app we test against.
8
+ class IntegrationTest < Test::Unit::TestCase
9
+ extend IntegrationHelper
10
+ attr_accessor :server
11
+
12
+ it('sets the app_file') { assert_equal server.app_file, server.get("/app_file") }
13
+
14
+ it 'logs once in development mode' do
15
+ random = "%064x" % Kernel.rand(2**256-1)
16
+ server.get "/ping?x=#{random}"
17
+ count = server.log.scan("GET /ping?x=#{random}").count
18
+ server.webrick? ? assert(count > 0) : assert_equal(1, count)
19
+ end
20
+
21
+ it 'streams' do
22
+ next if server.webrick?
23
+ times, chunks = [Time.now], []
24
+ server.get_stream do |chunk|
25
+ next if chunk.empty?
26
+ chunks << chunk
27
+ times << Time.now
28
+ end
29
+ assert_equal ["a", "b"], chunks
30
+ assert times[1] - times[0] < 1
31
+ assert times[2] - times[1] > 1
32
+ end
33
+
34
+ it 'streams async' do
35
+ next unless server.thin?
36
+
37
+ Timeout.timeout(3) do
38
+ chunks = []
39
+ server.get_stream '/async' do |chunk|
40
+ next if chunk.empty?
41
+ chunks << chunk
42
+ case chunk
43
+ when "hi!" then server.get "/send?msg=hello"
44
+ when "hello" then server.get "/send?close=1"
45
+ end
46
+ end
47
+
48
+ assert_equal ['hi!', 'hello'], chunks
49
+ end
50
+ end
51
+
52
+ it 'streams async from subclass' do
53
+ next unless server.thin?
54
+
55
+ Timeout.timeout(3) do
56
+ chunks = []
57
+ server.get_stream '/subclass/async' do |chunk|
58
+ next if chunk.empty?
59
+ chunks << chunk
60
+ case chunk
61
+ when "hi!" then server.get "/subclass/send?msg=hello"
62
+ when "hello" then server.get "/subclass/send?close=1"
63
+ end
64
+ end
65
+
66
+ assert_equal ['hi!', 'hello'], chunks
67
+ end
68
+ end
69
+
70
+ it 'starts the correct server' do
71
+ exp = %r{
72
+ ==\sSinatra/#{Sinatra::VERSION}\s
73
+ has\staken\sthe\sstage\son\s\d+\sfor\sdevelopment\s
74
+ with\sbackup\sfrom\s#{server}
75
+ }ix
76
+
77
+ assert_match exp, server.log
78
+ end
79
+
80
+ it 'does not generate warnings' do
81
+ assert_raise(OpenURI::HTTPError) { server.get '/' }
82
+ server.get '/app_file'
83
+ assert_equal [], server.warnings
84
+ end
85
+ end
@@ -0,0 +1,67 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ begin
4
+ require 'less'
5
+
6
+ class LessTest < Test::Unit::TestCase
7
+ def less_app(options = {}, &block)
8
+ mock_app {
9
+ set :views, File.dirname(__FILE__) + '/views'
10
+ set options
11
+ get '/', &block
12
+ }
13
+ get '/'
14
+ end
15
+
16
+ it 'renders inline Less strings' do
17
+ less_app { less "@white_color: #fff; #main { background-color: @white_color }" }
18
+ assert ok?
19
+ assert_equal "#main{background-color:#ffffff;}", body.gsub(/\s/, "")
20
+ end
21
+
22
+ it 'defaults content type to css' do
23
+ less_app { less "@white_color: #fff; #main { background-color: @white_color }" }
24
+ assert ok?
25
+ assert_equal "text/css;charset=utf-8", response['Content-Type']
26
+ end
27
+
28
+ it 'defaults allows setting content type per route' do
29
+ less_app do
30
+ content_type :html
31
+ less "@white_color: #fff; #main { background-color: @white_color }"
32
+ end
33
+ assert ok?
34
+ assert_equal "text/html;charset=utf-8", response['Content-Type']
35
+ end
36
+
37
+ it 'defaults allows setting content type globally' do
38
+ less_app(:less => { :content_type => 'html' }) do
39
+ less "@white_color: #fff; #main { background-color: @white_color }"
40
+ end
41
+ assert ok?
42
+ assert_equal "text/html;charset=utf-8", response['Content-Type']
43
+ end
44
+
45
+ it 'renders .less files in views path' do
46
+ less_app { less :hello }
47
+ assert ok?
48
+ assert_equal "#main{background-color:#ffffff;}", body.gsub(/\s/, "")
49
+ end
50
+
51
+ it 'ignores the layout option' do
52
+ less_app { less :hello, :layout => :layout2 }
53
+ assert ok?
54
+ assert_equal "#main{background-color:#ffffff;}", body.gsub(/\s/, "")
55
+ end
56
+
57
+ it "raises error if template not found" do
58
+ mock_app {
59
+ get('/') { less :no_such_template }
60
+ }
61
+ assert_raise(Errno::ENOENT) { get('/') }
62
+ end
63
+ end
64
+
65
+ rescue LoadError
66
+ warn "#{$!.to_s}: skipping less tests"
67
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ begin
4
+ require 'liquid'
5
+
6
+ class LiquidTest < Test::Unit::TestCase
7
+ def liquid_app(&block)
8
+ mock_app do
9
+ set :views, File.dirname(__FILE__) + '/views'
10
+ get '/', &block
11
+ end
12
+ get '/'
13
+ end
14
+
15
+ it 'renders inline liquid strings' do
16
+ liquid_app { liquid '<h1>Hiya</h1>' }
17
+ assert ok?
18
+ assert_equal "<h1>Hiya</h1>", body
19
+ end
20
+
21
+ it 'renders .liquid files in views path' do
22
+ liquid_app { liquid :hello }
23
+ assert ok?
24
+ assert_equal "<h1>Hello From Liquid</h1>\n", body
25
+ end
26
+
27
+ it "renders with inline layouts" do
28
+ mock_app do
29
+ layout { "<h1>THIS. IS. {{ yield }}</h1>" }
30
+ get('/') { liquid '<EM>SPARTA</EM>' }
31
+ end
32
+ get '/'
33
+ assert ok?
34
+ assert_equal "<h1>THIS. IS. <EM>SPARTA</EM></h1>", body
35
+ end
36
+
37
+ it "renders with file layouts" do
38
+ liquid_app { liquid 'Hello World', :layout => :layout2 }
39
+ assert ok?
40
+ assert_equal "<h1>Liquid Layout!</h1>\n<p>Hello World</p>\n", body
41
+ end
42
+
43
+ it "raises error if template not found" do
44
+ mock_app { get('/') { liquid :no_such_template } }
45
+ assert_raise(Errno::ENOENT) { get('/') }
46
+ end
47
+
48
+ it "allows passing locals" do
49
+ liquid_app do
50
+ liquid '{{ value }}', :locals => { :value => 'foo' }
51
+ end
52
+ assert ok?
53
+ assert_equal 'foo', body
54
+ end
55
+ end
56
+
57
+ rescue LoadError
58
+ warn "#{$!.to_s}: skipping liquid tests"
59
+ end