cuca 0.06 → 0.07
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.
- data/CHANGELOG +9 -0
- data/README +43 -34
- data/application_skeleton/app/_layouts/simple.rb +1 -1
- data/application_skeleton/public/dispatch-fwdev.cgi +21 -0
- data/application_skeleton/public/dispatch.cgi +1 -1
- data/application_skeleton/public/dispatch.cgi-old +18 -0
- data/application_skeleton/public/dispatch.fcgi-old +25 -0
- data/application_skeleton/scripts/rack-test.rb +52 -0
- data/application_skeleton/scripts/server-rack-thin.rb +40 -0
- data/application_skeleton/scripts/server-rack-webrick.rb +22 -0
- data/application_skeleton/scripts/server-webrick-cgihandler.rb +26 -0
- data/application_skeleton/scripts/server-webrick.rb +1 -1
- data/lib/cuca.rb +2 -30
- data/lib/cuca/app.rb +35 -30
- data/lib/cuca/app_cgi.rb +289 -0
- data/lib/cuca/config.rb +8 -15
- data/lib/cuca/controller.rb +22 -11
- data/lib/cuca/generator/view.rb +4 -51
- data/lib/cuca/generator_context.rb +42 -0
- data/lib/cuca/layout.rb +1 -1
- data/lib/cuca/session.rb-cgi +171 -0
- data/lib/cuca/sessionpage.rb +13 -2
- data/lib/cuca/stdlib/arform.rb +19 -2
- data/lib/cuca/stdlib/form.rb +6 -3
- data/lib/cuca/stdlib/formelements.rb +33 -8
- data/lib/cuca/stdlib/formelements.rbs +157 -0
- data/lib/cuca/stdlib/listwidget/dblist.rb +10 -6
- data/lib/cuca/stdlib/listwidget/list.rb +24 -4
- data/lib/cuca/widget.rb +11 -14
- data/lib/cuca_cli.rb +14 -0
- data/tests/controller.rb +21 -5
- data/tests/generator_markaby.rb +1 -1
- data/tests/widget.rb +2 -1
- metadata +96 -96
data/lib/cuca/app_cgi.rb
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'cuca/config'
|
3
|
+
|
4
|
+
|
5
|
+
def cuca_register_autoload(object, file)
|
6
|
+
autoload(object, file)
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
module Cuca
|
11
|
+
|
12
|
+
module Objects
|
13
|
+
end
|
14
|
+
|
15
|
+
# Sandbox is used internally run the action script defined by the controller
|
16
|
+
# In future this can be extended to implement some security features etc..
|
17
|
+
class Sandbox
|
18
|
+
|
19
|
+
def self.run(controller_class_name, mod, assigns, request_method, subcall)
|
20
|
+
self.class.send(:include, mod)
|
21
|
+
controller_class = mod::const_get(controller_class_name)
|
22
|
+
controller = controller_class.send(:new, :assigns=>assigns)
|
23
|
+
controller.run_before_filters
|
24
|
+
|
25
|
+
|
26
|
+
controller.send('_do'.intern, 'run', subcall)
|
27
|
+
case request_method
|
28
|
+
when 'POST'
|
29
|
+
controller.send('_do'.intern, 'post', subcall)
|
30
|
+
when 'GET'
|
31
|
+
controller.send('_do'.intern, 'get', subcall)
|
32
|
+
end
|
33
|
+
|
34
|
+
controller.run_after_filters
|
35
|
+
|
36
|
+
return [controller.http_status, controller.mime_type, controller.to_s]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# == Cuca Application
|
41
|
+
#
|
42
|
+
# A Cuca::App object will be created directly by the dispatcher - which again is the
|
43
|
+
# direct cgi or fastcgi script that get run by the webserver.
|
44
|
+
# Normally you just create a Cuca::App object and run the cgicall function with optional with
|
45
|
+
# a cgi object as paramenter.
|
46
|
+
# Before doing that you must set $cuca_path to the root of your appication structure.
|
47
|
+
class App
|
48
|
+
|
49
|
+
|
50
|
+
attr_reader :app_path, :log_path, :public_path, :urlmap, :cgi, :logger
|
51
|
+
|
52
|
+
@@app_config = Cuca::Config.new
|
53
|
+
|
54
|
+
## Application configuration
|
55
|
+
public
|
56
|
+
def self.configure
|
57
|
+
yield @@app_config
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.config
|
61
|
+
@@app_config
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def init_app
|
66
|
+
@app_path = $cuca_path + "/app"
|
67
|
+
@public_path = $cuca_path + "/public"
|
68
|
+
@log_path = $cuca_path + "/log"
|
69
|
+
@app_path.freeze
|
70
|
+
@public_path.freeze
|
71
|
+
@log_path.freeze
|
72
|
+
@logger = Logger.new("#{@log_path}/messages")
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Initializes settings to run the appliction
|
77
|
+
#
|
78
|
+
# @xxx_path - path to /app directory
|
79
|
+
private
|
80
|
+
def init_call(path_info)
|
81
|
+
require 'cuca/urlmap'
|
82
|
+
|
83
|
+
begin
|
84
|
+
@urlmap = URLMap.new(@app_path, path_info)
|
85
|
+
|
86
|
+
rescue RoutingError => r # no script found - maybe serve a static file?
|
87
|
+
@urlmap = nil # == no script found
|
88
|
+
return
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# will do a 'require' on all .rb files in path
|
93
|
+
private
|
94
|
+
def include_files(path)
|
95
|
+
return unless File.exist?(path)
|
96
|
+
pwd = Dir.pwd
|
97
|
+
Dir.chdir(path)
|
98
|
+
Dir['*.rb'].each do |f|
|
99
|
+
require "#{path}/#{f}"
|
100
|
+
end
|
101
|
+
Dir.chdir(pwd)
|
102
|
+
end
|
103
|
+
|
104
|
+
# this will schedule all files for autoloading
|
105
|
+
private
|
106
|
+
def autoload_files(path, naming_proc)
|
107
|
+
$app.logger.info "Autoload on #{path}"
|
108
|
+
return unless File.exist?(path)
|
109
|
+
pwd = Dir.pwd
|
110
|
+
Dir.chdir(path)
|
111
|
+
Dir['*.rb'].each do |f|
|
112
|
+
fn = f.scan(/(.*)\.rb/)[0][0]
|
113
|
+
classname = naming_proc.call(fn)
|
114
|
+
$app.logger.info "Scheduling Autoload Object '#{classname}' ==> #{path}/#{f}"
|
115
|
+
cuca_register_autoload(classname.intern, "#{path}/#{f}")
|
116
|
+
end
|
117
|
+
Dir.chdir(pwd)
|
118
|
+
end
|
119
|
+
|
120
|
+
public
|
121
|
+
def initialize
|
122
|
+
$app = self
|
123
|
+
init_app
|
124
|
+
end
|
125
|
+
|
126
|
+
# this will yield all support directories from base path and if defined
|
127
|
+
# the naming proc else nil
|
128
|
+
def all_support_directories(path_tree)
|
129
|
+
path_tree.each do |t|
|
130
|
+
(App::config['include_directories'] || []).each do |id|
|
131
|
+
if id.instance_of?(Hash) then
|
132
|
+
yield "#{t}/#{id[:dir]}", id[:class_naming]
|
133
|
+
else
|
134
|
+
yield "#{t}/#{id}", nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
public
|
141
|
+
def load_support_files(urlmap) # :nodoc:
|
142
|
+
# $stderr.puts "======== load support files ========="
|
143
|
+
# $stderr.puts "Inc Dir: #{App::config['include_directories'].inspect}"
|
144
|
+
# $stderr.puts "Path tr: #{@conf['PATH_TREE'].inspect}"
|
145
|
+
# $stderr.puts "====================================="
|
146
|
+
|
147
|
+
# db = $:.dup
|
148
|
+
# all_support_directories(@conf['APP_PATH']) { |d,p| $: << d }
|
149
|
+
|
150
|
+
# all_support_directories(@app_path, @urlmap.path_tree) do |dir, proc|
|
151
|
+
# $stderr.puts "PATH TREE: #{urlmap.path_tree.inspect}"
|
152
|
+
all_support_directories(urlmap.path_tree) do |dir, proc|
|
153
|
+
# $stderr.puts "Support Directory #{dir}"
|
154
|
+
if proc then
|
155
|
+
autoload_files(dir, proc)
|
156
|
+
else
|
157
|
+
include_files(dir)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
# this will build an error message depending on configuration
|
164
|
+
private
|
165
|
+
def get_error(title, exception, show_trace = true, file=nil)
|
166
|
+
err = "<h3>#{title}</h3>"
|
167
|
+
err << "<b>#{exception.class.to_s}: #{CGI::escapeHTML(exception.to_s)}</b><br/>"
|
168
|
+
$stderr.puts "ERROR: #{title} - #{exception.class.to_s}: #{exception.to_s}"
|
169
|
+
if (show_trace) then
|
170
|
+
exception.backtrace.each do |b|
|
171
|
+
$stderr.puts " #{b}"
|
172
|
+
err +="<br/>#{b}"
|
173
|
+
end
|
174
|
+
else
|
175
|
+
begin
|
176
|
+
err = File.open(file).read
|
177
|
+
rescue
|
178
|
+
end
|
179
|
+
end
|
180
|
+
err
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
public
|
185
|
+
def cgicall(cgi = nil)
|
186
|
+
@cgi = cgi || CGI.new
|
187
|
+
|
188
|
+
|
189
|
+
#
|
190
|
+
# 1st priority: Serve a file if it exists in the 'public' folder
|
191
|
+
#
|
192
|
+
file = @public_path + '/' + @cgi.path_info
|
193
|
+
if File.exists?(file) && File.ftype(file) == 'file' then
|
194
|
+
require 'cuca/mimetypes'
|
195
|
+
mt = MimeTypes.new
|
196
|
+
f = File.new(file)
|
197
|
+
extension = file.scan(/.*\.(.*)$/)[0][0] if file.include?('.')
|
198
|
+
extension ||= 'html'
|
199
|
+
mime = mt[extension] || 'text/html'
|
200
|
+
@cgi.out('type' => mime,
|
201
|
+
'expires' => (Time.now+App.config['http_static_content_expires'])) { f.read }
|
202
|
+
f.close
|
203
|
+
return
|
204
|
+
end
|
205
|
+
|
206
|
+
init_call(@cgi.path_info)
|
207
|
+
|
208
|
+
# If config (urlmap) couldn't find a script then let's give up
|
209
|
+
# with a file-not-found 404 error
|
210
|
+
if @urlmap.nil? then
|
211
|
+
begin
|
212
|
+
file = "#{@public_path}/#{Cuca::App.config['http_404']}"
|
213
|
+
c = File.open(file).read
|
214
|
+
@cgi.out('status' => 'NOT_FOUND', 'type' => 'text/html') { c }
|
215
|
+
rescue => e
|
216
|
+
@cgi.out('status' => 'NOT_FOUND', 'type' => 'text/html') { "404 - File not found!" }
|
217
|
+
end
|
218
|
+
return
|
219
|
+
end
|
220
|
+
|
221
|
+
logger.info "CGICall on #{@urlmap.url} - #{@urlmap.script}"
|
222
|
+
script = @urlmap.script
|
223
|
+
|
224
|
+
#
|
225
|
+
# 2nd: Check if we have a script for requested action
|
226
|
+
#
|
227
|
+
if (!File.exists?(script)) then
|
228
|
+
@cgi.out { "Script not found: #{script}" }
|
229
|
+
return
|
230
|
+
end
|
231
|
+
|
232
|
+
# 3rd: Load additional files
|
233
|
+
load_support_files(@urlmap)
|
234
|
+
|
235
|
+
test = ApplicationController.new
|
236
|
+
|
237
|
+
|
238
|
+
# 4th: Now let's run the actual page script code
|
239
|
+
controller_class_name = @urlmap.action.capitalize+"Controller"
|
240
|
+
|
241
|
+
# Clear all hints
|
242
|
+
Widget::clear_hints()
|
243
|
+
|
244
|
+
# Load the code of the action into the module
|
245
|
+
controller_module = @urlmap.action_module
|
246
|
+
|
247
|
+
|
248
|
+
# things fail in this block get error logged and/or displayed in browser
|
249
|
+
begin
|
250
|
+
# load controller
|
251
|
+
controller_module.module_eval(File.open(script).read, script) unless \
|
252
|
+
controller_module.const_defined?(controller_class_name.intern)
|
253
|
+
|
254
|
+
# Catch a common user error
|
255
|
+
raise Cuca::ApplicationException.new("Could not find #{controller_class_name} defined in #{script}") \
|
256
|
+
unless controller_module.const_defined?(controller_class_name.intern)
|
257
|
+
|
258
|
+
|
259
|
+
# run controller
|
260
|
+
(status, mime, content) = Sandbox.run(controller_class_name,
|
261
|
+
@urlmap.action_module, @urlmap.assigns,
|
262
|
+
@cgi.request_method, @urlmap.subcall)
|
263
|
+
|
264
|
+
logger.info "CGICall OK"
|
265
|
+
@cgi.out('type' => mime, 'status' => status) {content}
|
266
|
+
|
267
|
+
rescue Cuca::ApplicationException => e
|
268
|
+
err = get_error("Application Error", e,
|
269
|
+
Cuca::App.config['display_errors'], Cuca::App.config['http_500'])
|
270
|
+
@cgi.out('status' => 'SERVER_ERROR') { err }
|
271
|
+
|
272
|
+
logger.info "CGICall Application Error"
|
273
|
+
return
|
274
|
+
|
275
|
+
rescue => e
|
276
|
+
err = get_error("System Error", e,
|
277
|
+
Cuca::App.config['display_errors'], Cuca::App.config['http_500'])
|
278
|
+
@cgi.out('status' => 'SERVER_ERROR') { err }
|
279
|
+
|
280
|
+
logger.info "CGICall System Error"
|
281
|
+
return
|
282
|
+
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
end # module
|
data/lib/cuca/config.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Cuca
|
2
2
|
|
3
|
-
#
|
3
|
+
# = Configure the application
|
4
4
|
# App::Config is normally called from conf/environment.rb . The attributes below the framework
|
5
5
|
# will make use of, but you can always define own ones.
|
6
6
|
#
|
@@ -10,21 +10,14 @@ module Cuca
|
|
10
10
|
# conf.include_directories = %{_widgets _special_widgets}
|
11
11
|
# conf.database = 'mydb' # application specific
|
12
12
|
# end
|
13
|
-
# === Attributes:
|
14
13
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
# - :class_naming A proc that will take one parameter and returns the name of the class
|
23
|
-
# found within the file. It will get the filename as parameter.
|
24
|
-
# example :class_naming => Proc.new { |file_name| file_name.capitalize+'Controller' }
|
25
|
-
# * log_level - default 3 (future?)
|
26
|
-
# * magic_prefix - default: '__default_' (see Cuca::Controller)
|
27
|
-
# * display_errors - default: true , display errors in browser (swith off within production systems)
|
14
|
+
#
|
15
|
+
# == Attributes:
|
16
|
+
#
|
17
|
+
# The file *conf/environment.rb* within your application path contains a full list of
|
18
|
+
# attributes you can set.
|
19
|
+
#
|
20
|
+
|
28
21
|
class Config < Hash
|
29
22
|
def method_missing(m, *params)
|
30
23
|
met = m.id2name
|
data/lib/cuca/controller.rb
CHANGED
@@ -13,7 +13,7 @@ attr_reader :flags
|
|
13
13
|
end
|
14
14
|
|
15
15
|
|
16
|
-
#
|
16
|
+
# = Cuca Controller
|
17
17
|
#
|
18
18
|
# A controller handles the get/post request for your website and has the ability to
|
19
19
|
# generate an output directly making use of other Widgets and Layouts.
|
@@ -46,7 +46,7 @@ end
|
|
46
46
|
# File: app/user/__default_username/show.rb will map to http://user/johnrambo/show and define instance variable
|
47
47
|
# @username within the Controller defined in show.rb
|
48
48
|
#
|
49
|
-
# The magic URL prefix (default:
|
49
|
+
# The magic URL prefix (default: __) can be change within App::Config
|
50
50
|
#
|
51
51
|
# == Defining a Layouts
|
52
52
|
#
|
@@ -69,6 +69,12 @@ end
|
|
69
69
|
# that allows you to redirect, display error or set a different layout.
|
70
70
|
#
|
71
71
|
#
|
72
|
+
# == Filters
|
73
|
+
#
|
74
|
+
# A filter method can be run before or after the controller code is executed. It can be used
|
75
|
+
# to prepare data, restrict access or format the generated content for example.
|
76
|
+
# See: before_filter and after_filter
|
77
|
+
#
|
72
78
|
# == Examples
|
73
79
|
#
|
74
80
|
# Hello world (index.rb):
|
@@ -121,11 +127,17 @@ class Controller < Widget
|
|
121
127
|
@_http_status
|
122
128
|
end
|
123
129
|
|
130
|
+
# Add additional http header for the response
|
131
|
+
# No validation made
|
132
|
+
def http_header(field=nil, value=nil)
|
133
|
+
@_http_header ||= {}
|
134
|
+
@_http_header[field] = value if field
|
135
|
+
@_http_header
|
136
|
+
end
|
124
137
|
|
125
138
|
# get layout
|
126
139
|
private # ??
|
127
140
|
def self.def_layout
|
128
|
-
# return @_layout unless @_layout.nil?
|
129
141
|
self.run_attr_method('def_layout_name')
|
130
142
|
end
|
131
143
|
|
@@ -203,7 +215,7 @@ class Controller < Widget
|
|
203
215
|
break if @cancel_execution
|
204
216
|
filter = f[0]
|
205
217
|
begin
|
206
|
-
if (!self.
|
218
|
+
if (!self.respond_to?(filter.intern)) then
|
207
219
|
http_status "SERVER_ERROR"
|
208
220
|
raise ApplicationException.new("Filter not found in chain #{debug_filter_names}: #{filter}")
|
209
221
|
end
|
@@ -280,6 +292,10 @@ class Controller < Widget
|
|
280
292
|
end
|
281
293
|
mab { html { body { h2 "Error"; text @error_message; br; text trace }}}
|
282
294
|
end
|
295
|
+
|
296
|
+
if e.flags.has_key?(:cancel_execution) then
|
297
|
+
@cancel_execution = true
|
298
|
+
end
|
283
299
|
end
|
284
300
|
|
285
301
|
def action_name
|
@@ -311,7 +327,7 @@ class Controller < Widget
|
|
311
327
|
method_name = what
|
312
328
|
method_name = "#{method_name}_#{subcall}" if subcall
|
313
329
|
begin
|
314
|
-
self.send(method_name) if self.
|
330
|
+
self.send(method_name) if self.respond_to?(method_name.intern)
|
315
331
|
rescue BreakControllerException => e
|
316
332
|
handle_exception(e)
|
317
333
|
rescue ApplicationException => e
|
@@ -321,10 +337,6 @@ class Controller < Widget
|
|
321
337
|
|
322
338
|
|
323
339
|
def initialize(*args)
|
324
|
-
# set a global variable of the controller object.
|
325
|
-
# Widgets can make use of this to call or modify the controller class.
|
326
|
-
$controller_object = self
|
327
|
-
|
328
340
|
@cancel_execution = false
|
329
341
|
super(*args)
|
330
342
|
|
@@ -346,8 +358,7 @@ class Controller < Widget
|
|
346
358
|
begin
|
347
359
|
layout_class = Object::const_get(lname)
|
348
360
|
rescue => e
|
349
|
-
|
350
|
-
raise ApplicationException.new("Could not load layout: #{lname}: #{e}")
|
361
|
+
raise ApplicationException, "Could not load layout: #{lname}: #{e}"
|
351
362
|
return nil
|
352
363
|
end
|
353
364
|
end
|
data/lib/cuca/generator/view.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'erb'
|
2
|
-
|
2
|
+
require 'cuca/generator_context'
|
3
3
|
|
4
4
|
module Cuca
|
5
5
|
module Generator
|
@@ -40,53 +40,7 @@ module Generator
|
|
40
40
|
#
|
41
41
|
module View
|
42
42
|
|
43
|
-
|
44
|
-
def get_bindings
|
45
|
-
binding
|
46
|
-
end
|
47
|
-
|
48
|
-
def initialize(assigns, base_object)
|
49
|
-
assigns.each_pair do |k,v|
|
50
|
-
instance_variable_set("@#{k}", v)
|
51
|
-
end
|
52
|
-
@base = base_object
|
53
|
-
end
|
54
|
-
|
55
|
-
def method_missing(sym, *args, &block )
|
56
|
-
class_name = sym.id2name
|
57
|
-
|
58
|
-
|
59
|
-
# 1st try to find method in the base widget
|
60
|
-
if @base.methods.include?(class_name) then
|
61
|
-
return @base.send(class_name, *args, &block)
|
62
|
-
end
|
63
|
-
c = nil
|
64
|
-
# 2nd try to find a widget
|
65
|
-
if Object.const_defined?(class_name+'Widget') then
|
66
|
-
c = Object::const_get(class_name+'Widget')
|
67
|
-
else
|
68
|
-
# ...try to find in action namespace
|
69
|
-
mod = $app.urlmap.action_module
|
70
|
-
c = mod.const_get(class_name+'Widget') if mod.const_defined?(class_name+'Widget')
|
71
|
-
end
|
72
|
-
|
73
|
-
raise NameError.new "Undefined method: #{class_name}" unless c
|
74
|
-
|
75
|
-
widget = c.new({:args => args,
|
76
|
-
:assigns => @assigns },
|
77
|
-
&block)
|
78
|
-
|
79
|
-
# $stderr.puts "Widget:" + widget.inspect
|
80
|
-
return widget.to_s
|
81
|
-
|
82
|
-
# $stderr.puts "Good"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# VIEW_DIR = $cuca_path+'app/_views'
|
87
|
-
|
88
|
-
|
89
|
-
# Procuce content by a template file.
|
43
|
+
# Produce content by a template file.
|
90
44
|
# This will return the generated markup as a string
|
91
45
|
def viewtext(filename=nil)
|
92
46
|
view_dir = $cuca_path + '/' + App::config['view_directory']
|
@@ -105,13 +59,13 @@ module View
|
|
105
59
|
|
106
60
|
# Procuce content and append to widget.
|
107
61
|
def view(filename)
|
108
|
-
@_content << viewtext(filename)
|
62
|
+
@_content << viewtext(filename)
|
109
63
|
end
|
110
64
|
|
111
65
|
# Normally you have your view (the template) within a separate file. Nevertheless
|
112
66
|
# you can passing as a string to this function.
|
113
67
|
def viewtext_p(template)
|
114
|
-
ERB.new(template).result(
|
68
|
+
ERB.new(template).result(GeneratorContext.new(get_assigns, self).get_bindings)
|
115
69
|
end
|
116
70
|
|
117
71
|
# Equivaltent to view but take template as a string.
|
@@ -123,4 +77,3 @@ end
|
|
123
77
|
|
124
78
|
end # Mod: Generator
|
125
79
|
end # Mod: Cuca
|
126
|
-
|