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.
- 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
|