racket-mvc 0.4.0 → 0.5.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -5
  3. data/lib/racket.rb +25 -7
  4. data/lib/racket/application.rb +70 -136
  5. data/lib/racket/controller.rb +95 -38
  6. data/lib/racket/current.rb +1 -1
  7. data/lib/racket/helpers/file.rb +7 -5
  8. data/lib/racket/helpers/routing.rb +5 -5
  9. data/lib/racket/helpers/sass.rb +11 -11
  10. data/lib/racket/helpers/view.rb +8 -5
  11. data/lib/racket/plugins/base.rb +1 -1
  12. data/lib/racket/plugins/sass.rb +1 -1
  13. data/lib/racket/request.rb +3 -3
  14. data/lib/racket/response.rb +1 -1
  15. data/lib/racket/router.rb +31 -12
  16. data/lib/racket/session.rb +3 -3
  17. data/lib/racket/settings/application.rb +27 -33
  18. data/lib/racket/settings/base.rb +19 -8
  19. data/lib/racket/settings/controller.rb +9 -7
  20. data/lib/racket/settings/defaults.rb +81 -0
  21. data/lib/racket/utils.rb +19 -15
  22. data/lib/racket/utils/application.rb +4 -114
  23. data/lib/racket/utils/application/handler_stack.rb +163 -0
  24. data/lib/racket/utils/application/logger.rb +72 -0
  25. data/lib/racket/utils/application/registry_builder.rb +88 -0
  26. data/lib/racket/utils/application/stateless_services.rb +73 -0
  27. data/lib/racket/utils/exceptions.rb +3 -3
  28. data/lib/racket/utils/file_system.rb +75 -47
  29. data/lib/racket/utils/helpers.rb +35 -13
  30. data/lib/racket/utils/routing.rb +62 -46
  31. data/lib/racket/utils/views.rb +19 -187
  32. data/lib/racket/utils/views/renderer.rb +75 -0
  33. data/lib/racket/utils/views/template_cache.rb +126 -0
  34. data/lib/racket/utils/views/template_locator.rb +83 -0
  35. data/lib/racket/utils/views/template_resolver.rb +112 -0
  36. data/lib/racket/version.rb +2 -2
  37. data/lib/racket/view_manager.rb +12 -4
  38. data/rake/utils.rb +5 -5
  39. data/spec/_custom.rb +69 -19
  40. data/spec/_default.rb +60 -44
  41. data/spec/_plugin.rb +12 -14
  42. data/spec/_template_cache.rb +176 -0
  43. data/spec/racket.rb +10 -13
  44. data/spec/test_custom_app/controllers/sub1/custom_sub_controller_1.rb +1 -1
  45. data/spec/test_custom_app/controllers/sub3/custom_sub_controller_3.rb +19 -1
  46. data/spec/test_custom_app/controllers/sub5/custom_sub_controller_5.rb +8 -0
  47. data/spec/test_custom_app/files/stuff.rb +3 -0
  48. data/spec/test_custom_app/files/triplet.erb +5 -0
  49. data/spec/test_custom_app/templates/sub5/text.erb +3 -0
  50. data/spec/test_default_app/controllers/default_root_controller.rb +3 -0
  51. data/spec/test_default_app/controllers/sub1/default_sub_controller_1.rb +1 -1
  52. metadata +52 -11
@@ -1,5 +1,5 @@
1
1
  # Racket - The noisy Rack MVC framework
2
- # Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
2
+ # Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
3
3
  #
4
4
  # This file is part of Racket.
5
5
  #
@@ -22,9 +22,25 @@ module Racket
22
22
  module Helpers
23
23
  # Cache for helpers, ensuring that helpers get loaded exactly once.
24
24
  class HelperCache
25
- def initialize(helper_dir)
25
+ # Returns a service proc that can be used by the registry.
26
+ #
27
+ # @param [Hash] _options (unused)
28
+ # @return [Proc]
29
+ def self.service(_options = {})
30
+ lambda do |reg|
31
+ new(
32
+ reg.application_settings.helper_dir,
33
+ reg.application_logger,
34
+ reg.utils
35
+ )
36
+ end
37
+ end
38
+
39
+ def initialize(helper_dir, logger, utils)
26
40
  @helper_dir = helper_dir
27
41
  @helpers = {}
42
+ @logger = logger
43
+ @utils = utils
28
44
  end
29
45
 
30
46
  # Loads helper files and return the loadad modules as a hash. Any helper files that
@@ -51,20 +67,24 @@ module Racket
51
67
 
52
68
  def load_helper_file(helper)
53
69
  require_helper_file(helper)
54
- self.class.load_helper_module(helper)
70
+ load_helper_module(helper)
55
71
  end
56
72
 
57
73
  def require_helper_file(helper)
58
- loaded = Utils.safe_require("racket/helpers/#{helper}")
59
- Utils.safe_require(Utils.build_path(@helper_dir, helper).to_s) if !loaded && @helper_dir
74
+ loaded = @utils.safe_require("racket/helpers/#{helper}")
75
+ @utils.safe_require(@utils.build_path(@helper_dir, helper).to_s) if !loaded && @helper_dir
60
76
  end
61
77
 
62
- def self.load_helper_module(helper)
78
+ # Loads a helper module
79
+ #
80
+ # @param [Symbol] helper
81
+ # @return [Module]
82
+ def load_helper_module(helper)
63
83
  helper_module = nil
64
- Utils.run_block(NameError) do
84
+ @utils.run_block(NameError) do
65
85
  helper_module =
66
86
  Racket::Helpers.const_get(helper.to_s.split('_').collect(&:capitalize).join.to_sym)
67
- ::Racket::Application.inform_dev("Loaded helper module #{helper.inspect}.")
87
+ @logger.inform_dev("Loaded helper module #{helper.inspect}.")
68
88
  end
69
89
  helper_module
70
90
  end
@@ -73,23 +93,25 @@ module Racket
73
93
  # Applies helpers to a controller class by including the modules in the class.
74
94
  #
75
95
  # @param [Class] klass
76
- def self.apply_helpers(klass)
96
+ def apply_helpers(klass)
77
97
  klass.helper unless klass.settings.fetch(:helpers) # Makes sure default helpers are loaded.
78
98
  __apply_helpers(klass)
79
99
  nil
80
100
  end
81
101
 
82
- def self.__apply_helpers(klass)
102
+ # Applies helpers to a controller class by including the modules in the class.
103
+ #
104
+ # @param [Class] klass
105
+ # @return [Class]
106
+ def __apply_helpers(klass)
83
107
  klass.settings.fetch(:helpers).reverse_each do |pair|
84
108
  helper_key, helper = pair
85
- ::Racket::Application.inform_dev(
109
+ klass.context.logger.inform_dev(
86
110
  "Adding helper module #{helper_key.inspect} to #{klass}"
87
111
  )
88
112
  klass.send(:include, helper)
89
113
  end
90
114
  end
91
-
92
- private_class_method :__apply_helpers
93
115
  end
94
116
  end
95
117
  end
@@ -1,5 +1,5 @@
1
1
  # Racket - The noisy Rack MVC framework
2
- # Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
2
+ # Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
3
3
  #
4
4
  # This file is part of Racket.
5
5
  #
@@ -25,10 +25,19 @@ module Racket
25
25
 
26
26
  # Class for caching actions
27
27
  class ActionCache
28
+ # Returns a service proc that can be used by the registry.
29
+ #
30
+ # @param [Hash] _options (unused)
31
+ # @return [Proc]
32
+ def self.service(_options = {})
33
+ ->(reg) { new(reg.application_logger) }
34
+ end
35
+
28
36
  attr_reader :items
29
37
 
30
- def initialize
38
+ def initialize(logger)
31
39
  @items = {}
40
+ @logger = logger
32
41
  end
33
42
 
34
43
  # Returns whether +controller_class+ is in the cache and that it contains the action
@@ -50,7 +59,7 @@ module Racket
50
59
  __add(controller_class)
51
60
  actions = @items[controller_class].to_a
52
61
  @items[controller_class] = actions
53
- ::Racket::Application.inform_dev(
62
+ @logger.inform_dev(
54
63
  "Registering actions #{actions} for #{controller_class}."
55
64
  ) && nil
56
65
  end
@@ -69,55 +78,62 @@ module Racket
69
78
  end
70
79
  end
71
80
 
72
- # Extracts the target class, target params and target action from a list of valid routes.
73
- #
74
- # @param [HttpRouter::Response] response
75
- # @return [Array]
76
- def self.extract_target(response)
77
- target_klass = response.route.dest
78
- params = response.param_values.first.reject(&:empty?)
79
- action = params.empty? ? target_klass.settings.fetch(:default_action) : params.shift.to_sym
80
- [target_klass, params, action]
81
- end
81
+ # Class responsible for dispatching requests to controllers.
82
+ class Dispatcher
83
+ # Extracts the target class, target params and target action from a list of valid routes.
84
+ #
85
+ # @param [HttpRouter::Response] response
86
+ # @return [Array]
87
+ def self.extract_target(response)
88
+ target_klass = response.route.dest
89
+ params = response.param_values.first.reject(&:empty?)
90
+ action =
91
+ if params.empty?
92
+ target_klass.settings.fetch(:default_action)
93
+ else
94
+ params.shift.to_sym
95
+ end
96
+ [target_klass, params, action]
97
+ end
82
98
 
83
- def self.call_controller(target_klass, mod)
84
- target = target_klass.new
85
- target.extend(mod)
86
- target.__run
87
- end
99
+ # Constructor
100
+ #
101
+ # @param [Hash] env
102
+ # @param [Array] target_info
103
+ def initialize(env, target_info)
104
+ @env = env
105
+ @controller_class, @params, @action = target_info
106
+ end
88
107
 
89
- # Renders a controller. This is the default action whenever a matching route for a request
90
- # is found.
91
- #
92
- # @param [Hash] env
93
- # @param [Array] target_info
94
- # @return [Array] A racket response triplet
95
- def self.render_controller(env, target_info)
96
- controller_class, params, action = target_info
108
+ # Dispatches request to a controller. This is the default action whenever a matching
109
+ # route for a request is found.
110
+ #
111
+ # @return [Array] A racket response triplet
112
+ def dispatch
113
+ # Rewrite PATH_INFO to reflect that we split out the parameters
114
+ update_path_info(@params.length)
97
115
 
98
- # Rewrite PATH_INFO to reflect that we split out the parameters
99
- update_path_info(env, params.length)
116
+ # Call controller
117
+ call_controller(Current.init(RouterParams.new(@action, @params, @env)))
118
+ end
100
119
 
101
- # Initialize and render target
102
- call_controller(
103
- controller_class,
104
- Current.init(RouterParams.new(action, params, env))
105
- )
106
- end
120
+ private
107
121
 
108
- # Updates the PATH_INFO environment variable.
109
- #
110
- # @param [Hash] env
111
- # @param [Fixnum] num_params
112
- # @return [nil]
113
- def self.update_path_info(env, num_params)
114
- env['PATH_INFO'] = env['PATH_INFO']
115
- .split('/')[0...-num_params]
116
- .join('/') unless num_params.zero?
117
- nil
118
- end
122
+ def call_controller(mod)
123
+ @controller_class.new.extend(mod).__run
124
+ end
119
125
 
120
- private_class_method :call_controller, :update_path_info
126
+ # Updates the PATH_INFO environment variable.
127
+ #
128
+ # @param [Fixnum] num_params
129
+ # @return [nil]
130
+ def update_path_info(num_params)
131
+ @env['PATH_INFO'] = @env['PATH_INFO']
132
+ .split('/')[0...-num_params]
133
+ .join('/') unless num_params.zero?
134
+ nil
135
+ end
136
+ end
121
137
  end
122
138
  end
123
139
  end
@@ -1,5 +1,5 @@
1
1
  # Racket - The noisy Rack MVC framework
2
- # Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
2
+ # Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
3
3
  #
4
4
  # This file is part of Racket.
5
5
  #
@@ -18,197 +18,29 @@
18
18
 
19
19
  require 'tilt'
20
20
 
21
+ require_relative 'views/renderer.rb'
22
+ require_relative 'views/template_cache.rb'
23
+ require_relative 'views/template_locator.rb'
24
+ require_relative 'views/template_resolver.rb'
25
+
21
26
  module Racket
22
27
  module Utils
23
- # Utility functions for views.
28
+ # Namespace for view utilities
24
29
  module Views
25
- # Cache for storing templates
26
- class TemplateCache
27
- def initialize
28
- @cache = {}
29
- end
30
-
31
- # Returns a cached template. If the template has not been cached yet, this method will run a
32
- # lookup against the provided parameters.
33
- #
34
- # @param [String] path
35
- # @param [TemplateParams] template_params
36
- # @return [String|Proc|nil]
37
- def ensure_in_cache(path, template_params)
38
- return @cache[path] if @cache.key?(path)
39
- @cache[path] = TemplateLocator.calculate_path(path, template_params)
40
- end
41
- end
42
-
43
- # Class used for locating templates.
44
- class TemplateLocator
45
- def initialize(layout_base_dir, view_base_dir)
46
- @layout_base_dir = layout_base_dir
47
- @view_base_dir = view_base_dir
48
- @layout_cache = TemplateCache.new
49
- @view_cache = TemplateCache.new
50
- end
51
-
52
- # Returns the layout associated with the current request. On the first request to any action
53
- # the result is cached, meaning that the layout only needs to be looked up once.
54
- #
55
- # @param [Racket::Controller] controller
56
- # @return [String|nil]
57
- def get_layout(controller)
58
- get_template(TemplateParams.new(:layout, controller, @layout_base_dir, @layout_cache))
59
- end
60
-
61
- # Returns the view associated with the current request. On the first request to any action
62
- # the result is cached, meaning that the view only needs to be looked up once.
63
- #
64
- # @param [Racket::Controller] controller
65
- # @return [String|nil]
66
- def get_view(controller)
67
- get_template(TemplateParams.new(:view, controller, @view_base_dir, @view_cache))
68
- end
69
-
70
- private
71
-
72
- # Tries to locate a template matching +path+ in the file system and returns the path if a
73
- # matching file is found. If no matching file is found, +nil+ is returned. The result is
74
- # cached, meaning that the filesystem lookup for a specific path will only happen once.
75
- #
76
- # @param [TemplateParams] template_params
77
- # @return [String|nil]
78
- def get_template(template_params)
79
- klass = self.class
80
- path = klass.get_template_path(template_params.controller)
81
- template = template_params.cache.ensure_in_cache(path, template_params)
82
- klass.resolve_template(path, template, template_params)
83
- end
84
-
85
- def self.calculate_path(path, template_params)
86
- type, controller, base_dir = template_params.to_a
87
- default_template = controller.settings.fetch("default_#{type}".to_sym)
88
- template =
89
- TemplateLocator.lookup_template_with_default(
90
- Utils.fs_path(base_dir, path), default_template
91
- )
92
- ::Racket::Application.inform_dev(
93
- "Using #{type} #{template.inspect} for #{controller.class}.#{controller.racket.action}."
94
- )
95
- template
96
- end
97
-
98
- def self.resolve_template(path, template, template_params)
99
- return template unless template.is_a?(Proc)
100
- _, controller, base_dir = template_params.to_a
101
- lookup_template(
102
- Utils.fs_path(
103
- Utils.fs_path(base_dir, path).dirname,
104
- call_template_proc(template, controller)
105
- )
106
- )
107
- end
108
-
109
- # Calls a template proc. Depending on how many parameters the template proc takes, different
110
- # types of information will be passed to the proc.
111
- # If the proc takes zero parameters, no information will be passed.
112
- # If the proc takes one parameter, it will contain the current action.
113
- # If the proc takes two parameters, they will contain the current action and the current
114
- # params.
115
- # If the proc takes three parameters, they will contain the current action, the current
116
- # params and the current request.
117
- #
118
- # @param [Proc] proc
119
- # @param [Racket::Controller] controller
120
- # @return [String]
121
- def self.call_template_proc(proc, controller)
122
- racket = controller.racket
123
- proc_args = [racket.action, racket.params, controller.request].slice(0...proc.arity)
124
- proc.call(*proc_args).to_s
125
- end
126
-
127
- # Returns the "url path" that should be used when searching for templates.
128
- #
129
- # @param [Racket::Controller] controller
130
- # @return [String]
131
- def self.get_template_path(controller)
132
- template_path =
133
- [::Racket::Application.get_route(controller.class), controller.racket.action].join('/')
134
- template_path = template_path[1..-1] if template_path.start_with?('//')
135
- template_path
136
- end
137
-
138
- # Locates a file in the filesystem matching an URL path. If there exists a matching file,
139
- # the path to it is returned. If there is no matching file, +nil+ is returned.
140
- # @param [Pathname] path
141
- # @return [Pathname|nil]
142
- def self.lookup_template(path)
143
- Utils.first_matching_path(*Utils.extract_dir_and_glob(path))
144
- end
145
-
146
- # Locates a file in the filesystem matching an URL path. If there exists a matching file,
147
- # the path to it is returned. If there is no matching file and +default_template+ is a
148
- # String or a Symbol, another lookup will be performed using +default_template+. If
149
- # +default_template+ is a Proc or nil, +default_template+ will be used as is instead.
150
- #
151
- # @param [Pathname] path
152
- # @param [String|Symbol|Proc|nil] default_template
153
- # @return [String|Proc|nil]
154
- def self.lookup_template_with_default(path, default_template)
155
- template = lookup_template(path)
156
- unless template
157
- if default_template.is_a?(String) || default_template.is_a?(Symbol)
158
- # Strings and symbols can be lookup up in the file system...
159
- template = lookup_template(Utils.fs_path(path.dirname, default_template))
160
- else
161
- # ...but not nils/procs!
162
- template = default_template
163
- end
164
- end
165
- template
30
+ # Extracts what template settings to use based on context and incoming parameters.
31
+ #
32
+ # @param [Object] context
33
+ # @param [Hash] template_settings
34
+ # @return [Hash]
35
+ def self.extract_template_settings(context, template_settings)
36
+ if context.respond_to?(:view_settings) && !template_settings
37
+ context.view_settings
38
+ elsif template_settings
39
+ template_settings
40
+ else
41
+ {}
166
42
  end
167
43
  end
168
-
169
- # Class responsible for rendering a controller/view/layout combination.
170
- class ViewRenderer
171
- # Renders a page using the provided controller/view and layout combination and returns an
172
- # response array that can be sent to the client.
173
- #
174
- # @param [Racket::Controller] controller
175
- # @param [String] view
176
- # @param [String] layout
177
- # @return [Array]
178
- def self.render(controller, view, layout)
179
- send_response(
180
- controller.response,
181
- view ? render_template(controller, view, layout) : controller.racket.action_result
182
- )
183
- end
184
-
185
- # Renders a template/layout combo using Tilt and returns it as a string.
186
- #
187
- # @param [Racket::Controller] controller
188
- # @param [String] view
189
- # @param [String|nil] layout
190
- # @return [String]
191
- def self.render_template(controller, view, layout)
192
- output = Tilt.new(view).render(controller)
193
- output = Tilt.new(layout).render(controller) { output } if layout
194
- output
195
- end
196
-
197
- # Sends response to client.
198
- #
199
- # @param [Racket::Response] response
200
- # @param [String] output
201
- # @return nil
202
- def self.send_response(response, output)
203
- response.write(output)
204
- response.finish
205
- end
206
-
207
- private_class_method :render_template, :send_response
208
- end
209
-
210
- # Struct for holding template data.
211
- TemplateParams = Struct.new(:type, :controller, :base_dir, :cache)
212
44
  end
213
45
  end
214
46
  end