nekonote-framework 1.0.0.pre.beta

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +49 -0
  4. data/bin/nekonote +45 -0
  5. data/data/structure/Gemfile +25 -0
  6. data/data/structure/config.ru +14 -0
  7. data/data/structure/handler/base.rb +21 -0
  8. data/data/structure/handler/error.rb +35 -0
  9. data/data/structure/handler/welcome.rb +11 -0
  10. data/data/structure/lib/.gitkeep +0 -0
  11. data/data/structure/preference/development/logger.yml +62 -0
  12. data/data/structure/preference/development/middlewares.rb +152 -0
  13. data/data/structure/preference/development/public.yml +29 -0
  14. data/data/structure/preference/development/route.yml +30 -0
  15. data/data/structure/preference/development/route_error.yml +28 -0
  16. data/data/structure/preference/development/route_include.yml +22 -0
  17. data/data/structure/preference/development/server/puma.rb +63 -0
  18. data/data/structure/preference/development/setting/example.yml +40 -0
  19. data/data/structure/preference/development/setting/site.yml +3 -0
  20. data/data/structure/preference/development/setting/welcome.yml +7 -0
  21. data/data/structure/public/css/layout/common.css +11 -0
  22. data/data/structure/public/css/layout/default.css +3 -0
  23. data/data/structure/public/css/layout/error.css +3 -0
  24. data/data/structure/public/css/welcome.css +47 -0
  25. data/data/structure/public/favicon.ico +0 -0
  26. data/data/structure/public/img/.gitkeep +0 -0
  27. data/data/structure/public/img/logo.png +0 -0
  28. data/data/structure/public/js/.gitkeep +0 -0
  29. data/data/structure/static/layout/default.tpl +19 -0
  30. data/data/structure/static/layout/error.tpl +15 -0
  31. data/data/structure/static/sass/welcome.scss +52 -0
  32. data/data/structure/static/template/error.tpl +4 -0
  33. data/data/structure/static/template/welcome/index.tpl +26 -0
  34. data/data/structure/tmp/pids/.gitkeep +0 -0
  35. data/lib/loader.rb +83 -0
  36. data/lib/nekonote.rb +9 -0
  37. data/lib/nekonote/cli.rb +702 -0
  38. data/lib/nekonote/cmd_parser.rb +55 -0
  39. data/lib/nekonote/core.rb +116 -0
  40. data/lib/nekonote/env.rb +56 -0
  41. data/lib/nekonote/exception/cli_error.rb +34 -0
  42. data/lib/nekonote/exception/error.rb +75 -0
  43. data/lib/nekonote/exception/handler_error.rb +5 -0
  44. data/lib/nekonote/exception/logger_error.rb +8 -0
  45. data/lib/nekonote/exception/page_cache_error.rb +6 -0
  46. data/lib/nekonote/exception/preference_error.rb +11 -0
  47. data/lib/nekonote/exception/view_error.rb +7 -0
  48. data/lib/nekonote/handler.rb +274 -0
  49. data/lib/nekonote/handler/protected_methods.rb +119 -0
  50. data/lib/nekonote/liquid/tag_env_get.rb +12 -0
  51. data/lib/nekonote/liquid/tag_setting_get.rb +12 -0
  52. data/lib/nekonote/logger.rb +135 -0
  53. data/lib/nekonote/page_cache.rb +111 -0
  54. data/lib/nekonote/preference.rb +215 -0
  55. data/lib/nekonote/puma.rb +131 -0
  56. data/lib/nekonote/rack/rack_static.rb +17 -0
  57. data/lib/nekonote/rack/rack_static_file.rb +19 -0
  58. data/lib/nekonote/rack/url_mapper.rb +193 -0
  59. data/lib/nekonote/rackup.rb +319 -0
  60. data/lib/nekonote/request.rb +295 -0
  61. data/lib/nekonote/setting.rb +59 -0
  62. data/lib/nekonote/spec.rb +22 -0
  63. data/lib/nekonote/util/filer.rb +69 -0
  64. data/lib/nekonote/util/process.rb +43 -0
  65. data/lib/nekonote/view.rb +398 -0
  66. data/lib/nekonote/yaml_access.rb +60 -0
  67. metadata +144 -0
@@ -0,0 +1,55 @@
1
+ module Nekonote
2
+ class CmdParser
3
+ # @param array argv
4
+ public
5
+ def initialize(argv)
6
+ @argv = argv
7
+ end
8
+
9
+ # @return bool
10
+ public
11
+ def version_option?
12
+ return @argv.index('-v') || @argv.index('--version')
13
+ end
14
+
15
+ # @return bool
16
+ public
17
+ def help_option?
18
+ return @argv.index('-h') || @argv.index('--help')
19
+ end
20
+
21
+ # @return bool
22
+ public
23
+ def root_option?
24
+ return @argv.index '--root'
25
+ end
26
+
27
+ # @return nil|mixed
28
+ public
29
+ def get_op_val_root
30
+ index = @argv.index '--root'
31
+ return nil if index == nil
32
+ return @argv[index+1]
33
+ end
34
+
35
+ # @return cmd, subcmd, val
36
+ public
37
+ def parse_un_options
38
+ argv = @argv.clone
39
+
40
+ # delete options
41
+ index = argv.index '--root'
42
+ if index != nil
43
+ argv.delete_at(index+1) if argv[index+1] != nil
44
+ argv.delete_at index
45
+ end
46
+
47
+ # untaint
48
+ argv.map! do |v|
49
+ v.intern.to_s if v.is_a? String
50
+ end
51
+
52
+ return *argv
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,116 @@
1
+ module Nekonote
2
+ @@env ||= ENV['NEKONOTE_ENV']
3
+ @@root = nil
4
+ @@root_path = nil
5
+ @@logger_enabled = nil
6
+ @@logger_write_exception = nil
7
+
8
+ # set root directory to the application
9
+ # @param string root
10
+ def self.set_root(root)
11
+ if root.is_a?(String) && @@root != ''
12
+ root = File.expand_path root
13
+ root_path = root + '/'
14
+ @@root ||= root
15
+ @@root_path ||= root_path
16
+ else
17
+ # root is empty
18
+ raise Error, Error::MSG_MISSING_ROOT
19
+ end
20
+
21
+ # root exist?
22
+ if !Util::Filer.available_dir? @@root
23
+ raise Error, Error::MSG_MISSING_DIR% @@root
24
+ end
25
+
26
+ # config.ru exist?
27
+ if !Util::Filer.available_file? @@root_path + 'config.ru'
28
+ msg = Error::MSG_MISSING_RACKUP% @@root_path + $/
29
+ msg += Error::SPACE + Error::MSG_WRONG_ROOT
30
+ raise Error, msg
31
+ end
32
+ end
33
+
34
+ # @return string
35
+ def self.get_env
36
+ if !has_valid_env?
37
+ raise Error, Error::MSG_MISSING_ENV
38
+ end
39
+ return @@env
40
+ end
41
+
42
+ # @return bool
43
+ def self.has_valid_env?
44
+ return @@env.is_a?(String) && @@env != '' && @@env.match('/') == nil && @@env.match('\*') == nil
45
+ end
46
+
47
+ # @return string
48
+ def self.get_root
49
+ if @@root == nil
50
+ raise Error, Error::MSG_NOT_DEFINED_ROOT
51
+ end
52
+ return @@root
53
+ end
54
+
55
+ # @return string
56
+ def self.get_root_path
57
+ if @@root_path == nil
58
+ raise Error, Error::MSG_NOT_DEFINED_ROOT
59
+ end
60
+ return @@root_path
61
+ end
62
+
63
+ # @return bool
64
+ def self.has_root?
65
+ return @@root != nil
66
+ end
67
+
68
+ # @return bool
69
+ def self.from_cli?
70
+ return defined?(@@from_cli) && @@from_cli
71
+ end
72
+
73
+ # logger is enabled or not?
74
+ # @return bool
75
+ def self.logger_enabled?
76
+ return @@logger_enabled
77
+ end
78
+
79
+ # @return bool
80
+ def self.need_logging_exception?
81
+ return logger_enabled? && @@logger_write_exception
82
+ end
83
+
84
+ # load and initialize logger if it has been enabled
85
+ def self.init_logger
86
+ pref_logger = Preference.instance.get_logger true # don't use property cache
87
+
88
+ if pref_logger['write_exception'] == true
89
+ @@logger_write_exception = true
90
+ else
91
+ @@logger_write_exception = false
92
+ end
93
+
94
+ if pref_logger['enabled'] == true
95
+ # the method for logger will be defined
96
+ Logger.instance.init pref_logger
97
+ @@logger_enabled = true
98
+ else
99
+ # remove the defined method for logging
100
+ if Object.respond_to? :logwrite, true
101
+ Object.class_eval { remove_method :logwrite }
102
+ end
103
+ @@logger_enabled = false
104
+ end
105
+ end
106
+
107
+ # load base handler from app root
108
+ def self.load_base_handler
109
+ begin
110
+ require get_root_path + 'handler/base'
111
+
112
+ rescue LoadError
113
+ raise LoadError, Error::MSG_MISSING_FILE% path
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,56 @@
1
+ module Nekonote
2
+ class Env
3
+ private_class_method :new
4
+ @rackenv = {}
5
+
6
+ # Get values in a specified field
7
+ # @param string|synbol|nil field
8
+ # @return mixed|nil in the case of that missing specified key or missing fields will return nil
9
+ def self.get(field = nil)
10
+ return nil if field == nil
11
+
12
+ if !field.is_a? String
13
+ field = field.to_s
14
+ end
15
+
16
+ field.strip!
17
+ return '' if field == ''
18
+
19
+ return @rackenv[field]
20
+ end
21
+
22
+ # Get the whole environments
23
+ # @return hash
24
+ def self.get_all
25
+ return @rackenv
26
+ end
27
+
28
+ # @return string
29
+ def self.current
30
+ return Nekonote.get_env
31
+ end
32
+
33
+ # @return string
34
+ def self.root
35
+ return Nekonote.get_root
36
+ end
37
+
38
+ # @return string
39
+ def self.root_path
40
+ return Nekonote.get_root_path
41
+ end
42
+
43
+ # @return array|nil
44
+ def self.keys
45
+ if @rackenv.is_a? Hash
46
+ return @rackenv.keys
47
+ end
48
+ return nil
49
+ end
50
+
51
+ # @param hash rackenv
52
+ def self.set_rackenv(rackenv)
53
+ @rackenv = rackenv
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,34 @@
1
+ module Nekonote
2
+ class CLIError < Error
3
+ MSG_PERMIT_MAKE_DIR = %{Failed to generate an application structure in '%s' because of invalid permission or not found.}
4
+ MSG_UNABLE_TO_LOAD = %(Unable to load %s. Did you really install it?)
5
+
6
+ MSG_MISSING_AFTER_NEW = %(Please set something after new.)
7
+ MSG_MISSING_NEW_PATH = %(Please set some name to generate.)
8
+ MSG_MISSING_STRUCTURE_DIR = %(The specified directory was not found. Please make sure '%s' directory exists on your server.)
9
+ MSG_MISSING_DEST_PATH = %('%s' was not found in the application structure.)
10
+ MSG_MISSING_AFTER_SERVER = %('nekonote server' requires some sub-command.)
11
+
12
+ MSG_INVALID_NEW_NAME = %(Invalid name '%s'.)
13
+ MSG_INVALID_HANDLER_NAME = %(Invalid handler name '%s'.)
14
+ MSG_INVALID_TEMPLATE_NAME = %(Invalid template name '%s'.)
15
+ MSG_INVALID_LAYOUT_NAME = %(Invalid layout name '%s'.)
16
+ MSG_INVALID_ENV_NAME = %(Invalid environment name '%s'.)
17
+
18
+ MSG_ALREADY_EXISTS_APP = %('%s' exists already. You need to remove it at first.)
19
+ MSG_ALREADY_EXISTS_HANDLER = %(Handler '%s' exists already. Nothing to do.)
20
+ MSG_ALREADY_EXISTS_TEMPLATE = %(Template '%s' exists already. Nothing to do.)
21
+ MSG_ALREADY_EXISTS_LAYOUT = %(Layout '%s' exists already. Nothing to do.)
22
+ MSG_ALREADY_EXISTS_ENV = %(Environment '%s' exists already. Nothing to do.)
23
+
24
+ MSG_FAILED_CREATE_HANDLER = %(Failed to create a handler by the following reason '%s')
25
+ MSG_FAILED_CREATE_TEMPLATE = %(Failed to create a template by the following reason '%s')
26
+ MSG_FAILED_CREATE_LAYOUT = %(Failed to create a layout by the following reason '%s')
27
+ MSG_FAILED_GEN_DIR_BY_FILE = %(Failed to create a directory '%s'. There is the file which the same name.)
28
+ MSG_FAILED_START_SERVER = %(Failed to start the web server. Please check the error message.)
29
+
30
+ MSG_UNKNOWN_AFTER_NEW = %(Unknown sub command 'nekonote new %s'.)
31
+ MSG_UNKNOWN_AFTER_SERVER = %(Unknown sub command 'nekonote server %s'.)
32
+ MSG_UNKNOWN_SUB_COMMAND = %(Unknown sub command '%s'. Typing 'nekonote -h' will help you.)
33
+ end
34
+ end
@@ -0,0 +1,75 @@
1
+ module Nekonote
2
+ # static methods class
3
+ class Error < StandardError
4
+ SPACE = ' '
5
+ MSG_MISSING_ENV = %(Environment variable NEKONOTE_ENV is empty or invalid environment name is set.)
6
+ MSG_MISSING_ROOT = %(Missing root directory to the application structure.)
7
+ MSG_NOT_DEFINED_ROOT = %(Application root is not defined yet.)
8
+ MSG_MISSING_FILE = %(No such file '%s' or can't read it.)
9
+ MSG_MISSING_DIR = %(No such directory '%s' or can't read it.)
10
+ MSG_MISSING_RACKUP = %(Missing config.ru under '%s' or can't read it.)
11
+ MSG_LACK_FIELD_IN_YAML = %(Lack of the required field '%s'. Please check if '%s' is set properly.)
12
+ MSG_EMPTY_YAML = %('%s' is empty. You must configure something.)
13
+ MSG_WRONG_ROOT = %(You'd better run the command at the application root, or --root option will solve the problem.)
14
+ MSG_WRONG_TYPE = %(You gave %s but the expected type is '%s'.)
15
+ MSG_MISSING_FIELD = %(The required field '%s' was not found in '%s'.)
16
+ MSG_INVALID_FIELD = %(Invalid format field '%s' in '%s'.)
17
+ MSG_MISSING_CONST = %(Missing such class or module or contant -> '%s'.)
18
+ MSG_EMPTY_FILE_NOT_EMPTY = %('%s' is not empty. Failed to create an empty file.)
19
+
20
+ # write message as warning to log file if logging is enabled
21
+ # @param string msg
22
+ def self.warning(msg)
23
+ logging_warn msg
24
+
25
+ begin
26
+ if Nekonote.from_cli?
27
+ warn 'Warning: ' + msg
28
+ end
29
+ rescue
30
+ end
31
+ end
32
+
33
+ # @param StandardError e
34
+ def self.abort(e)
35
+ logging_error e
36
+
37
+ # if executed from cli do not throw exception
38
+ if Nekonote.from_cli?
39
+ warn "#{e.class}; =(-x-=;)" + $/
40
+ warn SPACE + e.message + $/ + $/
41
+ if !e.is_a? Nekonote::Error
42
+ warn e.backtrace
43
+ end
44
+ exit 1
45
+ end
46
+
47
+ raise e
48
+ end
49
+
50
+ # write error informaton to log file if logging is enabled
51
+ # @param string msg
52
+ def self.logging_warn(msg)
53
+ begin
54
+ if Nekonote.need_logging_exception?
55
+ logwrite msg, Nekonote::Logger::WARN
56
+ Nekonote::Logger.instance.set_level_default
57
+ end
58
+ rescue
59
+ end
60
+ end
61
+
62
+ # write error informaton to log file if logging is enabled
63
+ # @param StandardError e
64
+ def self.logging_error(e)
65
+ begin
66
+ if Nekonote.need_logging_exception?
67
+ msg = %(#{e.class} - #{e.message}) + $/ + e.backtrace.join($/)
68
+ logwrite msg, Nekonote::Logger::FATAL
69
+ Nekonote::Logger.instance.set_level_default
70
+ end
71
+ rescue
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,5 @@
1
+ module Nekonote
2
+ class HandlerError < Error
3
+ MSG_MISSING_ERR_HANDLER = 'No Handler in route_error.yml.';
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ module Nekonote
2
+ class LoggerError < Error
3
+ MSG_MISSING_LOGFILE = %(Lack of the required directives 'logfile' and 'keep' and 'limit' in logger.yml.)
4
+ MSG_FAILED_INITIALIZE = %(Failed to initialize logger. Wrong syntax in logger.yml or '%s' doesn't have a permission to create '%s'.)
5
+ MSG_FAILED_DEF_OPT = %(Failed to define optional value. Maybe there is wrong syntax in logger.yml. Please check the reference manual.)
6
+ MSG_UNDIFINED_LOG_LEVEL = %(Undifined log level was given. Please check the reference manual.)
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module Nekonote
2
+ class PageCacheError < Error
3
+ MSG_THERE_IS_DIRECTORY = %(There is the unknown directory under the 'cache' directory.)
4
+ MSG_CHACHE_DIR_WRONG_PERMIT = %(Wrong permission or the cache directory '%s' was not found.)
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module Nekonote
2
+ class PreferenceError < Error
3
+ MSG_UNDEFINED_FIELD = %(Undefined field '%s'.)
4
+ MSG_WRONG_YAML_SYNTAX = %(The yaml file '%s' is invalid syntax.)
5
+ MSG_MISSING_INCLUDE = %(No such field '%s' in %s.)
6
+ MSG_INVALID_HANDLER_NAME = %(Handler class name '%s' is invalid.)
7
+ MSG_NO_SUCH_HANDLER = %(No such handler class '%s'. You must make it at first.)
8
+ MSG_DUPLICATE_PATH = %(There is the duplicate path in %s. Values of 'path' directive are supposed to be uniq.)
9
+ MSG_EVAL_MIDDLEWARES = %(Unable to evaluate '%s' because there's something wrong.)
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Nekonote
2
+ class ViewError < Error
3
+ MSG_MISSING_TEMPLATE_FILE = %(Failed to load the tempalte file. No such file '%s')
4
+ MSG_MISSING_LAYOUT_FILE = %(Failed to load the layout file. No such file '%s')
5
+ MSG_FAILED_TO_ASSIGN = %(Failed to assign variables into templates. You gave wrong type. The expected type is Hash.)
6
+ end
7
+ end
@@ -0,0 +1,274 @@
1
+ module Nekonote
2
+ class Handler
3
+ EXIT = 'Nekonote exit from queue'
4
+ require Nekonote.get_lib_root_path + 'handler/protected_methods'
5
+ include ProtectedMethods
6
+
7
+ # =============================================
8
+ # Accessors
9
+ # =============================================
10
+ attr_accessor :route_regexp,
11
+ :url_path_params_mapper
12
+
13
+ # =============================================
14
+ # Static method
15
+ # =============================================
16
+ # make error handler and execute it
17
+ # @param string field
18
+ # @param hash env
19
+ # @param nil e
20
+ # @return array
21
+ def self.call_error_handler(field, env = {}, e = nil)
22
+ pref = Preference.instance.get_route_error
23
+
24
+ # field is expected String and pref[field] is expected Hash
25
+ if !field.is_a?(String) || !pref[field].is_a?(Hash)
26
+ raise HandlerError, HandlerError::MSG_MISSING_FIELD%[field, Preference.instance.path_route_error_yml]
27
+ end
28
+
29
+ # make handler newly
30
+ handler_name = pref[field][Preference::FIELD_ROUTE_HANDLER]
31
+ if handler_name.is_a? String
32
+ begin
33
+ handler_class = Object.const_get handler_name
34
+ error_handler = handler_class.new pref[field].clone, field, e
35
+ rescue
36
+ raise HandlerError, HandlerError::MSG_MISSING_CONST% handler_name
37
+ end
38
+ else
39
+ raise HandlerError, HandlerError::MSG_MISSING_ERR_HANDLER
40
+ end
41
+
42
+ return error_handler.call env
43
+ end
44
+
45
+
46
+ # It would be called only one time
47
+ # @param hash info route information
48
+ # @param bool|nil error_field_name
49
+ # @param StandardError error
50
+ def initialize(info={}, error_field_name = nil, error = nil)
51
+ # Both the properties are allowed to access in sub classes
52
+ @view = View.new (info || {}), self.class.to_s, error_field_name
53
+
54
+ @request = nil # Object of Nekonote::Request
55
+ @session = nil # Object about session management
56
+ @route_regexp = nil # Object of Regexp
57
+ @url_path_params_mapper = nil # Hash
58
+
59
+ # exception object will be set if fatal error occured
60
+ @error = error
61
+
62
+ # custom fields will be set, if no custom field just {} will be set
63
+ @custom_fields = Preference.get_custom_fields info, error_field_name.is_a?(String)
64
+ end
65
+
66
+ # reload preferences
67
+ private
68
+ def pref_reloader
69
+ path = Nekonote.get_root_path + Preference::FILE_NAME_FOR_RELOAD
70
+ if File.exist? path
71
+ # reload logger
72
+ Nekonote.init_logger
73
+
74
+ # reload setting
75
+ Setting.init
76
+
77
+ Util::Filer::safe_delete_empty_file path
78
+ end
79
+ end
80
+
81
+ # This method would be called from rack and called in every request
82
+ # @param hash env rack environment
83
+ public
84
+ def call(env)
85
+ # reload preferences if needed
86
+ pref_reloader
87
+
88
+ # get reponse data from page cache if it's available
89
+ if @view.can_get_from_page_cache?(env['REQUEST_URI'])
90
+ response_data = @view.get_response_data_from_page_cache env['REQUEST_URI']
91
+ if response_data.size == 3
92
+ # return the cache
93
+ return response_data
94
+ else
95
+ # invalid page cache file, put warning message
96
+ Error.warning 'Wrong page cache file was detected. Please remove page cache.'
97
+ end
98
+ end
99
+
100
+ # initialize response
101
+ @view.init_for_response
102
+
103
+ # set env to Nekonote::env
104
+ Env.set_rackenv env
105
+
106
+ # set session if it's enabled
107
+ @session = Env.get 'rack.session'
108
+
109
+ # set request
110
+ @request = Request.new env, @view.info_params
111
+
112
+ # set request body to request object if it exists
113
+ if env['rack.input'].is_a? StringIO
114
+ # reverting file pointer I don't know why the pointer moved just one...?
115
+ env['rack.input'].rewind
116
+ @request.set_payload env['rack.input'].read
117
+ end
118
+
119
+ # execute
120
+ if !@view.is_error_route
121
+ begin
122
+ return execute env
123
+
124
+ rescue StandardError, ScriptError => e
125
+ # fatal error occured
126
+ process_exception_raised e
127
+
128
+ # if ShowExceptions is disabled and fatal error route has been defined, forward custom error page
129
+ if Preference.instance.has_error_route? Preference::FIELD_ROUTE_ERR_FATAL
130
+ return Handler.call_error_handler Preference::FIELD_ROUTE_ERR_FATAL, Env.get_all, e
131
+ end
132
+ end
133
+
134
+ else
135
+ # If self is Handler for error routes
136
+ begin
137
+ execute_handler_methods @view.info_exec_method
138
+ return get_response_data
139
+
140
+ rescue StandardError, ScriptError => e
141
+ process_exception_raised e
142
+ end
143
+ end
144
+
145
+ # in the case of an exception raised but
146
+ # there is no fatal error route OR the exception raised in fatal error route
147
+ # and also ShowExceptons does not handle an exception
148
+ # returns an empty response
149
+ # TODO Do I have to change them to be customizable?
150
+ return View.get_default_error_response
151
+ end
152
+
153
+ # @param StandardError|ScriptError e
154
+ # @throws StandardError|ScriptError
155
+ private
156
+ def process_exception_raised(e)
157
+ # logging if logger is enabled
158
+ Error.logging_error e
159
+
160
+ # raise the exception for ShowExceptions if it's enabled
161
+ raise e if Preference.instance.is_enabled_show_exceptions
162
+ end
163
+
164
+ # It would be called by every request
165
+ # @param hash env
166
+ # @return array
167
+ # response_code int
168
+ # response_header hash
169
+ # response_body array
170
+ private
171
+ def execute(env)
172
+ # check path machs the expected path
173
+ if matched_route_strictly? @request.path, @view.info_path
174
+ # check request HTTP method
175
+ if is_allowed_http_method @request.method
176
+ # matched some route then call methods defined in concrete handlers
177
+ execute_handler_methods @view.info_exec_method
178
+ else
179
+ # request method error
180
+ return Handler.call_error_handler Preference::FIELD_ROUTE_ERR_WRONG_METHOD, Env.get_all
181
+ end
182
+
183
+ else
184
+ # doesn't match any routes
185
+ return Handler.call_error_handler Preference::FIELD_ROUTE_ERR_MISSING_ROUTE, Env.get_all
186
+ end
187
+
188
+ return get_response_data
189
+ end
190
+
191
+ # Check does requested path correspond with the given value to #map
192
+ # @return bool
193
+ private
194
+ def matched_route_strictly?(requested_path, expected_path)
195
+ match_data = @route_regexp.match requested_path
196
+ if match_data.is_a? MatchData
197
+ # set URL path parameters to Request object
198
+ if @url_path_params_mapper.is_a? Hash
199
+ stack = requested_path.split('/')
200
+ map = {}
201
+ @url_path_params_mapper.each_pair do |name, index|
202
+ map[name] = stack[index]
203
+ end
204
+ @request.assign_from_url map
205
+ end
206
+
207
+ return true
208
+
209
+ else
210
+ # doesn't match, the requested path is wrong
211
+ return false
212
+ end
213
+ end
214
+
215
+ # Get response data and return values as rack needed
216
+ # @return array
217
+ private
218
+ def get_response_data
219
+ # return response if redirection
220
+ if @view.is_redirect
221
+ return @view.get_response_data
222
+ end
223
+
224
+ # set response body with template and/or layout when response body hasn't been set by __set_body
225
+ @view.set_body_with_tpl if !@view.is_body_set?
226
+
227
+ # make page cache file if need it
228
+ if @view.need_create_page_cache? @request.uri
229
+ @view.create_page_cache @request.uri
230
+ end
231
+
232
+ return @view.get_response_data
233
+ end
234
+
235
+ # @param string|symbol|nil task method name defined in 'klass'
236
+ private
237
+ def execute_handler_methods(task)
238
+ if task != nil && !task.is_a?(String) && !task.is_a?(Symbol)
239
+ raise HandlerError, HandlerError::MSG_WRONG_TYPE%[task.class, 'String|Symbol|nil']
240
+ end
241
+
242
+ # execute methods when it's defined
243
+ # if __pre is defined execute it first
244
+ return if execute_method(:__pre, self) == EXIT
245
+ # execute particular method specified with routes.yml when it isn't nil
246
+ return if execute_method(task, self) == EXIT
247
+ # if __post is defined execute it last
248
+ execute_method :__post, self
249
+ end
250
+
251
+ # @param method symbol|string
252
+ private
253
+ def execute_method(method, handler)
254
+ if !@view.is_redirect && (method != nil) && handler.respond_to?(method, true)
255
+ handler.method(method).call
256
+ end
257
+ end
258
+
259
+ # Is given method allowed?
260
+ # @pram string method
261
+ # @return false|nil|int
262
+ private
263
+ def is_allowed_http_method(method)
264
+ return false if !method.is_a? String
265
+ http_methods = @view.info_allow_methods
266
+ return true if http_methods == nil
267
+ http_methods = http_methods.split(',')
268
+ http_methods.map! do |val|
269
+ val.strip
270
+ end
271
+ return http_methods.index method
272
+ end
273
+ end
274
+ end