goliath 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of goliath might be problematic. Click here for more details.

data/HISTORY.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # HISTORY
2
2
 
3
+ # v1.0.1 (November 8, 2012)
4
+
5
+ - integrated console (CLI flag)
6
+ - log shutdown sequence
7
+
3
8
  ## v1.0.0 (August 11, 2012)
4
9
 
5
10
  - Improved WebSocket handling
@@ -5,7 +5,6 @@ require 'goliath'
5
5
  require 'yajl' if RUBY_PLATFORM != 'java'
6
6
 
7
7
  class AsyncUpload < Goliath::API
8
- use Goliath::Rack::Params # parse & merge query and body parameters
9
8
  use Goliath::Rack::DefaultMimeType # cleanup accepted media types
10
9
  use Goliath::Rack::Render, 'json' # auto-negotiate response format
11
10
 
data/examples/stream.rb CHANGED
@@ -2,9 +2,9 @@
2
2
  $:<< '../lib' << 'lib'
3
3
 
4
4
  #
5
- # A simple HTTP streaming API which returns a 200 response for any GET request
6
- # and then emits numbers 1 through 10 in 1 second intervals, and then closes the
7
- # connection.
5
+ # A simple HTTP streaming API which returns a 202 (Accepted) response for any
6
+ # GET request and then emits numbers 1 through 10 in 1 second intervals, and
7
+ # then closes the connection.
8
8
  #
9
9
  # A good use case for this pattern would be to provide a stream of updates or a
10
10
  # 'firehose' like API to stream data back to the clients. Simply hook up to your
data/examples/template.rb CHANGED
@@ -40,8 +40,7 @@ class Template < Goliath::API
40
40
 
41
41
  def response(env)
42
42
  case env['PATH_INFO']
43
- # TODO(dj2): change /root -> / when rack > 1.4.0 is released
44
- when '/root' then [200, {}, haml(:root)]
43
+ when '/' then [200, {}, haml(:root)]
45
44
  when '/haml_str' then [200, {}, haml("%h1 Header")]
46
45
  when '/debug' then [200, {}, haml(:debug)]
47
46
  when '/oops' then [200, {}, haml(:no_such_template)] # will 500
data/goliath.gemspec CHANGED
@@ -57,7 +57,7 @@ Gem::Specification.new do |s|
57
57
  s.add_development_dependency 'rb-fsevent'
58
58
  end
59
59
 
60
- ignores = File.readlines(".gitignore").grep(/\S+/).map {|i| i.chomp }
60
+ ignores = File.readlines(".gitignore").grep(/\S+/).map {|i| i.chomp }.map {|i| File.directory?(i) ? i.sub(/\/?$/, '/*') : i }
61
61
  dotfiles = [".gemtest", ".gitignore", ".rspec", ".yardopts"]
62
62
 
63
63
  s.files = Dir["**/*"].reject {|f| File.directory?(f) || ignores.any? {|i| File.fnmatch(i, f) } } + dotfiles
@@ -7,7 +7,7 @@ module Goliath
7
7
  # handler to run the server.
8
8
  #
9
9
  # @private
10
- class Application
10
+ module Application
11
11
  # Most of this stuff is straight out of sinatra.
12
12
 
13
13
  # Set of caller regex's to be skipped when looking for our API file
@@ -22,15 +22,17 @@ module Goliath
22
22
  # @todo add rubinius (and hopefully other VM impls) ignore patterns ...
23
23
  CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS) if defined?(RUBY_IGNORE_CALLERS)
24
24
 
25
+ module_function
26
+
25
27
  # Like Kernel#caller but excluding certain magic entries and without
26
28
  # line / method information; the resulting array contains filenames only.
27
- def self.caller_files
29
+ def caller_files
28
30
  caller_locations.map { |file, line| file }
29
31
  end
30
32
 
31
33
  # Like caller_files, but containing Arrays rather than strings with the
32
34
  # first element being the file, and the second being the line.
33
- def self.caller_locations
35
+ def caller_locations
34
36
  caller(1).
35
37
  map { |line| line.split(/:(?=\d|in )/)[0,2] }.
36
38
  reject { |file, line| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
@@ -39,7 +41,7 @@ module Goliath
39
41
  # Find the app_file that was used to execute the application
40
42
  #
41
43
  # @return [String] The app file
42
- def self.app_file
44
+ def app_file
43
45
  c = caller_files.first
44
46
  c = $0 if !c || c.empty?
45
47
  c
@@ -48,7 +50,7 @@ module Goliath
48
50
  # Returns the userland class which inherits the Goliath API
49
51
  #
50
52
  # @return [String] The app class
51
- def self.app_class
53
+ def app_class
52
54
  @app_class
53
55
  end
54
56
 
@@ -56,7 +58,7 @@ module Goliath
56
58
  #
57
59
  # @param app_class [String|Symbol|Constant] The new app class
58
60
  # @return [String] app_class The new app class
59
- def self.app_class=(app_class)
61
+ def app_class=(app_class)
60
62
  @app_class = app_class.to_s
61
63
  end
62
64
 
@@ -68,7 +70,7 @@ module Goliath
68
70
  #
69
71
  # @param args [Array] Any arguments to append to the path
70
72
  # @return [String] path for the given arguments
71
- def self.app_path(*args)
73
+ def app_path(*args)
72
74
  @app_path ||= File.expand_path(File.dirname(app_file))
73
75
  File.join(@app_path, *args)
74
76
  end
@@ -77,7 +79,7 @@ module Goliath
77
79
  #
78
80
  # @param args [Array] Any arguments to append to the path
79
81
  # @return [String] path for the given arguments
80
- def self.root_path(*args)
82
+ def root_path(*args)
81
83
  return app_path(args) if Goliath.env?(:test)
82
84
 
83
85
  @root_path ||= File.expand_path("./")
@@ -87,7 +89,7 @@ module Goliath
87
89
  # Execute the application
88
90
  #
89
91
  # @return [Nil]
90
- def self.run!
92
+ def run!
91
93
  unless @app_class
92
94
  file = File.basename(app_file, '.rb')
93
95
  @app_class = camel_case(file)
@@ -115,7 +117,7 @@ module Goliath
115
117
  #
116
118
  # @param str [String] The string to convert
117
119
  # @return [String] The camel cased string
118
- def self.camel_case(str)
120
+ def camel_case(str)
119
121
  return str if str !~ /_/ && str =~ /[A-Z]+.*/
120
122
 
121
123
  str.split('_').map { |e| e.capitalize }.join
@@ -0,0 +1,20 @@
1
+ module Goliath
2
+ # The console execution class for Goliath. This will load a REPL inside of a
3
+ # running reactor with the associated Goliath config loaded.
4
+ #
5
+ # @private
6
+ class Console
7
+ # Starts the reactor and the REPL.
8
+ #
9
+ # @return [Nil]
10
+ def self.run!(server)
11
+ require 'irb'
12
+ EM.synchrony do
13
+ server.load_config
14
+ Object.send(:define_method, :goliath_server) { server }
15
+ IRB.start
16
+ EM.stop
17
+ end
18
+ end
19
+ end
20
+ end
@@ -69,7 +69,7 @@ module Goliath
69
69
  # This method is invoked only once per request.
70
70
  #
71
71
  # @param h [Hash] Request headers
72
- # @param parser [Http::Parser] The parsed used to parse the request
72
+ # @param parser [Http::Parser] The parser used to parse the request
73
73
  # @return [Nil]
74
74
  def parse_header(h, parser)
75
75
  h.each do |k, v|
@@ -174,7 +174,7 @@ module Goliath
174
174
  # Sending of the data is deferred until the request
175
175
  # is marked as ready to push data by the connection.
176
176
  # Hence, two pipelined requests can come in via same
177
- # connection, first can take take 1s to render, while
177
+ # connection, first can take 1s to render, while
178
178
  # second may take 0.5. Because HTTP spec does not
179
179
  # allow for interleaved data exchange, we block the
180
180
  # second request until the first one is done and the
@@ -1,5 +1,6 @@
1
1
  require 'goliath/goliath'
2
2
  require 'goliath/server'
3
+ require 'goliath/console'
3
4
  require 'optparse'
4
5
  require 'log4r'
5
6
 
@@ -19,7 +20,7 @@ module Goliath
19
20
  # For more detail, see: https://github.com/postrank-labs/goliath/issues/18
20
21
  class EnvironmentParser
21
22
 
22
- # Work out the current runtime environemnt.
23
+ # Work out the current runtime environment.
23
24
  #
24
25
  # The sources of environment, in increasing precedence, are:
25
26
  #
@@ -29,7 +30,7 @@ module Goliath
29
30
  # 4. Hard-coded call to Goliath#env=
30
31
  #
31
32
  # @param argv [Array] The command line arguments
32
- # @return [Symbol] The current environemnt
33
+ # @return [Symbol] The current environment
33
34
  def self.parse(argv = [])
34
35
  env = ENV["RACK_ENV"] || Goliath::DEFAULT_ENV
35
36
  if (i = argv.index('-e')) || (i = argv.index('--environment'))
@@ -102,7 +103,7 @@ module Goliath
102
103
  api.options_parser(options_parser, options) if api
103
104
  options_parser.parse!(argv)
104
105
 
105
- # We've already dealt with the environemnt, so just discard it.
106
+ # We've already dealt with the environment, so just discard it.
106
107
  options.delete(:env)
107
108
 
108
109
  @api = api
@@ -167,6 +168,7 @@ module Goliath
167
168
  opts.separator ""
168
169
  opts.separator "Common options:"
169
170
 
171
+ opts.on('-C', '--console', 'Start a console') { @options[:console] = true }
170
172
  opts.on('-v', '--verbose', "Enable verbose logging (default: #{@options[:verbose]})") { |v| @options[:verbose] = v }
171
173
  opts.on('-h', '--help', 'Display help message') { show_options(opts) }
172
174
  end
@@ -185,6 +187,11 @@ module Goliath
185
187
  #
186
188
  # @return [Nil]
187
189
  def run
190
+ if options[:console]
191
+ Goliath::Console.run!(setup_server)
192
+ return
193
+ end
194
+
188
195
  unless Goliath.env?(:test)
189
196
  $LOADED_FEATURES.unshift(File.basename($0))
190
197
  Dir.chdir(File.expand_path(File.dirname($0)))
@@ -241,6 +248,20 @@ module Goliath
241
248
  log
242
249
  end
243
250
 
251
+ # Sets up the Goliath server
252
+ #
253
+ # @param log [Logger] The logger to configure the server to log to
254
+ # @return [Server] an instance of a Goliath server
255
+ def setup_server(log = setup_logger)
256
+ server = Goliath::Server.new(@address, @port)
257
+ server.logger = log
258
+ server.app = @app
259
+ server.api = @api
260
+ server.plugins = @plugins || []
261
+ server.options = @server_options
262
+ server
263
+ end
264
+
244
265
  # Setup file logging
245
266
  #
246
267
  # @param log [Logger] The logger to add file logging too
@@ -271,12 +292,7 @@ module Goliath
271
292
 
272
293
  log.info("Starting server on #{@address}:#{@port} in #{Goliath.env} mode. Watch out for stones.")
273
294
 
274
- server = Goliath::Server.new(@address, @port)
275
- server.logger = log
276
- server.app = @app
277
- server.api = @api
278
- server.plugins = @plugins || []
279
- server.options = @server_options
295
+ server = setup_server(log)
280
296
  server.start
281
297
  end
282
298
 
@@ -71,8 +71,8 @@ module Goliath
71
71
  def start(&blk)
72
72
  EM.epoll
73
73
  EM.synchrony do
74
- trap("INT") { EM.stop }
75
- trap("TERM") { EM.stop }
74
+ trap("INT") { stop }
75
+ trap("TERM") { stop }
76
76
 
77
77
  if RUBY_PLATFORM !~ /mswin|mingw/
78
78
  trap("HUP") { load_config(options[:config]) }
@@ -105,6 +105,12 @@ module Goliath
105
105
  end
106
106
  end
107
107
 
108
+ # Stops the server running.
109
+ def stop
110
+ logger.info('Stopping server...')
111
+ EM.stop
112
+ end
113
+
108
114
  # Loads a configuration file
109
115
  #
110
116
  # @param file [String] The file to load, if not set will use the basename of $0
@@ -0,0 +1,42 @@
1
+ require 'em-http-request'
2
+
3
+ module Goliath
4
+ module TestHelper
5
+ class StreamingHelper
6
+ attr_reader :connection
7
+ def initialize(url)
8
+ @queue = EM::Queue.new
9
+
10
+ fiber = Fiber.current
11
+ @connection = EventMachine::HttpRequest.new(url).get
12
+ @connection.errback do |e|
13
+ puts "Error encountered during connection: #{e}"
14
+ EM::stop_event_loop
15
+ end
16
+
17
+ @connection.callback { EM::stop_event_loop }
18
+
19
+ @connection.stream { |m| @queue.push(m) }
20
+
21
+ Fiber.yield
22
+ end
23
+
24
+ def send(m)
25
+ @connection.send_msg(m)
26
+ end
27
+
28
+ def receive
29
+ fiber = Fiber.current
30
+ @queue.pop {|m| fiber.resume(m) }
31
+ Fiber.yield
32
+ end
33
+ end
34
+
35
+ def streaming_client_connect(path, &blk)
36
+ url = "http://localhost:#{@test_server_port}#{path}"
37
+ client = StreamingHelper.new(url)
38
+ blk.call(client) if blk
39
+ stop
40
+ end
41
+ end
42
+ end
@@ -1,4 +1,4 @@
1
1
  module Goliath
2
2
  # The current version of Goliath
3
- VERSION = '1.0.0'
3
+ VERSION = '1.0.1'
4
4
  end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require File.join(File.dirname(__FILE__), '../../', 'lib/goliath/test_helper_streaming')
3
+ require File.join(File.dirname(__FILE__), '../../', 'lib/goliath')
4
+
5
+ class ChunkedStreaming < Goliath::API
6
+
7
+ def response(env)
8
+
9
+ EM.next_tick do
10
+ env.chunked_stream_send("chunked")
11
+ env.chunked_stream_close
12
+ end
13
+
14
+ headers = { 'Content-Type' => 'text/plain', 'X-Stream' => 'Goliath' }
15
+ chunked_streaming_response(200, headers)
16
+ end
17
+
18
+ def on_close(env)
19
+ end
20
+ end
21
+
22
+ describe "ChunkedStreaming" do
23
+ include Goliath::TestHelper
24
+
25
+ let(:err) { Proc.new { |c| fail "HTTP Request failed #{c.response}" } }
26
+
27
+ it "should stream content" do
28
+ with_api(ChunkedStreaming, {:verbose => true, :log_stdout => true}) do |server|
29
+ streaming_client_connect('/streaming') do |client|
30
+ client.receive.should == "chunked"
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+
@@ -10,7 +10,7 @@ describe Template do
10
10
 
11
11
  it 'renders haml template with default haml layout' do
12
12
  with_api(Template, api_options) do
13
- get_request(:path => '/root') do |c|
13
+ get_request(:path => '/') do |c|
14
14
  c.response.should =~ %r{<li><a href="/joke">Tell me a joke</a></li>}
15
15
  end
16
16
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ require 'goliath/runner'
3
+ require 'irb'
4
+
5
+ describe Goliath::Console do
6
+ before(:each) do
7
+ @server = Goliath::Server.new
8
+ end
9
+
10
+ describe 'run!' do
11
+ it "starts a irb session" do
12
+ Object.should_receive(:send).with(:define_method, :goliath_server)
13
+ IRB.should_receive(:start)
14
+ @server.should_receive(:load_config)
15
+ Goliath::Console.run!(@server)
16
+ end
17
+ end
18
+ end
@@ -103,6 +103,18 @@ describe Goliath::Server do
103
103
  end
104
104
  end
105
105
 
106
+ describe 'stop' do
107
+ it 'logs when receives TERM signal' do
108
+ EM.run do
109
+ logger = mock('logger')
110
+ logger.should_receive(:info).with('Stopping server...')
111
+ @s.logger = logger
112
+ @s.start
113
+ @s.stop
114
+ end
115
+ end
116
+ end
117
+
106
118
  context 'config parsing' do
107
119
  context 'environment' do
108
120
  after(:all) do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goliath
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-08-11 00:00:00.000000000 Z
13
+ date: 2012-11-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: eventmachine
@@ -540,6 +540,7 @@ files:
540
540
  - lib/goliath/api.rb
541
541
  - lib/goliath/application.rb
542
542
  - lib/goliath/connection.rb
543
+ - lib/goliath/console.rb
543
544
  - lib/goliath/constants.rb
544
545
  - lib/goliath/env.rb
545
546
  - lib/goliath/goliath.rb
@@ -589,6 +590,7 @@ files:
589
590
  - lib/goliath/runner.rb
590
591
  - lib/goliath/server.rb
591
592
  - lib/goliath/test_helper.rb
593
+ - lib/goliath/test_helper_streaming.rb
592
594
  - lib/goliath/test_helper_ws.rb
593
595
  - lib/goliath/validation/error.rb
594
596
  - lib/goliath/validation/standard_http_errors.rb
@@ -597,14 +599,10 @@ files:
597
599
  - lib/goliath/websocket.rb
598
600
  - lib/goliath.rb
599
601
  - LICENSE
600
- - pkg/data.tar.gz
601
- - pkg/goliath-0.9.3.gem
602
- - pkg/goliath-0.9.4.gem
603
- - pkg/goliath-1.0.0.beta.1.gem
604
- - pkg/metadata
605
602
  - Rakefile
606
603
  - README.md
607
604
  - spec/integration/async_request_processing.rb
605
+ - spec/integration/chunked_streaming_spec.rb
608
606
  - spec/integration/early_abort_spec.rb
609
607
  - spec/integration/echo_spec.rb
610
608
  - spec/integration/empty_body_spec.rb
@@ -621,6 +619,7 @@ files:
621
619
  - spec/spec_helper.rb
622
620
  - spec/unit/api_spec.rb
623
621
  - spec/unit/connection_spec.rb
622
+ - spec/unit/console_spec.rb
624
623
  - spec/unit/env_spec.rb
625
624
  - spec/unit/headers_spec.rb
626
625
  - spec/unit/rack/default_mime_type_spec.rb
@@ -645,7 +644,6 @@ files:
645
644
  - spec/unit/validation/standard_http_errors_spec.rb
646
645
  - test/echo_test.rb
647
646
  - test/test_helper.rb
648
- - test.rb
649
647
  - .gemtest
650
648
  - .gitignore
651
649
  - .rspec
@@ -668,6 +666,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
668
666
  - - ! '>='
669
667
  - !ruby/object:Gem::Version
670
668
  version: '0'
669
+ segments:
670
+ - 0
671
+ hash: -356739448572304908
671
672
  requirements: []
672
673
  rubyforge_project:
673
674
  rubygems_version: 1.8.24
@@ -676,6 +677,7 @@ specification_version: 3
676
677
  summary: Async framework for writing API servers
677
678
  test_files:
678
679
  - spec/integration/async_request_processing.rb
680
+ - spec/integration/chunked_streaming_spec.rb
679
681
  - spec/integration/early_abort_spec.rb
680
682
  - spec/integration/echo_spec.rb
681
683
  - spec/integration/empty_body_spec.rb
@@ -692,6 +694,7 @@ test_files:
692
694
  - spec/spec_helper.rb
693
695
  - spec/unit/api_spec.rb
694
696
  - spec/unit/connection_spec.rb
697
+ - spec/unit/console_spec.rb
695
698
  - spec/unit/env_spec.rb
696
699
  - spec/unit/headers_spec.rb
697
700
  - spec/unit/rack/default_mime_type_spec.rb
data/test.rb DELETED
@@ -1,6 +0,0 @@
1
- class ValidationErrorInJson < Goliath::API
2
- use Goliath::Rack::Render, 'json'
3
- def response(env)
4
- raise Goliath::Validation::Error.new(420, 'You Must Chill')
5
- end
6
- end