rocketio 0.1.0 → 0.2.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rocketio.rb +6 -3
  3. data/lib/rocketio/application.rb +3 -7
  4. data/lib/rocketio/controller.rb +62 -90
  5. data/lib/rocketio/controller/authentication.rb +38 -44
  6. data/lib/rocketio/controller/authorization.rb +8 -4
  7. data/lib/rocketio/controller/error_handlers.rb +12 -8
  8. data/lib/rocketio/controller/filters.rb +14 -19
  9. data/lib/rocketio/controller/helpers.rb +1 -1
  10. data/lib/rocketio/controller/middleware.rb +1 -1
  11. data/lib/rocketio/controller/render/engine.rb +3 -3
  12. data/lib/rocketio/controller/render/layout.rb +1 -1
  13. data/lib/rocketio/controller/render/layouts.rb +6 -6
  14. data/lib/rocketio/controller/render/template_vars.rb +3 -3
  15. data/lib/rocketio/controller/render/templates.rb +6 -6
  16. data/lib/rocketio/controller/sessions.rb +1 -1
  17. data/lib/rocketio/error_templates/409.html +11 -7
  18. data/lib/rocketio/error_templates/501.html +4 -4
  19. data/lib/rocketio/router.rb +35 -21
  20. data/lib/rocketio/version.rb +1 -1
  21. data/rocketio.gemspec +2 -0
  22. data/test/aliases_test.rb +2 -2
  23. data/test/api_test.rb +24 -117
  24. data/test/authentication_test.rb +96 -60
  25. data/test/authorization_test.rb +28 -17
  26. data/test/cache_control_test.rb +12 -12
  27. data/test/content_type_test.rb +7 -7
  28. data/test/cookies_test.rb +4 -4
  29. data/test/error_handlers_test.rb +14 -12
  30. data/test/etag_test.rb +32 -32
  31. data/test/filters_test.rb +96 -79
  32. data/test/halt_test.rb +1 -1
  33. data/test/helpers_test.rb +6 -6
  34. data/test/middleware_test.rb +4 -4
  35. data/test/redirect_test.rb +6 -7
  36. data/test/render/{post.erb → b.erb} +0 -0
  37. data/test/render/{put.erb → c.erb} +0 -0
  38. data/test/render/engine_test.rb +5 -5
  39. data/test/render/{get.erb → index.erb} +0 -0
  40. data/test/render/layout_test.rb +21 -17
  41. data/test/render/layouts_test.rb +14 -14
  42. data/test/render/render_test.rb +17 -14
  43. data/test/render/template_vars_test.rb +9 -9
  44. data/test/render/templates_test.rb +16 -16
  45. data/test/response_test.rb +4 -4
  46. data/test/routes_test.rb +21 -42
  47. data/test/sendfile_test.rb +8 -8
  48. data/test/sessions_test.rb +27 -27
  49. data/test/setup.rb +2 -0
  50. metadata +34 -6
@@ -13,8 +13,8 @@ module RocketIO
13
13
  #
14
14
  def self.token_auth *args, &block
15
15
  opts = args.last.is_a?(Hash) ? args.pop : {}
16
- (args.any? ? args.map!(&:to_sym) : RocketIO::REQUEST_METHODS.values).each do |rm|
17
- (@__token_auth__ ||= {})[rm] = {
16
+ (args.any? ? args.map(&:to_sym) : [:*]).each do |method|
17
+ (@__token_auth__ ||= {})[method] = {
18
18
  realm: opts[:realm] || RocketIO::DEFAULT_TOKEN_AUTH_REALM.freeze,
19
19
  block: block
20
20
  }
@@ -25,13 +25,13 @@ module RocketIO
25
25
  def self.define_token_auth_methods source = self
26
26
  prompts = allocate.token_auth.merge(source.instance_variable_get(:@__token_auth__) || {}).freeze
27
27
  return if prompts.empty?
28
- private_api << define_method(:token_auth) {prompts}
28
+ api.delete define_method(:token_auth) {prompts}
29
29
  end
30
30
 
31
31
  def token_auth; RocketIO::EMPTY_HASH end
32
32
 
33
33
  def validate_or_request_authorization_if_needed
34
- return unless auth = token_auth[requested_method]
34
+ return unless auth = authorization_required?
35
35
  return if validate_token_auth(&auth[:block])
36
36
  throw(:__response__, request_token_auth(auth[:realm]))
37
37
  end
@@ -47,5 +47,9 @@ module RocketIO
47
47
  def request_token_auth realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM
48
48
  RocketIO::TokenAuth.authentication_request(realm)
49
49
  end
50
+
51
+ def authorization_required?
52
+ token_auth[requested_method] || token_auth[:*]
53
+ end
50
54
  end
51
55
  end
@@ -38,12 +38,12 @@ module RocketIO
38
38
  def self.define_error_handlers_methods source = self
39
39
  handlers = (source.instance_variable_get(:@__error_handlers__) || {}).each_with_object({}) do |(code,block),o|
40
40
  o[code] = :"__#{code}_error_handler__"
41
- private_api << define_method(o[code], &block)
41
+ api.delete define_method(o[code], &block)
42
42
  end
43
43
  handlers.update(allocate.error_handlers)
44
44
  return if handlers.empty?
45
45
  handlers.freeze
46
- private_api << define_method(:error_handlers) {handlers}
46
+ api.delete define_method(:error_handlers) {handlers}
47
47
  end
48
48
 
49
49
  def error_handlers; RocketIO::EMPTY_HASH end
@@ -76,17 +76,21 @@ module RocketIO
76
76
  # 409: Wrong number of arguments received
77
77
  error 409 do
78
78
  RocketIO.error_renderer(409, xhr?, {
79
- env: env,
80
- controller: self.class,
81
- resolved_path: url,
82
- expected_parameters: parameters_policy[env[RocketIO::REQUEST_METHOD]],
83
- received_parameters: path_params
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
84
  })
85
85
  end
86
86
 
87
87
  # 501: Not Implemented
88
88
  error 501 do
89
- RocketIO.error_renderer(501, xhr?, env: env)
89
+ RocketIO.error_renderer(501, xhr?, {
90
+ url: url,
91
+ controller: self.class.to_s,
92
+ requested_method: requested_method
93
+ })
90
94
  end
91
95
  end
92
96
  end
@@ -11,47 +11,42 @@ module RocketIO
11
11
  # and can override them selectively, by name
12
12
  #
13
13
  # @note wildcard filters will run before/around/after any methods,
14
- # that's it, if defining `before {}` and `before(:get) {}` filters
15
- # `get` method will run `before` filter then `before(:get)`
14
+ # that's it, if defining `before {}` and `before(:index) {}` filters
15
+ # `index` method will run `before` filter then `before(:index)`
16
16
  #
17
- # @example run before any requested method, being it REST or websocket
17
+ # @example run before any method
18
18
  # before do
19
19
  # # some logic here
20
20
  # end
21
21
  #
22
- # @example run only before GET
23
- # before :get do
22
+ # @example run only before :create
23
+ # before :create do
24
24
  # # some logic here
25
25
  # end
26
26
  #
27
- # @example run only around PUT and POST
28
- # around :put, :post do |app|
27
+ # @example run only around :create and :edit
28
+ # around :create, :edit do |app|
29
29
  # # some logic here
30
30
  # app.call
31
31
  # end
32
32
  #
33
- # @example run only after :register websocket call
34
- # after :register do
35
- # # some logic here
36
- # end
37
- #
38
33
  # @example define a filter that does nothing. useful to override inherited filters.
39
- # before :get
34
+ # before {}
40
35
  #
41
- # @example run 2 blocks before GET
36
+ # @example run 2 blocks before :create
42
37
  # before do # wildcard filter, will run before any method
43
38
  # @user = User.find...
44
39
  # end
45
40
  #
46
- # before :get do # named filter, will run only before `get`
41
+ # before :create do # named filter, will run only before `create`
47
42
  # # wildcard filter already executed so we have `@user` variable here
48
43
  # @photo = @user.photos.find...
49
44
  # end
50
45
  #
51
- # # before calling `get` two filters will be executed:
46
+ # # at the moment we call :create two filters was executed:
52
47
  # # - wildcard one
53
48
  # # - named one
54
- # def get
49
+ # def create
55
50
  # # both @user and @photo variables available here
56
51
  # end
57
52
  #
@@ -75,12 +70,12 @@ module RocketIO
75
70
  define_singleton_method define_methods do |source = self|
76
71
  filters = (source.instance_variable_get(var) || {}).each_with_object({}) do |(meth,block),o|
77
72
  o[meth] = :"__#{filter}_#{meth}__"
78
- private_api << define_method(o[meth], &block)
73
+ api.delete define_method(o[meth], &block)
79
74
  end
80
75
  filters.update(allocate.__send__(filter))
81
76
  return unless filters.any?
82
77
  filters.freeze
83
- private_api << define_method(filter) {filters}
78
+ api.delete define_method(filter) {filters}
84
79
  end
85
80
  end
86
81
 
@@ -83,7 +83,7 @@ module RocketIO
83
83
  # pass User, :bob, :bobsen
84
84
  #
85
85
  def pass controller, *args
86
- halt controller.initialize_controller(RocketIO::REQUEST_METHODS[request_method], args).call(env)
86
+ halt controller.new(requested_method, args).call(env)
87
87
  end
88
88
 
89
89
  # stop executing any code and send response to browser.
@@ -24,7 +24,7 @@ module RocketIO
24
24
  def self.define_middleware_methods source = self
25
25
  middleware = ((source.instance_variable_get(:@__middleware__) || []) + allocate.middleware).uniq.freeze
26
26
  return if middleware.empty?
27
- private_api << define_method(:middleware) {middleware}
27
+ api.delete define_method(:middleware) {middleware}
28
28
  end
29
29
 
30
30
  def middleware; RocketIO::EMPTY_ARRAY end
@@ -60,14 +60,14 @@ module RocketIO
60
60
  return unless engine = source.instance_variable_get(:@__engine__)
61
61
  if Proc === engine
62
62
  selfengine = allocate.engine
63
- private_api << define_method(:__rocketio_engine__, &engine)
64
- private_api << define_method(:engine) {
63
+ api.delete define_method(:__rocketio_engine__, &engine)
64
+ api.delete define_method(:engine) {
65
65
  engine, *engine_options = __rocketio_engine__
66
66
  return selfengine unless engine
67
67
  [RocketIO.engine_class(engine), engine_options.freeze].freeze
68
68
  }
69
69
  else
70
- private_api << define_method(:engine) {engine}
70
+ api.delete define_method(:engine) {engine}
71
71
  end
72
72
  end
73
73
 
@@ -17,7 +17,7 @@ module RocketIO
17
17
  def self.define_layout_methods source = self
18
18
  return unless source.instance_variables.include?(:@__layout__)
19
19
  layout = source.instance_variable_get(:@__layout__)
20
- private_api << define_method(:layout) {layout}
20
+ api.delete define_method(:layout) {layout}
21
21
  end
22
22
 
23
23
  # by default no layout used, so this method returns nil.
@@ -59,25 +59,25 @@ module RocketIO
59
59
  o[name] = :"__#{name}_layout__"
60
60
  if setup[:block]
61
61
  # block given, do not search for file, use returned value instead
62
- private_api << define_method(o[name], &setup[:block])
62
+ api.delete define_method(o[name], &setup[:block])
63
63
  elsif setup[:file]
64
64
  # file given, search the file in original controller dirname
65
- private_api << meth_name = :"__#{name}_layout_file__"
65
+ meth_name = :"__#{name}_layout_file__"
66
66
  meth_proc = setup[:file].is_a?(::Proc) ? setup[:file] : -> {setup[:file]}
67
- private_api << define_method(meth_name, &meth_proc)
68
- private_api << define_method(o[name]) {
67
+ api.delete define_method(meth_name, &meth_proc)
68
+ api.delete define_method(o[name]) {
69
69
  engine, * = resolve_engine
70
70
  read_template(find_template(setup[:root], __send__(meth_name), engine))
71
71
  }
72
72
  else
73
73
  # only name given, search for a file with same name in controller's dirname
74
- private_api << define_method(o[name]) {
74
+ api.delete define_method(o[name]) {
75
75
  engine, * = resolve_engine
76
76
  read_template(find_template(self.dirname, setup[:name], engine))
77
77
  }
78
78
  end
79
79
  end.freeze
80
- private_api << define_method(:layouts) {layouts}
80
+ api.delete define_method(:layouts) {layouts}
81
81
  end
82
82
 
83
83
  def layouts; RocketIO::EMPTY_HASH end
@@ -17,12 +17,12 @@ module RocketIO
17
17
  vars = source.instance_variable_get(:@__template_vars__).each_with_object(allocate.__template_vars__.dup) do |(name,value),o|
18
18
  o[name] = :"__#{name}_template_var__"
19
19
  if value.is_a?(Proc)
20
- private_api << define_method(o[name], &value)
20
+ api.delete define_method(o[name], &value)
21
21
  else
22
- private_api << define_method(o[name]) {value}
22
+ api.delete define_method(o[name]) {value}
23
23
  end
24
24
  end.freeze
25
- private_api << define_method(:__template_vars__) {vars}
25
+ api.delete define_method(:__template_vars__) {vars}
26
26
  end
27
27
 
28
28
  def __template_vars__; RocketIO::EMPTY_HASH end
@@ -57,25 +57,25 @@ module RocketIO
57
57
  o[name] = :"__#{name}_template__"
58
58
  if setup[:block]
59
59
  # block given, do not search for file, use returned value instead
60
- private_api << define_method(o[name], &setup[:block])
60
+ api.delete define_method(o[name], &setup[:block])
61
61
  elsif setup[:file]
62
62
  # file given, search the file in original controller dirname
63
- private_api << meth_name = :"__#{name}_template_file__"
63
+ meth_name = :"__#{name}_template_file__"
64
64
  meth_proc = setup[:file].is_a?(Proc) ? setup[:file] : -> {setup[:file]}
65
- private_api << define_method(meth_name, &meth_proc)
66
- private_api << define_method(o[name]) {
65
+ api.delete define_method(meth_name, &meth_proc)
66
+ api.delete define_method(o[name]) {
67
67
  engine, * = resolve_engine
68
68
  read_template(find_template(setup[:root], __send__(meth_name), engine))
69
69
  }
70
70
  else
71
71
  # only name given, search for a file with same name in controller's dirname
72
- private_api << define_method(o[name]) {
72
+ api.delete define_method(o[name]) {
73
73
  engine, * = resolve_engine
74
74
  read_template(find_template(self.dirname, setup[:name], engine))
75
75
  }
76
76
  end
77
77
  end.freeze
78
- private_api << define_method(:templates) {templates}
78
+ api.delete define_method(:templates) {templates}
79
79
  end
80
80
 
81
81
  def templates; RocketIO::EMPTY_HASH end
@@ -56,7 +56,7 @@ module RocketIO
56
56
  def self.define_sessions_methods source = self
57
57
  return unless source.instance_variables.include?(:@__sessions__)
58
58
  sessions = source.instance_variable_get(:@__sessions__)
59
- private_api << define_method(:sessions) {sessions}
59
+ api.delete define_method(:sessions) {sessions}
60
60
  end
61
61
 
62
62
  def sessions; end
@@ -1,7 +1,11 @@
1
- <h1>Wrong number of arguments received</h1>
2
- <h2>
3
- Controller: {{controller}}<br>
4
- Resolved Path: {{resolved_path}}<br>
5
- Expected Parameters: {{expected_parameters}}<br>
6
- Received Parameters: {{received_parameters}}<br>
7
- </h2>
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>
@@ -1,6 +1,6 @@
1
1
  <h1>501: Not Implemented</h1>
2
2
  <hr>
3
- <h2>
4
- "{{env.PATH_INFO}}" page found
5
- but it is not supposed to work through "{{env.REQUEST_METHOD}}" request method
6
- </h2>
3
+ <h3>
4
+ <b>{{url}}</b> URL resolved to <b>{{controller}}</b> controller
5
+ but it does not respond to <b>{{requested_method}}</b> method
6
+ </h3>
@@ -1,26 +1,42 @@
1
1
  module RocketIO
2
2
  class Router
3
- ROOT_PATH_PIECES = [''.freeze].freeze
3
+
4
+ ROOT_PATH_PIECES = [EMPTY_STRING].freeze
5
+ PATH_SPLITTER = /\/+/.freeze
6
+
4
7
  attr_reader :controllers, :routes
5
8
 
6
9
  def initialize *controllers
7
10
  @controllers = controllers.flatten.compact.uniq
8
- @routes = build_routes
11
+ @controller_routes = controller_routes()
9
12
  freeze!
10
13
  end
11
14
 
15
+ # fully traversing the tree and use the last matched controller
12
16
  def resolve_path path
13
- controllers = @routes
17
+ controller_routes = @controller_routes
14
18
  pieces = path_pieces(path)
15
- depth = -1
16
- controller = nil
17
- offset = 0
18
- while controllers = controllers[pieces[depth += 1]]
19
- next unless controllers[0]
20
- controller = controllers[0]
21
- offset = depth + 1
19
+ depth = -1
20
+ controller = nil
21
+ path_params = []
22
+
23
+ while controller_routes = controller_routes[pieces[depth += 1]]
24
+ # do not use `next unless controller = controller_routes[0]` here
25
+ # cause this may override earlier found controller.
26
+ next unless controller_routes[0]
27
+ controller = controller_routes[0]
28
+ path_params = pieces[ (depth + 1) .. -1 ]
22
29
  end
23
- [controller, pieces[offset .. -1]]
30
+
31
+ return EMPTY_ARRAY unless controller
32
+
33
+ method = if path_params.any? && controller.api[path_params[0].to_sym]
34
+ path_params.slice!(0).to_sym
35
+ else
36
+ INDEX_METHOD
37
+ end
38
+
39
+ [controller, method, path_params]
24
40
  end
25
41
 
26
42
  private
@@ -34,30 +50,28 @@ module RocketIO
34
50
  #
35
51
  def path_pieces path
36
52
  return ROOT_PATH_PIECES if path == SLASH # that's root path, /
37
- path.to_s.split(/\/+/)
53
+ path.to_s.split(PATH_SPLITTER)
38
54
  end
39
55
 
40
56
  # building a routing tree
41
57
  #
42
58
  # @return [Hash]
43
59
  #
44
- def build_routes
45
- map = {}
46
- @controllers.each do |controller|
60
+ def controller_routes
61
+ controllers.each_with_object({}) do |controller,map|
47
62
  [controller.url, *controller.aliases].each do |url|
48
63
  depth = 0
49
64
  pieces = path_pieces(url)
50
- pieces.inject(map) do |map,chunk|
51
- map[chunk] ||= {}
52
- map[chunk][0] = controller if pieces.size == (depth += 1)
53
- map[chunk]
65
+ pieces.reduce(map) do |pieces_map,chunk|
66
+ pieces_map[chunk] ||= {}
67
+ pieces_map[chunk][0] = controller if pieces.size == (depth += 1)
68
+ pieces_map[chunk]
54
69
  end
55
70
  end
56
71
  end
57
- map
58
72
  end
59
73
 
60
- # freezing the application cause modifying it at runtime may blow the universe
74
+ # freezing the router cause modifying it at runtime may blow the universe
61
75
  def freeze!
62
76
  self.freeze
63
77
  end
@@ -1,3 +1,3 @@
1
1
  module RocketIO
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
data/rocketio.gemspec CHANGED
@@ -24,4 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency 'rake', '~> 10.0'
25
25
  spec.add_development_dependency 'tokyo', '~> 0'
26
26
  spec.add_development_dependency 'rack-radar', '~> 0'
27
+ spec.add_development_dependency 'pry', '~> 0'
28
+ spec.add_development_dependency 'pry-byebug', '~> 3'
27
29
  end
data/test/aliases_test.rb CHANGED
@@ -4,13 +4,13 @@ class A < RocketIO::Controller
4
4
  alias_url :x
5
5
  alias_url :y
6
6
 
7
- def get; url end
7
+ def index; url end
8
8
 
9
9
  class B < self
10
10
  alias_url :x
11
11
  alias_url :y
12
12
 
13
- def get; url end
13
+ def index; url end
14
14
  end
15
15
  end
16
16