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
@@ -0,0 +1,75 @@
1
+ # Racket - The noisy Rack MVC framework
2
+ # Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
3
+ #
4
+ # This file is part of Racket.
5
+ #
6
+ # Racket is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Racket is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with Racket. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ module Racket
20
+ module Utils
21
+ module Views
22
+ # Class responsible for rendering a controller/view/layout combination.
23
+ class Renderer
24
+ # Returns a service proc that can be used by the registry.
25
+ #
26
+ # @param [Hash] _options (unused)
27
+ # @return [Proc]
28
+ def self.service(_options = {})
29
+ -> { self }
30
+ end
31
+
32
+ # Renders a page using the provided controller/view and layout combination and returns an
33
+ # response array that can be sent to the client.
34
+ #
35
+ # @param [Racket::Controller] controller
36
+ # @param [String] view
37
+ # @param [String] layout
38
+ # @return [Array]
39
+ def self.render(controller, view, layout)
40
+ send_response(
41
+ controller.response,
42
+ view ? render_template(controller, view, layout) : controller.racket.action_result
43
+ )
44
+ end
45
+
46
+ # Renders a template/layout combo using Tilt and returns it as a string.
47
+ #
48
+ # @param [Racket::Controller] controller
49
+ # @param [String] view
50
+ # @param [String|nil] layout
51
+ # @return [String]
52
+ def self.render_template(controller, view, layout)
53
+ output = Tilt.new(view, nil, controller.view_settings).render(controller)
54
+ output =
55
+ Tilt.new(
56
+ layout, nil, controller.layout_settings
57
+ ).render(controller) { output } if layout
58
+ output
59
+ end
60
+
61
+ # Sends response to client.
62
+ #
63
+ # @param [Racket::Response] response
64
+ # @param [String] output
65
+ # @return nil
66
+ def self.send_response(response, output)
67
+ response.write(output)
68
+ response.finish
69
+ end
70
+
71
+ private_class_method :render_template, :send_response
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,126 @@
1
+ # Racket - The noisy Rack MVC framework
2
+ # Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
3
+ #
4
+ # This file is part of Racket.
5
+ #
6
+ # Racket is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Racket is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with Racket. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ module Racket
20
+ module Utils
21
+ module Views
22
+ # Class for caching templates.
23
+ # This class adheres to the Moneta API
24
+ # (https://github.com/minad/moneta#user-content-moneta-api), even though it is not using the
25
+ # Moneta framework.
26
+ class TemplateCache
27
+ # Default options for template cache
28
+ DEFAULT_OPTIONS = { expires: 0 }.freeze
29
+
30
+ # Returns a service proc that can be used by the registry.
31
+ #
32
+ # @param [Hash] _options (unused)
33
+ # @return [Proc]
34
+ def self.service(_options = {})
35
+ -> { new({}) }
36
+ end
37
+
38
+ def initialize(options)
39
+ @expirations = {}
40
+ @items = {}
41
+ @options = DEFAULT_OPTIONS.merge(options)
42
+ end
43
+
44
+ def [](key)
45
+ load(key)
46
+ end
47
+
48
+ def []=(key, value)
49
+ store(key, value)
50
+ end
51
+
52
+ def clear(_options = {})
53
+ @expirations.clear
54
+ @items.clear
55
+ end
56
+
57
+ def close
58
+ clear
59
+ end
60
+
61
+ def create(_key, _value, _options = {})
62
+ raise NotImplementedError
63
+ end
64
+
65
+ def decrement(_key, _amount = 1, _options = {})
66
+ raise NotImplementedError
67
+ end
68
+
69
+ def delete(key, _options = {})
70
+ @expirations.delete(key)
71
+ @items.delete(key)
72
+ end
73
+
74
+ def features
75
+ []
76
+ end
77
+
78
+ # This method handles both forms of fetch.
79
+ # With a default block - fetch(key, options = {}, &block)
80
+ # With a default value - fetch(key, value, options = {})
81
+ def fetch(*args)
82
+ key = args.shift
83
+ return load(key) if key?(key)
84
+ block_given? ? yield : args.first
85
+ end
86
+
87
+ def increment(_key, _amount = 1, _options = {})
88
+ raise NotImplementedError
89
+ end
90
+
91
+ def key?(key)
92
+ @items.key?(key)
93
+ end
94
+
95
+ def load(key, _options = {})
96
+ return @items[key] unless @expirations.key?(key)
97
+ if Time.now > @expirations[key]
98
+ @expirations.delete(key)
99
+ @items.delete(key)
100
+ end
101
+ @items[key]
102
+ end
103
+
104
+ def store(key, value, options = {})
105
+ set_expiration(key, options.fetch(:expires, @options[:expires]))
106
+ @items[key] = value
107
+ end
108
+
109
+ def supports?(feature)
110
+ features.include?(feature)
111
+ end
112
+
113
+ private
114
+
115
+ def set_expiration(key, expires)
116
+ expire_at = expires > 0 ? Time.now + expires : nil
117
+ if expire_at
118
+ @expirations[key] = expire_at
119
+ else
120
+ @expirations.delete(key)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,83 @@
1
+ # Racket - The noisy Rack MVC framework
2
+ # Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
3
+ #
4
+ # This file is part of Racket.
5
+ #
6
+ # Racket is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Racket is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with Racket. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ module Racket
20
+ module Utils
21
+ module Views
22
+ # Class used for locating templates. This class uses the TemplateResolver class internally
23
+ # for getting the template for the first time and caches the result so that subsequent Calls
24
+ # will not need to resolve the template again.
25
+ class TemplateLocator
26
+ # Returns a service proc that can be used by the registry.
27
+ #
28
+ # @param [Hash] _options (unused)
29
+ # @return [Proc]
30
+ def self.service(_options = {})
31
+ lambda do |reg|
32
+ new(
33
+ layout_cache: reg.layout_cache,
34
+ layout_resolver: reg.layout_resolver,
35
+ view_cache: reg.view_cache,
36
+ view_resolver: reg.view_resolver
37
+ )
38
+ end
39
+ end
40
+
41
+ def initialize(options)
42
+ options.each_pair { |key, value| instance_variable_set("@#{key}".to_sym, value) }
43
+ end
44
+
45
+ # Returns the layout associated with the current request. On the first request to any action
46
+ # the result is cached, meaning that the layout only needs to be looked up once.
47
+ #
48
+ # @param [Racket::Controller] controller
49
+ # @return [String|nil]
50
+ def get_layout(controller)
51
+ self.class.get_template(@layout_cache, @layout_resolver, controller)
52
+ end
53
+
54
+ # Returns the view associated with the current request. On the first request to any action
55
+ # the result is cached, meaning that the view only needs to be looked up once.
56
+ #
57
+ # @param [Racket::Controller] controller
58
+ # @return [String|nil]
59
+ def get_view(controller)
60
+ self.class.get_template(@view_cache, @view_resolver, controller)
61
+ end
62
+
63
+ # Tries to locate a template matching the controllers action in the file system and returns
64
+ # the path if a matching file is found. If no matching file is found, +nil+ is returned.
65
+ # The result is cached, meaning that the filesystem lookup for a specific path will only
66
+ # happen once.
67
+ #
68
+ # @param [TemplateCache] cache
69
+ # @param [TemplateResolver] resolver
70
+ # @param [Racket::Controller] controller
71
+ # @return [String|nil]
72
+ def self.get_template(cache, resolver, controller)
73
+ path = TemplateResolver.get_template_path(controller)
74
+ unless cache.key?(path)
75
+ template = resolver.get_template_object(path, controller)
76
+ cache.store(path, template)
77
+ end
78
+ resolver.resolve_template(path, cache.load(path), controller)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,112 @@
1
+ # Racket - The noisy Rack MVC framework
2
+ # Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
3
+ #
4
+ # This file is part of Racket.
5
+ #
6
+ # Racket is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Racket is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with Racket. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ module Racket
20
+ module Utils
21
+ module Views
22
+ # Class used for resolving template paths.
23
+ class TemplateResolver
24
+ # Alias for Racket::Utils::FileSystem module.
25
+ FSM = Racket::Utils::FileSystem
26
+
27
+ # Returns a service proc that can be used by the registry.
28
+ #
29
+ # @param [Hash] options
30
+ # @return [Proc]
31
+ def self.service(options = {})
32
+ type = options[:type]
33
+ lambda do |reg|
34
+ new(
35
+ base_dir: reg.application_settings.send("#{type}_dir"),
36
+ logger: reg.application_logger,
37
+ type: type,
38
+ utils: reg.utils
39
+ )
40
+ end
41
+ end
42
+
43
+ def initialize(options)
44
+ options.each_pair { |key, value| instance_variable_set("@#{key}".to_sym, value) }
45
+ end
46
+
47
+ # Returns the template object representing the specified path/controller combination.
48
+ #
49
+ # @param [String] path
50
+ # @param [Racket::Controller] controller
51
+ # @return [Pathname|Proc|nil]
52
+ def get_template_object(path, controller)
53
+ default_template = controller.settings.fetch("default_#{@type}".to_sym)
54
+ template =
55
+ FSM.resolve_path_with_default(
56
+ FSM.fs_path(@base_dir, path), default_template
57
+ )
58
+ @logger.inform_dev(
59
+ "Using #{@type} #{template.inspect} for #{controller.class}." \
60
+ "#{controller.racket.action}."
61
+ )
62
+ template
63
+ end
64
+
65
+ # Returns the "resolved" path for the given parameters. This is either a pathname or nil.
66
+ #
67
+ # @param [String] path
68
+ # @param [Proc|String|nil] template
69
+ # @param [Racket::Controller] controller
70
+ # @return [pathname|nil]
71
+ def resolve_template(path, template, controller)
72
+ return template unless template.is_a?(Proc)
73
+ FSM.resolve_path(
74
+ FSM.fs_path(
75
+ FSM.fs_path(@base_dir, path).dirname,
76
+ self.class.call_template_proc(template, controller)
77
+ )
78
+ )
79
+ end
80
+
81
+ # Calls a template proc. Depending on how many parameters the template proc takes, different
82
+ # types of information will be passed to the proc.
83
+ # If the proc takes zero parameters, no information will be passed.
84
+ # If the proc takes one parameter, it will contain the current action.
85
+ # If the proc takes two parameters, they will contain the current action and the current
86
+ # params.
87
+ # If the proc takes three parameters, they will contain the current action, the current
88
+ # params and the current request.
89
+ #
90
+ # @param [Proc] proc
91
+ # @param [Racket::Controller] controller
92
+ # @return [String]
93
+ def self.call_template_proc(proc, controller)
94
+ racket = controller.racket
95
+ proc_args = [racket.action, racket.params, controller.request].slice(0...proc.arity)
96
+ proc.call(*proc_args).to_s
97
+ end
98
+
99
+ # Returns the "url path" that should be used when searching for templates.
100
+ #
101
+ # @param [Racket::Controller] controller
102
+ # @return [String]
103
+ def self.get_template_path(controller)
104
+ template_path =
105
+ [controller.class.get_route, controller.racket.action].join('/')
106
+ template_path = template_path[1..-1] if template_path.start_with?('//')
107
+ template_path
108
+ end
109
+ end
110
+ end
111
+ end
112
+ 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
  #
@@ -23,7 +23,7 @@ module Racket
23
23
  # Major version
24
24
  MAJOR = 0
25
25
  # Minor version
26
- MINOR = 4
26
+ MINOR = 5
27
27
  # Teeny version
28
28
  TEENY = 0
29
29
  # Is it a prerelease?
@@ -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
  #
@@ -19,9 +19,17 @@
19
19
  module Racket
20
20
  # Handles rendering in Racket applications.
21
21
  class ViewManager
22
- def initialize(layout_base_dir, view_base_dir)
23
- @locator = Utils::Views::TemplateLocator.new(layout_base_dir, view_base_dir)
24
- @renderer = Utils::Views::ViewRenderer
22
+ # Returns a service proc that can be used by the registry.
23
+ #
24
+ # @param [Hash] _options (unused)
25
+ # @return [Proc]
26
+ def self.service(_options = {})
27
+ ->(reg) { new(reg.template_locator, reg.template_renderer) }
28
+ end
29
+
30
+ def initialize(locator, renderer)
31
+ @locator = locator
32
+ @renderer = renderer
25
33
  end
26
34
 
27
35
  # Renders a controller based on the request path and the variables set in the
@@ -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
  #
@@ -28,9 +28,9 @@ end
28
28
 
29
29
  def racket_files
30
30
  Dir.chdir(File.dirname(File.dirname(__FILE__))) do
31
- files = FileList['lib/**/*.rb'].to_a
32
- files.concat(FileList['rake/**/*'].to_a)
33
- files.concat(FileList['spec/**/*'].to_a)
34
- files.concat(FileList['COPYING.AGPL', 'Rakefile', 'README.md'].to_a)
31
+ files = Dir.glob('lib/**/*.rb')
32
+ files.concat(Dir.glob('rake/**/*'))
33
+ files.concat(Dir.glob('spec/**/*'))
34
+ files.concat(['COPYING.AGPL', 'Rakefile', 'README.md'])
35
35
  end
36
36
  end