rocketio 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf979ed0411d6e6e4388a1832715d82a792054e6
4
- data.tar.gz: 4a9cd0d72c951843860d0872ba5310caf3932b11
3
+ metadata.gz: ac617b26c0a9352ff073f5e194202d8e09f90c4c
4
+ data.tar.gz: 1e9aa0b25987687e5d1b25193bfb60bf014e6fa2
5
5
  SHA512:
6
- metadata.gz: 7cfb21a58bae0e43d65b07fe1cee2355f5126407ca942e30058aac18cb644f77a2566335dc83c15778405b7c688ce82290c887c90cb77facca232b115595b860
7
- data.tar.gz: 7b1a235d42e494da7e923e7116223107cd6f4c310b2e069c4baf3629ab864d0a72caf9d8e32cb10bc03b7daaea3196cb8f4983c99d1968461b467006473023a9
6
+ metadata.gz: 143b5e6041e82d762c8a55ad0d4fc759bbd05a5cbaddca814141c76cd04fabc07ae17df6e168525b3f518ec3944140ebc18ce9275be13765dfdd8f70ff363489
7
+ data.tar.gz: 9e087b5c7c644b4a84ac5830824537902710ea8d3203e882e08b33139184ee6802960d4a85819e173c9360fa9369be29a8dac71a2c69d0c7e204a57ca6d4566e
data/lib/rocketio.rb CHANGED
@@ -62,7 +62,11 @@ module RocketIO
62
62
  DEFAULT_CONTENT_TYPE = 'text/html'.freeze
63
63
  CONTENT_LENGTH = 'Content-Length'.freeze
64
64
  CONTENT_DISPOSITION = 'Content-Disposition'.freeze
65
+
65
66
  APPLICATION_OCTET_STREAM = 'application/octet-stream'.freeze
67
+ APPLICATION_JSON = 'application/json'.freeze
68
+ APPLICATION_JSON_REGEXP = /application\/json/i
69
+
66
70
  DEFAULT_AUTH_REALM = 'AccessRestricted'.freeze
67
71
  DEFAULT_TOKEN_AUTH_REALM = 'Application'.freeze
68
72
 
@@ -71,31 +75,39 @@ module RocketIO
71
75
  HTTP_ACCEPT = 'HTTP_ACCEPT'.freeze
72
76
  REMOTE_USER = 'REMOTE_USER'.freeze
73
77
 
74
- HTTP_CONNECTION = 'HTTP_CONNECTION'.freeze
78
+ HTTP_CONTENT_TYPE = 'CONTENT_TYPE'.freeze
79
+ HTTP_CONNECTION = 'HTTP_CONNECTION'.freeze
80
+
75
81
  HTTP_AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'].map(&:freeze).freeze
76
82
  HTTP_AUTHORIZATION_MOCKS = {
77
83
  basic: 'Basic Og=='.freeze,
78
84
  digest: 'Digest opaque="", qop="auth", uri="%s"'.freeze
79
85
  }.freeze
80
- HTTP_UPGRADE = 'HTTP_UPGRADE'.freeze
81
- UPGRADE = 'upgrade'.freeze
82
- WEBSOCKET = 'websocket'.freeze
83
- HTTP_1_1 = 'HTTP/1.1'.freeze
84
- HTTP_VERSION = 'HTTP_VERSION'.freeze
85
- HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'.freeze
86
- HTTP_IF_MATCH = 'HTTP_IF_MATCH'.freeze
87
- HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
88
- HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
86
+
87
+ HTTP_UPGRADE = 'HTTP_UPGRADE'.freeze
88
+
89
+ UPGRADE = 'upgrade'.freeze
90
+ WEBSOCKET = 'websocket'.freeze
91
+
92
+ HTTP_1_1 = 'HTTP/1.1'.freeze
93
+ HTTP_VERSION = 'HTTP_VERSION'.freeze
94
+ HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'.freeze
95
+ HTTP_IF_MATCH = 'HTTP_IF_MATCH'.freeze
96
+ HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
97
+ HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
89
98
  HTTP_IF_UNMODIFIED_SINCE = 'HTTP_IF_UNMODIFIED_SINCE'.freeze
90
- HTTP_X_REQUESTED_WITH = 'HTTP_X_REQUESTED_WITH'.freeze
91
- XML_HTTP_REQUEST = 'XMLHttpRequest'.freeze
92
-
93
- LOCATION = 'Location'.freeze
94
- CACHE_CONTROL = 'Cache-Control'.freeze
95
- EXPIRES = 'Expires'.freeze
96
- LAST_MODIFIED = 'Last-Modified'.freeze
97
- ETAG = 'ETag'.freeze
98
- ETAG_KINDS = [:strong, :weak].freeze
99
+ HTTP_X_REQUESTED_WITH = 'HTTP_X_REQUESTED_WITH'.freeze
100
+ XML_HTTP_REQUEST = 'XMLHttpRequest'.freeze
101
+
102
+ LOCATION = 'Location'.freeze
103
+ CACHE_CONTROL = 'Cache-Control'.freeze
104
+ EXPIRES = 'Expires'.freeze
105
+ LAST_MODIFIED = 'Last-Modified'.freeze
106
+
107
+ ETAG = 'ETag'.freeze
108
+ ETAG_KINDS = [:strong, :weak].freeze
109
+
110
+ RACK_INPUT = 'rack.input'.freeze
99
111
 
100
112
  DROP_BODY_RESPONSES = {204 => true, 205 => true, 304 => true}.freeze
101
113
 
@@ -13,7 +13,11 @@ module RocketIO
13
13
  catch :__response__ do # catch 404 errors and potential `halt` calls from middleware
14
14
  controller, method, path_params = @router.resolve_path(env[PATH_INFO])
15
15
 
16
- controller || Controller.new(INDEX_METHOD, EMPTY_ARRAY, env).error(404)
16
+ unless controller
17
+ controller = Controller.new
18
+ controller.instance_variable_set(:@__env__, env)
19
+ controller.error(404)
20
+ end
17
21
 
18
22
  controller = controller.new(method, path_params)
19
23
  chain = controller.middleware.reverse.inject(controller) {|app,ware| ware.call(app)}
@@ -2,7 +2,7 @@ module RocketIO
2
2
  class Controller
3
3
  extend Forwardable
4
4
 
5
- def_delegators :'self.class', :url, :api, :dirname, :parameters_policy
5
+ def_delegators :'self.class', :url, :api, :dirname, :path_params_expected
6
6
  def_delegators :request, :session, :request_method
7
7
  def_delegators RocketIO, :environment
8
8
  def_delegators RocketIO, :indifferent_params, :indifferent_hash, :mime_type
@@ -15,10 +15,9 @@ module RocketIO
15
15
  def_delegators RocketIO, :"#{env}?"
16
16
  end
17
17
 
18
- def initialize requested_method = RocketIO::INDEX_METHOD, path_params = RocketIO::EMPTY_ARRAY, env = nil
19
- @__requested_method__ = requested_method
18
+ def initialize requested_method = RocketIO::INDEX_METHOD, path_params = RocketIO::EMPTY_ARRAY
19
+ @__requested_method__ = requested_method.to_sym
20
20
  @__path_params_array__ = path_params.freeze
21
- @__env__ = env if env
22
21
  end
23
22
 
24
23
  # call requested method
@@ -26,35 +25,35 @@ module RocketIO
26
25
  # @param [Hash] env
27
26
  # @return [Rack::Response]
28
27
  #
29
- def call env
28
+ def call env, params: nil
30
29
  @__env__ = env
30
+ @__params__ = params if params
31
31
  catch :__response__ do
32
32
 
33
33
  api[requested_method] || error(501)
34
34
 
35
35
  if error_handlers[500]
36
36
  begin
37
- __call__(env)
37
+ __call__
38
38
  rescue Exception => e
39
39
  error(500, e)
40
40
  end
41
41
  else
42
- __call__(env)
42
+ __call__
43
43
  end
44
44
  end
45
45
  end
46
46
 
47
47
  private
48
- def __call__ env
48
+ def __call__
49
49
 
50
50
  validate_or_request_authentication_if_needed
51
51
  validate_or_request_authorization_if_needed
52
- validate_parameters
53
52
 
54
53
  __run__ proc {
55
54
  invoke_before_filter
56
55
  invoke_around_filter proc {
57
- response.body = public_send(requested_method, *path_params_array)
56
+ response.body = public_send(requested_method, *requested_method_arguments)
58
57
  }
59
58
  invoke_after_filter
60
59
  }
@@ -65,6 +64,10 @@ module RocketIO
65
64
  response.body ||= RocketIO::EMPTY_ARRAY
66
65
  end
67
66
 
67
+ if env[RocketIO::HTTP_ACCEPT] == RocketIO::APPLICATION_JSON
68
+ response[RocketIO::CONTENT_TYPE] = RocketIO::APPLICATION_JSON
69
+ end
70
+
68
71
  response.finish
69
72
  end
70
73
 
@@ -76,20 +79,23 @@ module RocketIO
76
79
  @__env__
77
80
  end
78
81
 
79
- def validate_parameters
80
-
81
- if path_params_array.size >= api[requested_method][:parameters_policy][:min]
82
- return if api[requested_method][:parameters_policy][:max] == :* ||
83
- path_params_array.size <= api[requested_method][:parameters_policy][:max]
84
- end
85
-
86
- error(409)
87
- end
88
-
89
82
  def requested_method
90
83
  @__requested_method__
91
84
  end
92
85
 
86
+ def requested_method_arguments
87
+ if api[requested_method][:params_accepted_as_last_argument?]
88
+ [
89
+ *path_params_array,
90
+ # creating a shallow params copy with symbolized keys
91
+ # cause Ruby does not accept string keys for keyword arguments
92
+ params.keys.each_with_object({}) {|k,o| o[k.to_sym] = params[k]}
93
+ ]
94
+ else
95
+ path_params_array
96
+ end
97
+ end
98
+
93
99
  def path_params_array
94
100
  @__path_params_array__
95
101
  end
@@ -105,7 +111,7 @@ module RocketIO
105
111
  end
106
112
 
107
113
  def params
108
- @__params__ ||= indifferent_params(request.params)
114
+ @__params__ ||= indifferent_params(json? ? __parse_json_body__ : request.params)
109
115
  end
110
116
 
111
117
  def request
@@ -250,13 +256,11 @@ module RocketIO
250
256
  return if self == RocketIO::Controller
251
257
  return unless public_instance_methods(false).include?(meth)
252
258
 
253
- parameters = instance_method(meth).parameters
259
+ params, path_params = instance_method(meth).parameters.partition {|p| p[0] =~ /\Akey/}
254
260
 
255
261
  api[meth] = {
256
- # api methods should be called with a predetermined set of parameters
257
- # so setting an appropriate policy for just defined method based on its parameters.
258
- path_params: RocketIO.path_params(parameters).freeze,
259
- parameters_policy: RocketIO.parameters_policy(parameters).freeze
262
+ path_params: RocketIO.path_params(path_params).freeze,
263
+ params_accepted_as_last_argument?: params.any?,
260
264
  }
261
265
  end
262
266
 
@@ -73,17 +73,6 @@ module RocketIO
73
73
  RocketIO.error_renderer(404, xhr?, env: env)
74
74
  end
75
75
 
76
- # 409: Wrong number of arguments received
77
- error 409 do
78
- RocketIO.error_renderer(409, xhr?, {
79
- url: url,
80
- controller: self.class.to_s,
81
- requested_method: requested_method,
82
- expected: api[requested_method][:parameters_policy],
83
- received: path_params_array
84
- })
85
- end
86
-
87
76
  # 501: Not Implemented
88
77
  error 501 do
89
78
  RocketIO.error_renderer(501, xhr?, {
@@ -73,6 +73,16 @@ module RocketIO
73
73
  env[RocketIO::HTTP_X_REQUESTED_WITH] == RocketIO::XML_HTTP_REQUEST
74
74
  end
75
75
 
76
+ def json?
77
+ # using regex because Content-Type may look like "application/json;charset=UTF-8"
78
+ # TODO: consider using Regexp#match? on Ruby 2.4
79
+ env[HTTP_CONTENT_TYPE] =~ RocketIO::APPLICATION_JSON_REGEXP
80
+ end
81
+
82
+ def __parse_json_body__
83
+ JSON.parse(env[RACK_INPUT].read)
84
+ end
85
+
76
86
  # switch controller and halt with returned response.
77
87
  # any arguments will be passed to requested method.
78
88
  #
@@ -59,6 +59,10 @@ module RocketIO
59
59
  if drop_body?
60
60
  close
61
61
  self.body = EMPTY_ARRAY
62
+ else
63
+ if headers[CONTENT_TYPE] == APPLICATION_JSON
64
+ self.body = [self.body.to_json]
65
+ end
62
66
  end
63
67
 
64
68
  [status.to_i, headers, self.body]
data/lib/rocketio/util.rb CHANGED
@@ -31,7 +31,7 @@ module RocketIO
31
31
  #
32
32
  # @param [Array] parameters
33
33
  # @return [Array]
34
- def parameters_policy parameters
34
+ def path_params_expected parameters
35
35
  min, max = 0, parameters.size
36
36
 
37
37
  unlimited = false
@@ -1,3 +1,3 @@
1
1
  module RocketIO
2
- VERSION = '0.2.1'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
@@ -32,40 +32,6 @@ spec :ErrorHandlers do
32
32
  end
33
33
  end
34
34
 
35
- context 'Parameters Error' do
36
-
37
- it 'uses default handler' do
38
- app mock_app(mock_controller {
39
- def index; end
40
- })
41
- get :x
42
- assert(last_response.status) == 409
43
- assert(last_response.body) =~ /wrong arguments/i
44
- end
45
-
46
- it 'uses custom handler' do
47
- app mock_app(mock_controller {
48
- def index; end
49
- error 409 do
50
- 'noway'
51
- end
52
- })
53
- get :x
54
- assert(last_response.status) == 409
55
- assert(last_response.body) == 'noway'
56
- end
57
-
58
- it 'can be triggered manually' do
59
- called = false
60
- app mock_app(mock_controller {
61
- define_method(:index) {called = true; error(409)}
62
- })
63
- get
64
- assert(called) == true
65
- assert(last_response.status) == 409
66
- end
67
- end
68
-
69
35
  context '500: Fatal Error' do
70
36
 
71
37
  it 'raises when no handler defined' do
data/test/json_body.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'setup'
2
+
3
+ spec 'JSON body and params' do
4
+ it 'is parsing JSON body if request Content-Type contains "application/json"' do
5
+ app mock_controller {
6
+ def index; params[:x] end
7
+ }
8
+ env[RocketIO::RACK_INPUT] = StringIO.new({x: :y}.to_json)
9
+ header['Content-Type'] = 'application/json'
10
+ get
11
+ assert(last_response.body) == 'y'
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require 'setup'
2
+
3
+ spec :keyword_arguments do
4
+ it 'is accepting params as last argument' do
5
+ app mock_controller {
6
+ def index a, b:; a + b end
7
+ }
8
+ get :a, b: :b
9
+ assert(last_response.body) == 'ab'
10
+ end
11
+ end
@@ -116,4 +116,23 @@ spec RocketIO::Response do
116
116
  get
117
117
  assert(last_response).is_ok_with_body ''
118
118
  end
119
+
120
+ it "sets Content-Type to application/json when request's Accept header is application/json" do
121
+ app mock_controller {
122
+ def index; end
123
+ }
124
+ header['Accept'] = 'application/json'
125
+ get
126
+ assert(last_response.headers['Content-Type']) == 'application/json'
127
+ end
128
+
129
+ it "responds with a JSON body when request's Accept header is application/json" do
130
+ body = {x: :y}
131
+ app mock_controller {
132
+ define_method(:index) {body}
133
+ }
134
+ header['Accept'] = 'application/json'
135
+ get
136
+ assert(last_response.body) == body.to_json
137
+ end
119
138
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rocketio
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Slee Woo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-25 00:00:00.000000000 Z
11
+ date: 2016-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -174,7 +174,6 @@ files:
174
174
  - lib/rocketio/controller/token_auth.rb
175
175
  - lib/rocketio/controller/websocket.rb
176
176
  - lib/rocketio/error_templates/404.html
177
- - lib/rocketio/error_templates/409.html
178
177
  - lib/rocketio/error_templates/501.html
179
178
  - lib/rocketio/error_templates/layout.html
180
179
  - lib/rocketio/exceptions.rb
@@ -194,6 +193,8 @@ files:
194
193
  - test/filters_test.rb
195
194
  - test/halt_test.rb
196
195
  - test/helpers_test.rb
196
+ - test/json_body.rb
197
+ - test/keyword_arguments.rb
197
198
  - test/middleware_test.rb
198
199
  - test/redirect_test.rb
199
200
  - test/render/b.erb
@@ -241,5 +242,5 @@ rubyforge_project:
241
242
  rubygems_version: 2.5.1
242
243
  signing_key:
243
244
  specification_version: 4
244
- summary: '["rocketio-0.2.1", "Simple, fast, scalable web framework for Ruby"]'
245
+ summary: '["rocketio-0.3.0", "Simple, fast, scalable web framework for Ruby"]'
245
246
  test_files: []
@@ -1,11 +0,0 @@
1
- <h1>409: Wrong arguments</h1>
2
- <h3>
3
- <b>{{url}}</b> URL resolved to <b>{{controller}}#{{requested_method}}</b> method
4
- but it was called with wrong arguments.
5
- <div>
6
- Expected: {min: {{expected.min}}, max: {{expected.max}}}
7
- </div>
8
- <div>
9
- Received: {{received}}
10
- </div>
11
- </h3>