rocketio 0.2.1 → 0.3.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.
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>