nekonote-framework 1.0.0.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
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