nekonote-framework 1.0.0.pre.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +49 -0
- data/bin/nekonote +45 -0
- data/data/structure/Gemfile +25 -0
- data/data/structure/config.ru +14 -0
- data/data/structure/handler/base.rb +21 -0
- data/data/structure/handler/error.rb +35 -0
- data/data/structure/handler/welcome.rb +11 -0
- data/data/structure/lib/.gitkeep +0 -0
- data/data/structure/preference/development/logger.yml +62 -0
- data/data/structure/preference/development/middlewares.rb +152 -0
- data/data/structure/preference/development/public.yml +29 -0
- data/data/structure/preference/development/route.yml +30 -0
- data/data/structure/preference/development/route_error.yml +28 -0
- data/data/structure/preference/development/route_include.yml +22 -0
- data/data/structure/preference/development/server/puma.rb +63 -0
- data/data/structure/preference/development/setting/example.yml +40 -0
- data/data/structure/preference/development/setting/site.yml +3 -0
- data/data/structure/preference/development/setting/welcome.yml +7 -0
- data/data/structure/public/css/layout/common.css +11 -0
- data/data/structure/public/css/layout/default.css +3 -0
- data/data/structure/public/css/layout/error.css +3 -0
- data/data/structure/public/css/welcome.css +47 -0
- data/data/structure/public/favicon.ico +0 -0
- data/data/structure/public/img/.gitkeep +0 -0
- data/data/structure/public/img/logo.png +0 -0
- data/data/structure/public/js/.gitkeep +0 -0
- data/data/structure/static/layout/default.tpl +19 -0
- data/data/structure/static/layout/error.tpl +15 -0
- data/data/structure/static/sass/welcome.scss +52 -0
- data/data/structure/static/template/error.tpl +4 -0
- data/data/structure/static/template/welcome/index.tpl +26 -0
- data/data/structure/tmp/pids/.gitkeep +0 -0
- data/lib/loader.rb +83 -0
- data/lib/nekonote.rb +9 -0
- data/lib/nekonote/cli.rb +702 -0
- data/lib/nekonote/cmd_parser.rb +55 -0
- data/lib/nekonote/core.rb +116 -0
- data/lib/nekonote/env.rb +56 -0
- data/lib/nekonote/exception/cli_error.rb +34 -0
- data/lib/nekonote/exception/error.rb +75 -0
- data/lib/nekonote/exception/handler_error.rb +5 -0
- data/lib/nekonote/exception/logger_error.rb +8 -0
- data/lib/nekonote/exception/page_cache_error.rb +6 -0
- data/lib/nekonote/exception/preference_error.rb +11 -0
- data/lib/nekonote/exception/view_error.rb +7 -0
- data/lib/nekonote/handler.rb +274 -0
- data/lib/nekonote/handler/protected_methods.rb +119 -0
- data/lib/nekonote/liquid/tag_env_get.rb +12 -0
- data/lib/nekonote/liquid/tag_setting_get.rb +12 -0
- data/lib/nekonote/logger.rb +135 -0
- data/lib/nekonote/page_cache.rb +111 -0
- data/lib/nekonote/preference.rb +215 -0
- data/lib/nekonote/puma.rb +131 -0
- data/lib/nekonote/rack/rack_static.rb +17 -0
- data/lib/nekonote/rack/rack_static_file.rb +19 -0
- data/lib/nekonote/rack/url_mapper.rb +193 -0
- data/lib/nekonote/rackup.rb +319 -0
- data/lib/nekonote/request.rb +295 -0
- data/lib/nekonote/setting.rb +59 -0
- data/lib/nekonote/spec.rb +22 -0
- data/lib/nekonote/util/filer.rb +69 -0
- data/lib/nekonote/util/process.rb +43 -0
- data/lib/nekonote/view.rb +398 -0
- data/lib/nekonote/yaml_access.rb +60 -0
- metadata +144 -0
@@ -0,0 +1,398 @@
|
|
1
|
+
module Nekonote
|
2
|
+
# define original liquid tag
|
3
|
+
::Liquid::Template.register_tag 'env_get', TagEnvGet
|
4
|
+
::Liquid::Template.register_tag 'setting_get', TagSettingGet
|
5
|
+
|
6
|
+
class View
|
7
|
+
NO_USING_NAME = 'none'
|
8
|
+
DEFAULT_LAYOUT_NAME = 'default'
|
9
|
+
PATH_TO_TEMPLATE = 'static/template'
|
10
|
+
PATH_TO_LAYOUT = 'static/layout'
|
11
|
+
|
12
|
+
# accessor
|
13
|
+
attr_accessor :is_redirect
|
14
|
+
attr_reader :is_error_route,
|
15
|
+
:info_path,
|
16
|
+
:info_exec_method,
|
17
|
+
:info_allow_methods,
|
18
|
+
:info_params,
|
19
|
+
:info_content_type,
|
20
|
+
:info_template,
|
21
|
+
:info_layout,
|
22
|
+
:info_page_cache_time
|
23
|
+
|
24
|
+
# @return array
|
25
|
+
def self.get_default_error_response
|
26
|
+
return [
|
27
|
+
500,
|
28
|
+
{
|
29
|
+
},
|
30
|
+
[]
|
31
|
+
]
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param hash info
|
35
|
+
# @param string handler_name
|
36
|
+
# @param nil|string error_field_name
|
37
|
+
def initialize(info, handler_name, error_field_name = nil)
|
38
|
+
register_info_properies info
|
39
|
+
|
40
|
+
# initialize response
|
41
|
+
init_for_response
|
42
|
+
|
43
|
+
# check error route or not?
|
44
|
+
if error_field_name.is_a? String
|
45
|
+
@is_error_route = true
|
46
|
+
# set default response code for error (users can customize it on concrete handlers).
|
47
|
+
set_code get_error_response_code(error_field_name)
|
48
|
+
else
|
49
|
+
@is_error_route = false
|
50
|
+
end
|
51
|
+
|
52
|
+
# set template
|
53
|
+
if @info_template.is_a? String
|
54
|
+
# use template described in route.yml
|
55
|
+
@template_path = Nekonote.get_root_path + PATH_TO_TEMPLATE + '/' + @info_template + '.tpl'
|
56
|
+
elsif @info_template == nil
|
57
|
+
# set default if no template is specified
|
58
|
+
@template_path = get_default_template_path handler_name
|
59
|
+
else
|
60
|
+
# no template with response
|
61
|
+
@template_path = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# set layout
|
65
|
+
if @info_layout.is_a? String
|
66
|
+
# use layout described in route.yml
|
67
|
+
@layout_path = Nekonote.get_root_path + PATH_TO_LAYOUT + '/' + @info_layout + '.tpl'
|
68
|
+
elsif @info_layout == nil
|
69
|
+
# if no layout is specified, try to use default layout
|
70
|
+
@layout_path = Nekonote.get_root_path + PATH_TO_LAYOUT + '/' + DEFAULT_LAYOUT_NAME + '.tpl'
|
71
|
+
if !Util::Filer.available_file? @layout_path
|
72
|
+
# but if it's not available, no layout with response
|
73
|
+
@layout_path = nil
|
74
|
+
end
|
75
|
+
else
|
76
|
+
# no layout with response
|
77
|
+
@layout_path = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
# assign extra fields into templates
|
81
|
+
assign_custom_fields info
|
82
|
+
end
|
83
|
+
|
84
|
+
# Initialize stored information about response
|
85
|
+
public
|
86
|
+
def init_for_response
|
87
|
+
@response = ::Rack::Response.new
|
88
|
+
set_content_type @info_content_type
|
89
|
+
|
90
|
+
# initialize the properties
|
91
|
+
@is_body_set = false
|
92
|
+
@is_redirect = false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Is page cache enabled for this route.
|
96
|
+
# @return bool
|
97
|
+
public
|
98
|
+
def enable_page_cache?
|
99
|
+
return @info_page_cache_time.is_a? Integer
|
100
|
+
end
|
101
|
+
|
102
|
+
# Need to create page cache?
|
103
|
+
# @param string uri
|
104
|
+
# @return bool
|
105
|
+
public
|
106
|
+
def need_create_page_cache?(uri)
|
107
|
+
return enable_page_cache? && !PageCache.instance.has_available_cache?(uri, @info_page_cache_time)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Is it Allowed to gets response data from page cache for givevn uri?
|
111
|
+
# @param string uri
|
112
|
+
# @return bool
|
113
|
+
public
|
114
|
+
def can_get_from_page_cache?(uri)
|
115
|
+
return enable_page_cache? && PageCache.instance.has_available_cache?(uri, @info_page_cache_time)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Makes page cache file
|
119
|
+
# @param string uri
|
120
|
+
public
|
121
|
+
def create_page_cache(uri)
|
122
|
+
PageCache.instance.make_cache(
|
123
|
+
uri,
|
124
|
+
@response.status,
|
125
|
+
@response.header,
|
126
|
+
@response.body
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
# @return array
|
131
|
+
public
|
132
|
+
def get_response_data
|
133
|
+
return @response.finish
|
134
|
+
end
|
135
|
+
|
136
|
+
# Gets response data for given uri from page cache
|
137
|
+
# @param string uri
|
138
|
+
public
|
139
|
+
def get_response_data_from_page_cache(uri)
|
140
|
+
return PageCache.instance.get_page_cache uri
|
141
|
+
end
|
142
|
+
|
143
|
+
# @return string
|
144
|
+
public
|
145
|
+
def set_body_with_tpl
|
146
|
+
# if nil is given for layout and/or template, No template and/or layout will be used
|
147
|
+
if @template_path != nil && !Util::Filer.available_file?(@template_path)
|
148
|
+
raise ViewError, ViewError::MSG_MISSING_TEMPLATE_FILE% @template_path
|
149
|
+
end
|
150
|
+
|
151
|
+
if @layout_path != nil && !Util::Filer.available_file?(@layout_path)
|
152
|
+
raise ViewError, ViewError::MSG_MISSING_LAYOUT_FILE% @layout_path
|
153
|
+
end
|
154
|
+
|
155
|
+
@response.write get_parsed(@template_path, @layout_path)
|
156
|
+
end
|
157
|
+
|
158
|
+
# @param string|symbol subject
|
159
|
+
# @param mixed value
|
160
|
+
public
|
161
|
+
def set_header(subject, value)
|
162
|
+
subject = subject.to_s if subject.is_a?(Symbol)
|
163
|
+
raise ViewError, ViewError::MSG_WRONG_TYPE%[subject.class, 'String or Symbol'] if !subject.is_a?(String)
|
164
|
+
@response[subject] = value
|
165
|
+
end
|
166
|
+
|
167
|
+
# @param string type
|
168
|
+
public
|
169
|
+
def set_content_type(type)
|
170
|
+
@response['Content-Type'] = get_content_type type
|
171
|
+
end
|
172
|
+
|
173
|
+
# @param string|symbol subject
|
174
|
+
# @param mixed value
|
175
|
+
# @param string delimiter
|
176
|
+
public
|
177
|
+
def add_header(subject, value, delimiter)
|
178
|
+
subject = subject.to_s if subject.is_a?(Symbol)
|
179
|
+
raise ViewError, ViewError::MSG_WRONG_TYPE%[subject.class, 'String or Symbol'] if !subject.is_a?(String)
|
180
|
+
raise ViewError, ViewError::MSG_WRONG_TYPE%[delimiter.class, 'String'] if !delimiter.is_a?(String)
|
181
|
+
|
182
|
+
if @response.header.has_key? subject
|
183
|
+
@response[subject] = "#{@response[subject]}#{delimiter}#{value}"
|
184
|
+
else
|
185
|
+
set_header subject, value
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# @param int code
|
190
|
+
public
|
191
|
+
def set_code(code)
|
192
|
+
begin
|
193
|
+
code = code.to_i if !code.is_a?(Fixnum)
|
194
|
+
rescue
|
195
|
+
raise ViewError, ViewError::MSG_WRONG_TYPE%[code.class, 'Fixnum or convertible types into Fixnum']
|
196
|
+
end
|
197
|
+
@response.status = code
|
198
|
+
end
|
199
|
+
|
200
|
+
# @param string body
|
201
|
+
public
|
202
|
+
def set_body(body)
|
203
|
+
if !body.is_a?(String)
|
204
|
+
begin
|
205
|
+
body = body.to_s
|
206
|
+
rescue
|
207
|
+
raise ViewError, ViewError::MSG_WRONG_TYPE%[body.class, 'String or convertible types into Fixnum']
|
208
|
+
end
|
209
|
+
end
|
210
|
+
@response.body = []
|
211
|
+
@response.write body
|
212
|
+
@is_body_set = true
|
213
|
+
end
|
214
|
+
|
215
|
+
# @param string body
|
216
|
+
public
|
217
|
+
def add_body(body)
|
218
|
+
if !body.is_a?(String)
|
219
|
+
begin
|
220
|
+
body = body.to_s
|
221
|
+
rescue
|
222
|
+
raise ViewError, ViewError::MSG_WRONG_TYPE%[body.class, 'String or convertible types into String']
|
223
|
+
end
|
224
|
+
end
|
225
|
+
@response.write body
|
226
|
+
@is_body_set = true
|
227
|
+
end
|
228
|
+
|
229
|
+
# is set something for response body?
|
230
|
+
public
|
231
|
+
def is_body_set?
|
232
|
+
return @is_body_set
|
233
|
+
end
|
234
|
+
|
235
|
+
# assign mapping into teplate and/or layout
|
236
|
+
# when already exst mapping it would be merged
|
237
|
+
# @param hash list
|
238
|
+
# @throw ::Nekonote::Error
|
239
|
+
public
|
240
|
+
def assign_variables(list)
|
241
|
+
if !list.is_a? Hash
|
242
|
+
raise ViewError, ViewError::MSG_FAILED_TO_ASSIGN
|
243
|
+
end
|
244
|
+
|
245
|
+
# convert symbol key to string key
|
246
|
+
list_cnv = {}
|
247
|
+
list.map {|pair| list_cnv[pair[0].to_s] = pair[1] }
|
248
|
+
|
249
|
+
if defined?(@mapping) && @mapping.is_a?(Hash)
|
250
|
+
@mapping.merge! list_cnv
|
251
|
+
else
|
252
|
+
@mapping = list_cnv
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# assign custom fields into teplate and/or layout
|
257
|
+
# @param bool is_error_route
|
258
|
+
public
|
259
|
+
def assign_custom_fields(info)
|
260
|
+
fields = Preference.get_custom_fields info, @is_error_route
|
261
|
+
if fields.is_a?(Hash)
|
262
|
+
assign_variables fields
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# @param hash info
|
267
|
+
private
|
268
|
+
def register_info_properies(info)
|
269
|
+
@info_path = info[Preference::FIELD_ROUTE_PATH]
|
270
|
+
@info_exec_method = info[Preference::FIELD_ROUTE_EXEC_METHOD]
|
271
|
+
@info_allow_methods = info[Preference::FIELD_ROUTE_ALLOW_METHODS]
|
272
|
+
@info_params = info[Preference::FIELD_ROUTE_PARAMS]
|
273
|
+
@info_content_type = info[Preference::FIELD_ROUTE_CONTENT_TYPE]
|
274
|
+
@info_template = info[Preference::FIELD_ROUTE_TEMPLATE]
|
275
|
+
@info_layout = info[Preference::FIELD_ROUTE_LAYOUT]
|
276
|
+
@info_page_cache_time = info[Preference::FIELD_ROUTE_PAGE_CACHE_TIME]
|
277
|
+
end
|
278
|
+
|
279
|
+
# initialize the properties
|
280
|
+
private
|
281
|
+
def init_property
|
282
|
+
@is_body_set = false
|
283
|
+
@is_redirect = false
|
284
|
+
end
|
285
|
+
|
286
|
+
# Returns template path for the default when it was found and available
|
287
|
+
# @param string|nil
|
288
|
+
private
|
289
|
+
def get_default_template_path(handler_name)
|
290
|
+
return nil if !handler_name.is_a? String
|
291
|
+
|
292
|
+
# get default template path when no template specified
|
293
|
+
begin
|
294
|
+
template = handler_name.sub(/Handler$/, '').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
295
|
+
rescue
|
296
|
+
template = nil
|
297
|
+
end
|
298
|
+
|
299
|
+
# invalid template
|
300
|
+
return nil if (template.nil? || template == '')
|
301
|
+
|
302
|
+
template_path = Nekonote.get_root_path + PATH_TO_TEMPLATE + '/' + template + '.tpl'
|
303
|
+
if Util::Filer.available_file? template_path
|
304
|
+
template_path = template_path
|
305
|
+
else
|
306
|
+
template_path = nil
|
307
|
+
end
|
308
|
+
|
309
|
+
return template_path
|
310
|
+
end
|
311
|
+
|
312
|
+
# @param string type
|
313
|
+
# @return string
|
314
|
+
private
|
315
|
+
def get_content_type(type)
|
316
|
+
type = type.intern if type.is_a?(String)
|
317
|
+
|
318
|
+
content_type = 'text/plain'
|
319
|
+
if type == nil
|
320
|
+
content_type = 'text/html'
|
321
|
+
elsif type == :html
|
322
|
+
content_type = 'text/html'
|
323
|
+
elsif type == :json
|
324
|
+
content_type = 'application/json'
|
325
|
+
elsif type == :xml
|
326
|
+
content_type = 'application/xml'
|
327
|
+
elsif type == :plain
|
328
|
+
content_type = 'text/plain'
|
329
|
+
end
|
330
|
+
return content_type
|
331
|
+
end
|
332
|
+
|
333
|
+
# @param string field
|
334
|
+
private
|
335
|
+
def get_error_response_code(field)
|
336
|
+
case field
|
337
|
+
when Preference::FIELD_ROUTE_ERR_MISSING_ROUTE
|
338
|
+
return 404
|
339
|
+
when Preference::FIELD_ROUTE_ERR_WRONG_METHOD
|
340
|
+
return 405
|
341
|
+
when Preference::FIELD_ROUTE_ERR_FATAL
|
342
|
+
return 500
|
343
|
+
when Preference::FIELD_ROUTE_ERR_NOT_FOUND
|
344
|
+
return 404
|
345
|
+
else
|
346
|
+
raise PreferenceError, PreferenceError::MSG_UNDEFINED_FIELD% field
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
# @param string|nil template_path
|
351
|
+
# @param string|nil layout_path
|
352
|
+
# @return string
|
353
|
+
private
|
354
|
+
def get_parsed(template_path = nil, layout_path = nil)
|
355
|
+
data = ''
|
356
|
+
liq_tpl_template = nil
|
357
|
+
liq_tpl_layout = nil
|
358
|
+
begin
|
359
|
+
if template_path.is_a? String
|
360
|
+
liq_tpl_template = Liquid::Template.parse IO.read(template_path)
|
361
|
+
end
|
362
|
+
|
363
|
+
if layout_path.is_a? String
|
364
|
+
liq_tpl_layout = Liquid::Template.parse IO.read(layout_path)
|
365
|
+
end
|
366
|
+
|
367
|
+
# parse and render template
|
368
|
+
if liq_tpl_template.is_a? Liquid::Template
|
369
|
+
content = liq_tpl_template.render @mapping
|
370
|
+
else
|
371
|
+
content = nil
|
372
|
+
end
|
373
|
+
|
374
|
+
# parse and render layout
|
375
|
+
if liq_tpl_layout.is_a? Liquid::Template
|
376
|
+
if content != nil
|
377
|
+
# assgin tempalte for layout
|
378
|
+
mapping = {
|
379
|
+
'content' => content
|
380
|
+
}
|
381
|
+
# and put it to @mapping
|
382
|
+
@mapping.merge! mapping
|
383
|
+
end
|
384
|
+
data = liq_tpl_layout.render @mapping
|
385
|
+
|
386
|
+
else
|
387
|
+
# if template data is available set it to data
|
388
|
+
data = content if content != nil
|
389
|
+
end
|
390
|
+
|
391
|
+
rescue => e
|
392
|
+
raise ViewError, e.message
|
393
|
+
end
|
394
|
+
|
395
|
+
return data
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Nekonote
|
2
|
+
class YamlAccess
|
3
|
+
DIR_PREFERENCE = 'preference'
|
4
|
+
|
5
|
+
# @param string path
|
6
|
+
# @return hash
|
7
|
+
def self.get_parsed(path)
|
8
|
+
if !Util::Filer.available_file? path
|
9
|
+
return nil
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
contents = YAML.load_file path
|
14
|
+
contents = {} if (!contents.is_a? Hash)
|
15
|
+
return contents
|
16
|
+
|
17
|
+
rescue Psych::SyntaxError => e
|
18
|
+
msg = PreferenceError::MSG_WRONG_YAML_SYNTAX% path
|
19
|
+
msg += $/ + $/ + e.message
|
20
|
+
raise PreferenceError, msg
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the parsed routing information
|
25
|
+
# @param string path
|
26
|
+
def self.get_parsed_route(path)
|
27
|
+
if !Util::Filer.available_file? path
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
ast = YAML.parse_file path
|
33
|
+
|
34
|
+
route_list = []
|
35
|
+
ast.root.children.each_with_index do |node, index|
|
36
|
+
cnt = index / 2
|
37
|
+
if node.is_a?(Psych::Nodes::Scalar) && node.value != Preference::FIELD_OPTION_ROUTE
|
38
|
+
route_list[cnt] = {Preference::FIELD_ROUTE_HANDLER => node.value}
|
39
|
+
elsif node.is_a? Psych::Nodes::Mapping
|
40
|
+
if route_list[cnt].is_a? Hash
|
41
|
+
route_list[cnt] = route_list[cnt].merge node.to_ruby
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
parsed = []
|
47
|
+
route_list.each do |info|
|
48
|
+
parsed << info if info.is_a?(Hash)
|
49
|
+
end
|
50
|
+
|
51
|
+
return parsed
|
52
|
+
|
53
|
+
rescue Psych::SyntaxError => e
|
54
|
+
msg = PreferenceError::MSG_WRONG_YAML_SYNTAX% path
|
55
|
+
msg += $/ + $/ + e.message
|
56
|
+
raise PreferenceError, msg
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|