racket-mvc 0.2.1 → 0.2.2

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: 29d1cf6f2b573f99dc79d38f46c61bbc877b81c6
4
- data.tar.gz: 114fceb1c2ff59e31a94b1300c1efbad34fc78cc
3
+ metadata.gz: 105b08877782c44a513558312f3e9b6af7daef75
4
+ data.tar.gz: 4ccd48feacc8788667f1ea5ab580a3cdb17cbdb0
5
5
  SHA512:
6
- metadata.gz: fb0558de22c5ed965ea8955caa8aac5309cadaa4ed80f01d08cce38666575f3715bd3a84dd1d6e005d94aa191cc433cd5a123cf73169cfba005b9497094f7aff
7
- data.tar.gz: b1b51dc4dcf51d057d472f1897b0e9cd92be66dad8abe06fe9eb7127bedc8e0122d3a58787acc2aee77027831abb0bbcab82bbff1bb85e64191675908062da0e
6
+ metadata.gz: 7275ee4175f164a783c9f827ddaee45f68b949ac4977c3637f6ff80e6f7662d03f0985926872d8fa68b68d4f1fa91a34c8aca3d2e3c8383cdbbdb9f3f4d45bac
7
+ data.tar.gz: 6795e2bfcca8c5671df6e65012fc60ec6ab374458d074c69f91451f9d5fde72de257530c9c32cf0e9336b16f18bc20417a2f6339edeefd09dbba91f62c5a3ab8
data/README.md CHANGED
@@ -1,49 +1,49 @@
1
- # Racket - The noisy Rack MVC framework
2
-
3
- [![Build Status](https://travis-ci.org/lasso/racket.svg?branch=master)](https://travis-ci.org/lasso/racket)    [![codecov.io](https://codecov.io/github/lasso/racket/coverage.svg?branch=master)](https://codecov.io/github/lasso/racket?branch=master)    [![Gem Version](https://badge.fury.io/rb/racket-mvc.svg)](http://badge.fury.io/rb/racket-mvc)
4
-
5
- ## Say what?
6
- Yes. It is yet another framework built on rack. Using MVC. Doing silly stuff while you look the other way.
7
-
8
- ## Why? I though there were a gazillion frameworks that did the same thing already...
9
- You are correct. There are _lots_ of Rack frameworks out there. This one does not pretend to do anything special
10
- that you could not get from any of them.
11
-
12
- ## So, I have to ask again. Why did you create this monstrosity?
13
- Well, when my web host suddenly started insisting on using Phusion Passenger on all of their servers
14
- I needed to replace my old [Ramaze](http://ramaze.net/) setup without to much hassle. I tried several
15
- other Rack framework, but none of them seemed capable of replacing my apps without some major rewrites.
16
-
17
- ## So you just though writing a whole new framework would be easier than using Rails?
18
- Yes. Writing Rack frameworks is easy! And since I am able to decide exactly what features I want I don't
19
- need to adopt to a large ecosystem of concepts I do not like.
20
-
21
- ## So, is it any good?
22
- Let us just say it is good _enough_ for my needs at the moment. I plan to add more features/make stuff faster
23
- whenever I am finished porting most of my old apps from Ramaze.
24
-
25
- ## Where are the tests?
26
- Have a look in the `spec` directory. The code base have tests covering 100 per cent of the code and I am planning on keeping it that way. At the moment the code is tested on the following platforms (using [Travis CI](https://travis-ci.org/)):
27
-
28
- - ruby 1.9.3
29
- - ruby 2.0.0
30
- - ruby 2.1.7
31
- - ruby 2.2.3
32
- - jruby 1.7 (1.9 mode only)
33
- - jruby 9.0.0.0
34
- - rbx-2 (latest version)
35
-
36
- I am using [bacon](https://github.com/chneukirchen/bacon) and [rack-test](https://github.com/brynary/rack-test) for testing. Run the tests by typing `rake test`in the root directory. Code coverage reports are provided by [simplecov](https://rubygems.org/gems/simplecov). After the tests have run the an HTML report can be found in the `coverage` directory.
37
-
38
- If you are not interested in running the tests yourself you could have a look at the test status at [Travis CI](https://travis-ci.org/lasso/racket) and the code coverage at [Codecov](https://codecov.io/github/lasso/racket). Their stats get updated on every commit.
39
-
40
- ## Alright, I want to try using this stuff. Where are the docs?
41
- At the moment there is not much documentation available, but I have started working on the [wiki](https://github.com/lasso/racket/wiki).
42
-
43
- The code itself is documented using [Yard](http://yardoc.org/). The docs are not generated automatically, you need to run `rake doc` in the root directory to generate them. After running the rake task the documentation will be available in the `doc` directory.
44
-
45
- ## Why is the code licenced under the GNU Affero General Public License? I want a more liberal licence!
46
- Because I think it is a Good Thing™ to share code. The
47
- [GNU Affero General Public License licence](https://www.gnu.org/licenses/agpl.html) is very liberal unless you plan
48
- on beeing egotistical. I you feel you cannot work with that, please choose
49
- [something else](https://en.wikipedia.org/wiki/Comparison_of_web_application_frameworks#Ruby).
1
+ # Racket - The noisy Rack MVC framework
2
+
3
+ [![Build Status](https://travis-ci.org/lasso/racket.svg?branch=master)](https://travis-ci.org/lasso/racket)    [![Code Climate](https://codeclimate.com/github/lasso/racket/badges/gpa.svg)](https://codeclimate.com/github/lasso/racket)    [![codecov.io](https://codecov.io/github/lasso/racket/coverage.svg?branch=master)](https://codecov.io/github/lasso/racket?branch=master)    [![Gem Version](https://badge.fury.io/rb/racket-mvc.svg)](http://badge.fury.io/rb/racket-mvc)
4
+
5
+ ## Say what?
6
+ Yes. It is yet another framework built on rack. Using MVC. Doing silly stuff while you look the other way.
7
+
8
+ ## Why? I though there were a gazillion frameworks that did the same thing already...
9
+ You are correct. There are _lots_ of Rack frameworks out there. This one does not pretend to do anything special
10
+ that you could not get from any of them.
11
+
12
+ ## So, I have to ask again. Why did you create this monstrosity?
13
+ Well, when my web host suddenly started insisting on using Phusion Passenger on all of their servers
14
+ I needed to replace my old [Ramaze](http://ramaze.net/) setup without to much hassle. I tried several
15
+ other Rack framework, but none of them seemed capable of replacing my apps without some major rewrites.
16
+
17
+ ## So you just though writing a whole new framework would be easier than using Rails?
18
+ Yes. Writing Rack frameworks is easy! And since I am able to decide exactly what features I want I don't
19
+ need to adopt to a large ecosystem of concepts I do not like.
20
+
21
+ ## So, is it any good?
22
+ Let us just say it is good _enough_ for my needs at the moment. I plan to add more features/make stuff faster
23
+ whenever I am finished porting most of my old apps from Ramaze.
24
+
25
+ ## Where are the tests?
26
+ Have a look in the `spec` directory. The code base have tests covering 100 per cent of the code and I am planning on keeping it that way. At the moment the code is tested on the following platforms (using [Travis CI](https://travis-ci.org/)):
27
+
28
+ - 1.9.3
29
+ - 2.0.0
30
+ - 2.1.7
31
+ - 2.2.3
32
+ - jruby-19mode
33
+ - jruby-head
34
+ - rbx-2
35
+
36
+ I am using [bacon](https://github.com/chneukirchen/bacon) and [rack-test](https://github.com/brynary/rack-test) for testing. Run the tests by typing `rake test`in the root directory. Code coverage reports are provided by [simplecov](https://rubygems.org/gems/simplecov). After the tests have run the an HTML report can be found in the `coverage` directory.
37
+
38
+ If you are not interested in running the tests yourself you could have a look at the test status at [Travis CI](https://travis-ci.org/lasso/racket) and the code coverage at [Codecov](https://codecov.io/github/lasso/racket). Their stats get updated on every commit.
39
+
40
+ ## Alright, I want to try using this stuff. Where are the docs?
41
+ At the moment there is not much documentation available, but I have started working on the [wiki](https://github.com/lasso/racket/wiki).
42
+
43
+ The code itself is documented using [Yard](http://yardoc.org/). The docs are not generated automatically, you need to run `rake doc` in the root directory to generate them. After running the rake task the documentation will be available in the `doc` directory.
44
+
45
+ ## Why is the code licenced under the GNU Affero General Public License? I want a more liberal licence!
46
+ Because I think it is a Good Thing™ to share code. The
47
+ [GNU Affero General Public License licence](https://www.gnu.org/licenses/agpl.html) is very liberal unless you plan
48
+ on beeing egotistical. I you feel you cannot work with that, please choose
49
+ [something else](https://en.wikipedia.org/wiki/Comparison_of_web_application_frameworks#Ruby).
data/Rakefile CHANGED
@@ -1,30 +1,31 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
+ require './rake/utils.rb'
3
4
 
4
- desc "Run bacon tests"
5
+ desc 'Run bacon tests'
5
6
  task default: [:test]
6
7
 
7
- desc "Build racket-mvc gem"
8
+ desc 'Build racket-mvc gem'
8
9
  task :build_gem do
9
10
  exec 'gem build racket.gemspec'
10
11
  end
11
12
 
12
- desc "Build yard docs"
13
+ desc 'Build yard docs'
13
14
  task :doc do
14
15
  exec 'yard'
15
16
  end
16
17
 
17
- desc "Show list of undocumented modules/classes/methods"
18
+ desc 'Show list of undocumented modules/classes/methods'
18
19
  task :nodoc do
19
20
  exec 'yard stats --list-undoc'
20
21
  end
21
22
 
22
- desc "Publish racket-mvc gem"
23
+ desc 'Publish racket-mvc gem'
23
24
  task publish_gem: [:build_gem] do
24
- exec 'gem push racket-mvc.gem'
25
+ exec "gem push racket-mvc#{racket_version}.gem"
25
26
  end
26
27
 
27
- desc "Run bacon tests"
28
+ desc 'Run bacon tests'
28
29
  task :test do
29
30
  exec 'bacon spec/racket.rb'
30
31
  end
@@ -24,24 +24,17 @@ module Racket
24
24
  helper_modules = {}
25
25
  helpers.each do |helper|
26
26
  helper_module = helper.to_s.split('_').collect(&:capitalize).join.to_sym
27
- begin
28
- begin
29
- require "racket/helpers/#{helper}"
30
- rescue LoadError
31
- if helper_dir
32
- begin
33
- require Utils.build_path(helper_dir, helper)
34
- rescue LoadError
35
- end
36
- end
37
- end
27
+ Utils.run_block(NameError) do
28
+ Utils.run_block(LoadError) { require "racket/helpers/#{helper}" } ||
29
+ (helper_dir &&
30
+ Utils.run_block(LoadError) { require Utils.build_path(helper_dir, helper) }
31
+ )
38
32
  helper_modules[helper] = Racket::Helpers.const_get(helper_module)
39
33
  Application.inform_dev("Added helper module #{helper.inspect} to class #{self}.")
40
- rescue NameError
34
+ end ||
41
35
  Application.inform_dev(
42
36
  "Failed to add helper module #{helper.inspect} to class #{self}.", :warn
43
37
  )
44
- end
45
38
  end
46
39
  helper_modules
47
40
  end
@@ -102,7 +95,7 @@ module Racket
102
95
  helper_modules.merge!(__load_helpers(existing_helpers))
103
96
  end
104
97
  # Load new helpers
105
- helpers.map! { |helper| helper.to_sym }
98
+ helpers.map!(&:to_sym)
106
99
  helpers.reject! { |helper| helper_modules.key?(helper) }
107
100
  helper_modules.merge!(__load_helpers(helpers))
108
101
  set_option(:helpers, helper_modules)
@@ -35,17 +35,25 @@ module Racket
35
35
  # @return [Module] A module encapsulating all state relating to the current request
36
36
  def self.init(env, klass, action, params)
37
37
  klass.helper if klass.get_option(:helpers).nil? # Makes sure default helpers are loaded.
38
- racket = State.new(action, nil, params)
39
- request = Request.new(env)
40
- response = Response.new
41
- session = Session.new(env['rack.session']) if env.key?('rack.session')
38
+ properties = init_properties(action, params, env)
42
39
  Module.new do
43
40
  klass.get_option(:helpers).each_value { |helper| include helper }
44
- define_method(:racket) { racket }
45
- define_method(:request) { request }
46
- define_method(:response) { response }
47
- define_method(:session) { session } if env.key?('rack.session')
41
+ properties.each_pair { |key, value| define_method(key) { value } }
48
42
  end
49
43
  end
44
+
45
+ def self.init_properties(action, params, env)
46
+ properties =
47
+ {
48
+ racket: State.new(action, nil, params),
49
+ request: Request.new(env),
50
+ response: Response.new
51
+ }
52
+ session = env.fetch('rack.session', nil)
53
+ properties[:session] = Session.new(session) if session
54
+ properties
55
+ end
56
+
57
+ private_class_method :init_properties
50
58
  end
51
59
  end
@@ -28,12 +28,7 @@ module Racket
28
28
  # @return [Array]
29
29
  def send_file(file, options = {})
30
30
  file = Utils.build_path(file)
31
- # Respond with a 404 Not Found if the file cannot be read.
32
- respond!(
33
- 404,
34
- { 'Content-Type' => 'text/plain' },
35
- Rack::Utils::HTTP_STATUS_CODES[404]
36
- ) unless Utils.file_readable? (file)
31
+ _send_file_check_file_readable(file)
37
32
  headers = {}
38
33
  mime_type = options.fetch(:mime_type, nil)
39
34
  # Calculate MIME type if it was not already specified.
@@ -41,15 +36,28 @@ module Racket
41
36
  headers['Content-Type'] = mime_type
42
37
  # Set Content-Disposition (and a file name) if the file should be downloaded
43
38
  # instead of displayed inline.
44
- if options.fetch(:download, false)
45
- filename = options.fetch(:filename, nil).to_s
46
- headers['Content-Disposition'] = 'attachment'
47
- headers['Content-Disposition'] << sprintf('; filename="%s"', filename) unless
48
- filename.empty?
49
- end
39
+ _send_file_set_content_disposition(options, headers)
50
40
  # Send response
51
41
  respond!(200, headers, ::File.read(file))
52
42
  end
43
+
44
+ private
45
+
46
+ def _send_file_check_file_readable(file)
47
+ # Respond with a 404 Not Found if the file cannot be read.
48
+ respond!(
49
+ 404,
50
+ { 'Content-Type' => 'text/plain' },
51
+ Rack::Utils::HTTP_STATUS_CODES[404]
52
+ ) unless Utils.file_readable?(file)
53
+ end
54
+
55
+ def _send_file_set_content_disposition(options, headers)
56
+ return unless options.fetch(:download, false)
57
+ filename = options.fetch(:filename, nil).to_s
58
+ headers['Content-Disposition'] = 'attachment'
59
+ headers['Content-Disposition'] << format('; filename="%s"', filename) unless filename.empty?
60
+ end
53
61
  end
54
62
  end
55
63
  end
data/lib/racket/router.rb CHANGED
@@ -23,38 +23,41 @@ require 'http_router'
23
23
  module Racket
24
24
  # Handles routing in Racket applications.
25
25
  class Router
26
+ attr_reader :action_cache
27
+ attr_reader :routes
28
+
26
29
  def initialize
27
30
  @router = HttpRouter.new
28
- @routes_by_controller = {}
29
- @actions_by_controller = {}
31
+ @routes = {}
32
+ @action_cache = {}
30
33
  end
31
34
 
32
35
  # Caches available actions for each controller class. This also works for controller classes
33
36
  # that inherit from other controller classes.
34
37
  #
35
- # @param [Class] controller
38
+ # @param [Class] controller_class
36
39
  # @return [nil]
37
- def cache_actions(controller)
40
+ def cache_actions(controller_class)
38
41
  actions = SortedSet.new
39
- current = controller
40
- while current < Controller
41
- actions.merge(current.instance_methods(false))
42
- current = current.superclass
42
+ current_class = controller_class
43
+ while current_class < Controller
44
+ actions.merge(current_class.public_instance_methods(false))
45
+ current_class = current_class.superclass
43
46
  end
44
- (@actions_by_controller[controller] = actions.to_a) && nil
47
+ (@action_cache[controller_class] = actions.to_a) && nil
45
48
  end
46
49
 
47
50
  # Returns a route to the specified controller/action/parameter combination.
48
51
  #
49
- # @param [Class] controller
52
+ # @param [Class] controller_class
50
53
  # @param [Symbol] action
51
54
  # @param [Array] params
52
55
  # @return [String]
53
- def get_route(controller, action, params)
54
- fail "Cannot find controller #{controller}" unless @routes_by_controller.key?(controller)
56
+ def get_route(controller_class, action, params)
57
+ fail "Cannot find controller #{controller_class}" unless @routes.key?(controller_class)
55
58
  params.flatten!
56
59
  route = ''
57
- route << @routes_by_controller[controller]
60
+ route << @routes[controller_class]
58
61
  route << "/#{action}" unless action.nil?
59
62
  route << "/#{params.join('/')}" unless params.empty?
60
63
  route = route[1..-1] if route.start_with?('//') # Special case for root path
@@ -64,14 +67,14 @@ module Racket
64
67
  # Maps a controller to the specified path.
65
68
  #
66
69
  # @param [String] path
67
- # @param [Class] controller
70
+ # @param [Class] controller_class
68
71
  # @return [nil]
69
- def map(path, controller)
70
- controller_base_path = path.empty? ? '/' : path
71
- Application.inform_dev("Mapping #{controller} to #{controller_base_path}.")
72
- @router.add("#{path}(/*params)").to(controller)
73
- @routes_by_controller[controller] = controller_base_path
74
- cache_actions(controller) && nil
72
+ def map(path, controller_class)
73
+ controller_class_base_path = path.empty? ? '/' : path
74
+ Application.inform_dev("Mapping #{controller_class} to #{controller_class_base_path}.")
75
+ @router.add("#{path}(/*params)").to(controller_class)
76
+ @routes[controller_class] = controller_class_base_path
77
+ cache_actions(controller_class) && nil
75
78
  end
76
79
 
77
80
  # @todo: Allow the user to set custom handlers for different errors
@@ -105,7 +108,7 @@ module Racket
105
108
  action = params.empty? ? target_klass.get_option(:default_action) : params.shift.to_sym
106
109
 
107
110
  # Check if action is available on target
108
- return render_error(404) unless @actions_by_controller[target_klass].include?(action)
111
+ return render_error(404) unless @action_cache[target_klass].include?(action)
109
112
 
110
113
  # Rewrite PATH_INFO to reflect that we split out the parameters
111
114
  env['PATH_INFO'] = env['PATH_INFO']
data/lib/racket/utils.rb CHANGED
@@ -19,6 +19,38 @@
19
19
  module Racket
20
20
  # Collects utilities needed by different objects in Racket.
21
21
  class Utils
22
+ # Handles exceptions dynamically
23
+ class ExceptionHandler
24
+ # Runs a block.
25
+ # If no exceptions are raised, this method returns true.
26
+ # If any of the provided error types are raised, this method returns false.
27
+ # If any other exception is raised, this method will just forward the exception.
28
+ #
29
+ # @param [Array] errors
30
+ # @return [true|flase]
31
+ def self.run_block(errors)
32
+ fail 'Need a block' unless block_given?
33
+ begin
34
+ true.tap { yield }
35
+ rescue boolean_module(errors)
36
+ false
37
+ end
38
+ end
39
+
40
+ # Returns an anonymous module that can be used to rescue exceptions dynamically.
41
+ def self.boolean_module(errors)
42
+ Module.new do
43
+ (class << self; self; end).instance_eval do
44
+ define_method(:===) do |error|
45
+ errors.any? { |e| error.class <= e }
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ private_class_method :boolean_module
52
+ end
53
+
22
54
  # Builds and returns a path in the file system from the provided arguments. The first element
23
55
  # in the argument list can be either absolute or relative, all other arguments must be relative,
24
56
  # otherwise they will be removed from the final path.
@@ -29,7 +61,7 @@ module Racket
29
61
  if args.empty?
30
62
  path = Pathname.pwd
31
63
  else
32
- args.map! { |arg| arg.to_s }
64
+ args.map!(&:to_s)
33
65
  path = Pathname.new(args.shift)
34
66
  path = Pathname.new(Application.options[:root_dir]).join(path) if path.relative?
35
67
  args.each do |arg|
@@ -50,5 +82,16 @@ module Racket
50
82
  pathname = Pathname.new(path)
51
83
  pathname.exist? && pathname.file? && pathname.readable?
52
84
  end
85
+
86
+ # Runs a block.
87
+ # If no exceptions are raised, this method returns true.
88
+ # If any of the provided error types are raised, this method returns false.
89
+ # If any other exception is raised, this method will just forward the exception.
90
+ #
91
+ # @param [Array] errors
92
+ # @return [true|flase]
93
+ def self.run_block(*errors, &block)
94
+ ExceptionHandler.run_block(errors, &block)
95
+ end
53
96
  end
54
97
  end
@@ -25,7 +25,7 @@ module Racket
25
25
  # Minor version
26
26
  MINOR = 2
27
27
  # Teeny version
28
- TEENY = 1
28
+ TEENY = 2
29
29
  # Is it a prerelease?
30
30
  PRERELEASE = false
31
31
 
@@ -21,7 +21,6 @@ require 'tilt'
21
21
  module Racket
22
22
  # Handles rendering in Racket applications.
23
23
  class ViewManager
24
-
25
24
  attr_reader :layout_cache
26
25
  attr_reader :view_cache
27
26
 
@@ -77,6 +76,26 @@ module Racket
77
76
  proc.call(*proc_args).to_s
78
77
  end
79
78
 
79
+ # Returns a cached template. If the template has not been cached yet, this method will run a
80
+ # lookup against the provided parameters.
81
+ #
82
+ # @param [String] path
83
+ # @param [Racket::Controller] controller
84
+ # @param [Symbol] type
85
+ def ensure_in_cache(path, controller, type)
86
+ store = instance_variable_get("@#{type}_cache".to_sym)
87
+ return store[path] if store.key?(path)
88
+ base_dir = instance_variable_get("@#{type}_base_dir".to_sym)
89
+ default_template = controller.controller_option("default_#{type}".to_sym)
90
+ template = lookup_template(base_dir, path)
91
+ template =
92
+ lookup_default_template(base_dir, File.dirname(path), default_template) unless template
93
+ Application.inform_dev(
94
+ "Using #{type} #{template.inspect} for #{controller.class}.#{controller.racket.action}."
95
+ )
96
+ store[path] = template
97
+ end
98
+
80
99
  # Tries to locate a layout matching +path+ in the file system and returns the path if a
81
100
  # matching file is found. If no matching file is found, +nil+ is returned. The result is cached,
82
101
  # meaning that the filesystem lookup for a specific path will only happen once.
@@ -85,18 +104,7 @@ module Racket
85
104
  # @param [Racket::Controller] controller
86
105
  # @return [String|nil]
87
106
  def get_layout(path, controller)
88
- unless @layout_cache.key?(path)
89
- layout = lookup_template(@layout_base_dir, path)
90
- layout =
91
- lookup_default_template(
92
- @layout_base_dir, File.dirname(path), controller.controller_option(:default_layout)
93
- ) unless layout
94
- Application.inform_dev(
95
- "Using layout #{layout.inspect} for #{controller.class}.#{controller.racket.action}."
96
- )
97
- @layout_cache[path] = layout
98
- end
99
- layout = @layout_cache[path]
107
+ layout = ensure_in_cache(path, controller, :layout)
100
108
  if layout.is_a?(Proc)
101
109
  layout =
102
110
  lookup_template(
@@ -115,18 +123,7 @@ module Racket
115
123
  # @param [Racket::Controller] controller
116
124
  # @return [String|nil]
117
125
  def get_view(path, controller)
118
- unless @view_cache.key?(path)
119
- view = lookup_template(@view_base_dir, path)
120
- view =
121
- lookup_default_template(
122
- @view_base_dir, File.dirname(path), controller.controller_option(:default_view)
123
- ) unless view
124
- Application.inform_dev(
125
- "Using view #{view.inspect} for #{controller.class}.#{controller.racket.action}."
126
- )
127
- @view_cache[path] = view
128
- end
129
- view = @view_cache[path]
126
+ view = ensure_in_cache(path, controller, :view)
130
127
  if view.is_a?(Proc)
131
128
  view =
132
129
  lookup_template(
data/rake/utils.rb ADDED
@@ -0,0 +1,36 @@
1
+ # Racket - The noisy Rack MVC framework
2
+ # Copyright (C) 2015 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
+ def racket_version
20
+ mod = Module.new
21
+ mod.module_eval(
22
+ File.read(
23
+ File.join(File.dirname(File.dirname(__FILE__)), 'lib', 'racket', 'version.rb')
24
+ )
25
+ )
26
+ mod::Racket::Version.current
27
+ end
28
+
29
+ def racket_files
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)
35
+ end
36
+ end
data/spec/_custom.rb CHANGED
@@ -50,14 +50,14 @@ describe 'A custom Racket test Application' do
50
50
  last_response.headers.key?('X-Hook-Action').should.equal(true)
51
51
  last_response.headers['X-Hook-Action'].should.equal('run')
52
52
  response = JSON.parse(last_response.body)
53
- response.should.equal(["Data added in before block", "Data added in action"])
53
+ response.should.equal(['Data added in before block', 'Data added in action'])
54
54
  end
55
55
 
56
56
  it 'should let Rack::ShowExceptions handle the error' do
57
57
  get '/sub1/epic_fail'
58
58
  last_response.status.should.equal(500)
59
59
  last_response.headers['Content-Type'].should.equal('text/plain')
60
- last_response.body.should.match(%r(^RuntimeError: Epic fail!))
60
+ last_response.body.should.match(/^RuntimeError: Epic fail!/)
61
61
  end
62
62
 
63
63
  it 'should be able to render custom files' do
@@ -104,7 +104,7 @@ describe 'A custom Racket test Application' do
104
104
  last_response.status.should.equal(404)
105
105
  last_response.headers['Content-Type'].should.equal('text/plain')
106
106
  last_response.headers.key?('Content-Disposition').should.equal(false)
107
- last_response.body.should.equal("Not Found")
107
+ last_response.body.should.equal('Not Found')
108
108
  end
109
109
 
110
110
  it 'should be able to handle dynamic layouts and views' do
data/spec/_default.rb CHANGED
@@ -10,33 +10,27 @@ describe 'A default Racket test Application' do
10
10
  end
11
11
 
12
12
  it 'has mapped controllers correctly' do
13
- routes_by_controller = actions_by_controller = nil
14
- app.instance_eval do
15
- router.instance_eval do
16
- routes_by_controller = @routes_by_controller
17
- actions_by_controller = @actions_by_controller
18
- end
19
- end
20
-
21
- routes_by_controller[DefaultRootController].should.equal('/')
22
- routes_by_controller[DefaultSubController1].should.equal('/sub1')
23
- routes_by_controller[DefaultSubController2].should.equal('/sub2')
24
- routes_by_controller[DefaultSubController3].should.equal('/sub3')
25
- routes_by_controller[DefaultInheritedController].should.equal('/sub3/inherited')
26
-
27
- actions_by_controller[DefaultRootController].length.should.equal(5)
28
- actions_by_controller[DefaultRootController].include?(:index).should.equal(true)
29
- actions_by_controller[DefaultRootController].include?(:my_first_route).should.equal(true)
30
- actions_by_controller[DefaultRootController].include?(:my_second_route).should.equal(true)
31
- actions_by_controller[DefaultSubController1].length.should.equal(4)
32
- actions_by_controller[DefaultSubController1].include?(:route_to_root).should.equal(true)
33
- actions_by_controller[DefaultSubController1].include?(:route_to_nonexisting).should.equal(true)
34
- actions_by_controller[DefaultSubController2].length.should.equal(5)
35
- actions_by_controller[DefaultSubController2].include?(:index).should.equal(true)
36
- actions_by_controller[DefaultSubController2].include?(:current_action).should.equal(true)
37
- actions_by_controller[DefaultSubController2].include?(:current_params).should.equal(true)
38
- actions_by_controller[DefaultSubController3].should.equal([:index])
39
- actions_by_controller[DefaultInheritedController].should.equal([:index])
13
+ app.router.routes.length.should.equal(5)
14
+ app.router.routes[DefaultRootController].should.equal('/')
15
+ app.router.routes[DefaultSubController1].should.equal('/sub1')
16
+ app.router.routes[DefaultSubController2].should.equal('/sub2')
17
+ app.router.routes[DefaultSubController3].should.equal('/sub3')
18
+ app.router.routes[DefaultInheritedController].should.equal('/sub3/inherited')
19
+
20
+ app.router.action_cache[DefaultRootController].length.should.equal(5)
21
+ app.router.action_cache[DefaultRootController].include?(:index).should.equal(true)
22
+ app.router.action_cache[DefaultRootController].include?(:my_first_route).should.equal(true)
23
+ app.router.action_cache[DefaultRootController].include?(:my_second_route).should.equal(true)
24
+ app.router.action_cache[DefaultSubController1].length.should.equal(4)
25
+ app.router.action_cache[DefaultSubController1].include?(:route_to_root).should.equal(true)
26
+ app.router.action_cache[DefaultSubController1].include?(:route_to_nonexisting)
27
+ .should.equal(true)
28
+ app.router.action_cache[DefaultSubController2].length.should.equal(5)
29
+ app.router.action_cache[DefaultSubController2].include?(:index).should.equal(true)
30
+ app.router.action_cache[DefaultSubController2].include?(:current_action).should.equal(true)
31
+ app.router.action_cache[DefaultSubController2].include?(:current_params).should.equal(true)
32
+ app.router.action_cache[DefaultSubController3].should.equal([:index])
33
+ app.router.action_cache[DefaultInheritedController].should.equal([:index])
40
34
  end
41
35
 
42
36
  it 'should set rack variables correctly' do
@@ -46,7 +40,7 @@ describe 'A default Racket test Application' do
46
40
 
47
41
  get '/sub2/current_params/foo/bar/baz'
48
42
  last_response.status.should.equal(200)
49
- JSON.parse(last_response.body).should.equal(['foo', 'bar', 'baz'])
43
+ JSON.parse(last_response.body).should.equal(%w(foo bar baz))
50
44
  end
51
45
 
52
46
  it 'returns the correct respnse when calling index action' do
@@ -107,27 +101,27 @@ describe 'A default Racket test Application' do
107
101
  end
108
102
 
109
103
  it 'should be able to log messages to everybody' do
110
- _logger = app.options[:logger]
104
+ original_logger = app.options[:logger]
111
105
  sio = StringIO.new
112
106
  app.options[:logger] = Logger.new(sio)
113
- app.inform_all('Informational message');
107
+ app.inform_all('Informational message')
114
108
  sio.string.should.match(/Informational message/)
115
- app.options[:logger] = _logger
109
+ app.options[:logger] = original_logger
116
110
  end
117
111
 
118
112
  it 'should be able to log messages to developer' do
119
- _logger = app.options[:logger]
120
- _mode = app.options[:mode]
113
+ original_logger = app.options[:logger]
114
+ original_mode = app.options[:mode]
121
115
  sio = StringIO.new
122
116
  app.options[:logger] = Logger.new(sio)
123
117
  app.options[:mode] = :live
124
- app.inform_dev('Development message');
118
+ app.inform_dev('Development message')
125
119
  sio.string.should.be.empty
126
120
  app.options[:mode] = :dev
127
- app.inform_dev('Hey, listen up!');
128
- sio.string.should.match(%r(Hey, listen up!))
129
- app.options[:mode] = _mode
130
- app.options[:logger] = _logger
121
+ app.inform_dev('Hey, listen up!')
122
+ sio.string.should.match(/Hey, listen up!/)
123
+ app.options[:mode] = original_mode
124
+ app.options[:logger] = original_logger
131
125
  end
132
126
 
133
127
  it 'should be able to set and clear session variables' do
@@ -138,7 +132,7 @@ describe 'A default Racket test Application' do
138
132
  response.keys.should.be.empty
139
133
  get '/session_as_json?foo=bar'
140
134
  last_response.headers.keys.should.include('Set-Cookie')
141
- last_response.headers['Set-Cookie'].should.match(%r(racket.session=))
135
+ last_response.headers['Set-Cookie'].should.match(/racket.session=/)
142
136
  response = JSON.parse(last_response.body)
143
137
  response.class.should.equal(Hash)
144
138
  response.keys.length.should.equal(2)
@@ -146,7 +140,7 @@ describe 'A default Racket test Application' do
146
140
  response.keys.should.include('session_id')
147
141
  get '/session_as_json?baz=quux'
148
142
  last_response.headers.keys.should.include('Set-Cookie')
149
- last_response.headers['Set-Cookie'].should.match(%r(racket.session=))
143
+ last_response.headers['Set-Cookie'].should.match(/racket.session=/)
150
144
  response = JSON.parse(last_response.body)
151
145
  response.class.should.equal(Hash)
152
146
  response.keys.length.should.equal(3)
@@ -155,14 +149,14 @@ describe 'A default Racket test Application' do
155
149
  response.keys.should.include('session_id')
156
150
  get '/session_as_json?drop_session'
157
151
  last_response.headers.keys.should.include('Set-Cookie')
158
- last_response.headers['Set-Cookie'].should.match(%r(racket.session=))
152
+ last_response.headers['Set-Cookie'].should.match(/racket.session=/)
159
153
  response = JSON.parse(last_response.body)
160
154
  response.class.should.equal(Hash)
161
155
  response.keys.should.be.empty
162
156
  get '/session_strings'
163
157
  response = JSON.parse(last_response.body)
164
158
  response.length.should.equal(3)
165
- response.each { |elem| elem.should.match(%r(Racket::Session)) }
159
+ response.each { |elem| elem.should.match(/Racket::Session/) }
166
160
  end
167
161
 
168
162
  it 'should be able to build paths correctly' do
@@ -173,7 +167,7 @@ describe 'A default Racket test Application' do
173
167
  end
174
168
 
175
169
  it 'should handle GET parameters correctly' do
176
- get '/sub2/get_some_data/?data1=foo&data3=bar'
170
+ get '/sub2/some_get_data/?data1=foo&data3=bar'
177
171
  last_response.status.should.equal(200)
178
172
  response = JSON.parse(last_response.body, symbolize_names: true)
179
173
  response.class.should.equal(Hash)
@@ -184,7 +178,7 @@ describe 'A default Racket test Application' do
184
178
  end
185
179
 
186
180
  it 'should handle POST parameters correctly' do
187
- post '/sub2/post_some_data', { data1: 'foo', data3: 'bar' }
181
+ post '/sub2/some_post_data', data1: 'foo', data3: 'bar'
188
182
  last_response.status.should.equal(200)
189
183
  response = JSON.parse(last_response.body, symbolize_names: true)
190
184
  response.class.should.equal(Hash)
@@ -207,5 +201,4 @@ describe 'A default Racket test Application' do
207
201
  last_response.headers['Content-Type'].should.equal('text/plain')
208
202
  last_response.body.should.equal('500 Internal Server Error')
209
203
  end
210
-
211
204
  end
data/spec/_invalid.rb CHANGED
@@ -6,9 +6,8 @@ describe 'An invalid Racket test Application' do
6
6
  end
7
7
 
8
8
  it 'should never initialize' do
9
- lambda { get '/' }
9
+ -> { get '/' }
10
10
  .should.raise(RuntimeError)
11
11
  .message.should.equal('Application has already been initialized!')
12
12
  end
13
-
14
13
  end
data/spec/_request.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  describe 'Racket::Request should override Rack::Request correctly' do
2
-
3
2
  r = Racket::Request.new({}).freeze
4
3
 
5
4
  describe 'Racket::Request should inherit some methods from Rack::Request' do
@@ -45,5 +44,4 @@ describe 'Racket::Request should override Rack::Request correctly' do
45
44
  end
46
45
  end
47
46
  end
48
-
49
47
  end
data/spec/racket.rb CHANGED
@@ -11,8 +11,9 @@ if ENV['CI'] == 'true'
11
11
  SimpleCov.formatter = SimpleCov::Formatter::Codecov
12
12
  end
13
13
 
14
- TEST_DEFAULT_APP_DIR = File.absolute_path(File.join(File.dirname(__FILE__), 'test_default_app'))
15
- TEST_CUSTOM_APP_DIR = File.absolute_path(File.join(File.dirname(__FILE__), 'test_custom_app'))
14
+ TEST_DIR = File.absolute_path(File.dirname(__FILE__))
15
+ TEST_DEFAULT_APP_DIR = File.join(TEST_DIR, 'test_default_app')
16
+ TEST_CUSTOM_APP_DIR = File.join(TEST_DIR, 'test_custom_app')
16
17
 
17
18
  require 'racket'
18
19
 
@@ -22,9 +23,9 @@ require 'racket/helpers/file.rb'
22
23
  require 'rack/test'
23
24
  require 'bacon'
24
25
 
25
- require_relative '_request.rb'
26
+ require File.join(TEST_DIR, '_request.rb')
26
27
 
27
- Dir.chdir(TEST_DEFAULT_APP_DIR) { require_relative '_default.rb' }
28
- Dir.chdir(TEST_CUSTOM_APP_DIR) { require_relative '_custom.rb' }
28
+ Dir.chdir(TEST_DEFAULT_APP_DIR) { require File.join(TEST_DIR, '_default.rb') }
29
+ Dir.chdir(TEST_CUSTOM_APP_DIR) { require File.join(TEST_DIR, '_custom.rb') }
29
30
 
30
- require_relative '_invalid.rb'
31
+ require File.join(TEST_DIR, '_invalid.rb')
@@ -1,5 +1,5 @@
1
+ # Custom sub controller 1
1
2
  class CustomSubController1 < Racket::Controller
2
-
3
3
  helper :file
4
4
 
5
5
  def index
@@ -37,5 +37,4 @@ class CustomSubController1 < Racket::Controller
37
37
  def send_nonexisting_file
38
38
  send_file('files/no_such_thing.jpg')
39
39
  end
40
-
41
40
  end
@@ -1,5 +1,5 @@
1
+ # Custom sub controller 2
1
2
  class CustomSubController2 < Racket::Controller
2
-
3
3
  def index
4
4
  "#{self.class}::#{__method__}"
5
5
  end
@@ -30,5 +30,4 @@ class CustomSubController2 < Racket::Controller
30
30
  end
31
31
 
32
32
  helper :nonexisting
33
-
34
33
  end
@@ -1,5 +1,5 @@
1
+ # Custom sub controller 3
1
2
  class CustomSubController3 < Racket::Controller
2
-
3
3
  set_option(:top_secret, 42)
4
4
 
5
5
  def index
@@ -23,5 +23,4 @@ class CustomSubController3 < Racket::Controller
23
23
  obj.instance_eval { @secret = 42 }
24
24
  render_template('files/secret.erb', obj)
25
25
  end
26
-
27
26
  end
@@ -1,9 +1,8 @@
1
1
  require_relative '../custom_sub_controller_3.rb'
2
2
 
3
+ # Custom inherited controller
3
4
  class CustomInheritedController < CustomSubController3
4
-
5
5
  def index
6
6
  "#{self.class}::#{__method__}"
7
7
  end
8
-
9
8
  end
@@ -0,0 +1,25 @@
1
+ # Custom sub controller 4
2
+ class CustomSubController4 < Racket::Controller
3
+ set_option :default_layout, -> { 'layout.erb' }
4
+
5
+ set_option :default_view,
6
+ lambda { |action|
7
+ case action
8
+ when :foo then 'myfoo.erb'
9
+ when :bar then 'mybar.erb'
10
+ else 'default.erb'
11
+ end
12
+ }
13
+
14
+ def foo
15
+ @data = 'FOO'
16
+ end
17
+
18
+ def bar
19
+ @data = 'BAR'
20
+ end
21
+
22
+ def baz
23
+ @data = 'BAZ'
24
+ end
25
+ end
@@ -1,7 +1,7 @@
1
1
  require 'json'
2
2
 
3
+ # Default root controller
3
4
  class DefaultRootController < Racket::Controller
4
-
5
5
  def index
6
6
  "#{self.class}::#{__method__}"
7
7
  end
@@ -28,5 +28,4 @@ class DefaultRootController < Racket::Controller
28
28
  def session_strings
29
29
  [session.inspect, session.to_s, session.to_str].to_json
30
30
  end
31
-
32
31
  end
@@ -1,5 +1,5 @@
1
+ # Default sub controller 1
1
2
  class DefaultSubController1 < Racket::Controller
2
-
3
3
  def index
4
4
  "#{self.class}::#{__method__}"
5
5
  end
@@ -15,5 +15,4 @@ class DefaultSubController1 < Racket::Controller
15
15
  def epic_fail
16
16
  fail 'Epic fail!'
17
17
  end
18
-
19
18
  end
@@ -1,5 +1,5 @@
1
+ # Default sub controller 2
1
2
  class DefaultSubController2 < Racket::Controller
2
-
3
3
  def index
4
4
  "#{self.class}::#{__method__}"
5
5
  end
@@ -12,7 +12,7 @@ class DefaultSubController2 < Racket::Controller
12
12
  racket.params.to_json
13
13
  end
14
14
 
15
- def get_some_data
15
+ def some_get_data
16
16
  data = {}
17
17
  [:data1, :data2, :data3].each do |d|
18
18
  data[d] = request.get(d)
@@ -20,12 +20,11 @@ class DefaultSubController2 < Racket::Controller
20
20
  data.to_json
21
21
  end
22
22
 
23
- def post_some_data
23
+ def some_post_data
24
24
  data = {}
25
25
  [:data1, :data2, :data3].each do |d|
26
26
  data[d] = request.post(d)
27
27
  end
28
28
  data.to_json
29
29
  end
30
-
31
30
  end
@@ -1,7 +1,18 @@
1
+ # Default sub controller 3
1
2
  class DefaultSubController3 < Racket::Controller
2
-
3
3
  def index
4
4
  "#{self.class}::#{__method__}"
5
5
  end
6
6
 
7
+ protected
8
+
9
+ def protected_method
10
+ "I'm protected"
11
+ end
12
+
13
+ private
14
+
15
+ def private_method
16
+ "I'm private"
17
+ end
7
18
  end
@@ -1,9 +1,8 @@
1
1
  require_relative '../default_sub_controller_3.rb'
2
2
 
3
+ # Default inherited controller
3
4
  class DefaultInheritedController < DefaultSubController3
4
-
5
5
  def index
6
6
  "#{self.class}::#{__method__}"
7
7
  end
8
-
9
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: racket-mvc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lars Olsson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-24 00:00:00.000000000 Z
11
+ date: 2015-09-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http_router
@@ -159,6 +159,7 @@ files:
159
159
  - lib/racket/utils.rb
160
160
  - lib/racket/version.rb
161
161
  - lib/racket/view_manager.rb
162
+ - rake/utils.rb
162
163
  - spec/_custom.rb
163
164
  - spec/_default.rb
164
165
  - spec/_invalid.rb
@@ -168,7 +169,7 @@ files:
168
169
  - spec/test_custom_app/controllers/sub2/custom_sub_controller_2.rb
169
170
  - spec/test_custom_app/controllers/sub3/custom_sub_controller_3.rb
170
171
  - spec/test_custom_app/controllers/sub3/inherited/custom_inherited_controller.rb
171
- - spec/test_custom_app/controllers/sub4/default_sub_controller_4.rb
172
+ - spec/test_custom_app/controllers/sub4/custom_sub_controller_4.rb
172
173
  - spec/test_custom_app/extra/blob.rb
173
174
  - spec/test_custom_app/extra/blob/inner_blob.rb
174
175
  - spec/test_custom_app/files/plain_text.txt
@@ -1,26 +0,0 @@
1
- class CustomSubController4 < Racket::Controller
2
-
3
- set_option :default_layout, lambda { 'layout.erb' }
4
-
5
- set_option :default_view,
6
- lambda { |action|
7
- case action
8
- when :foo then 'myfoo.erb'
9
- when :bar then 'mybar.erb'
10
- else 'default.erb'
11
- end
12
- }
13
-
14
- def foo
15
- @data = 'FOO'
16
- end
17
-
18
- def bar
19
- @data = 'BAR'
20
- end
21
-
22
- def baz
23
- @data = 'BAZ'
24
- end
25
-
26
- end