cuca 0.01
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/application_skeleton/README +21 -0
- data/application_skeleton/app/_controllers/application.rb +7 -0
- data/application_skeleton/app/_layouts/simple.rb +19 -0
- data/application_skeleton/app/_widgets/sourcecode.rb +21 -0
- data/application_skeleton/app/_widgets/test.rb +23 -0
- data/application_skeleton/app/demo.rb +64 -0
- data/application_skeleton/app/index.rb +39 -0
- data/application_skeleton/app/user/__default_username/index.rb +7 -0
- data/application_skeleton/conf/environment.rb +16 -0
- data/application_skeleton/log/access.log +1 -0
- data/application_skeleton/log/error.log +1 -0
- data/application_skeleton/log/messages +1 -0
- data/application_skeleton/public/css/style.css +27 -0
- data/application_skeleton/public/dispatch.cgi +31 -0
- data/application_skeleton/public/dispatch.fcgi +36 -0
- data/application_skeleton/public/img/cuca-seagull.png +0 -0
- data/application_skeleton/scripts/console +5 -0
- data/application_skeleton/scripts/console.rb +5 -0
- data/application_skeleton/scripts/server-lighttpd-fcgi.rb +116 -0
- data/application_skeleton/scripts/server-lighttpd.rb +109 -0
- data/application_skeleton/scripts/server-webrick.rb +26 -0
- data/application_skeleton/scripts/test.rb +8 -0
- data/application_skeleton/tests/widgets/link.rb +22 -0
- data/bin/cuca +43 -0
- data/lib/cuca/app.rb +317 -0
- data/lib/cuca/cgi_emu.rb +67 -0
- data/lib/cuca/cgi_fix.rb +58 -0
- data/lib/cuca/const.rb +3 -0
- data/lib/cuca/controller.rb +240 -0
- data/lib/cuca/generator/markaby.rb +80 -0
- data/lib/cuca/generator/view.rb +121 -0
- data/lib/cuca/layout.rb +62 -0
- data/lib/cuca/mimetypes.rb +89 -0
- data/lib/cuca/session.rb +143 -0
- data/lib/cuca/sessionflash.rb +56 -0
- data/lib/cuca/sessionpage.rb +41 -0
- data/lib/cuca/stdlib/arform.rb +208 -0
- data/lib/cuca/stdlib/arview.rb +16 -0
- data/lib/cuca/stdlib/form.rb +137 -0
- data/lib/cuca/stdlib/formerrors.rb +20 -0
- data/lib/cuca/stdlib/link.rb +37 -0
- data/lib/cuca/stdlib/list.rb +3 -0
- data/lib/cuca/stdlib/listwidget/dblist.rb +122 -0
- data/lib/cuca/stdlib/listwidget/list.rb +189 -0
- data/lib/cuca/stdlib/listwidget/querydef.rb +167 -0
- data/lib/cuca/stdlib/listwidget/staticdatalist.rb +79 -0
- data/lib/cuca/stdlib/slink.rb +30 -0
- data/lib/cuca/test/helpers.rb +42 -0
- data/lib/cuca/urlmap.rb +267 -0
- data/lib/cuca/widget.rb +212 -0
- data/lib/cuca.rb +68 -0
- metadata +141 -0
data/lib/cuca/app.rb
ADDED
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
|
4
|
+
module Cuca
|
5
|
+
|
6
|
+
# Sandbox is used internally run the action script defined by the controller
|
7
|
+
# In future this can be extended to implement some security features etc..
|
8
|
+
class Sandbox
|
9
|
+
def self.run(controller_class_name, mod, assigns, request_method, subcall)
|
10
|
+
self.class.send(:include, mod)
|
11
|
+
controller_class = mod::const_get(controller_class_name)
|
12
|
+
controller = controller_class.send(:new, :assigns=>assigns)
|
13
|
+
controller.run_before_filters
|
14
|
+
|
15
|
+
|
16
|
+
controller.send('_do'.intern, 'run', subcall)
|
17
|
+
case $cgi.request_method
|
18
|
+
when 'POST'
|
19
|
+
controller.send('_do'.intern, 'post', subcall)
|
20
|
+
when 'GET'
|
21
|
+
controller.send('_do'.intern, 'get', subcall)
|
22
|
+
end
|
23
|
+
return controller.send(:to_s)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# == Cuca Application
|
28
|
+
#
|
29
|
+
# A Cuca::App object will be created directly by the dispatcher - which again is the
|
30
|
+
# direct cgi or fastcgi script that get run by the webserver.
|
31
|
+
# Normally you just create a Cuca::App object and run the cgicall function with optional with
|
32
|
+
# a cgi object as paramenter.
|
33
|
+
# Before doing that you must set $cuca_path to the root of your appication structure.
|
34
|
+
|
35
|
+
class App
|
36
|
+
|
37
|
+
# == Configure the application
|
38
|
+
# App::Config is normally called from conf/environment.rb . The attributes below the framework
|
39
|
+
# will make use of, but you can always define own ones.
|
40
|
+
#
|
41
|
+
# == Example
|
42
|
+
#
|
43
|
+
# Cuca::App.configure do |conf|
|
44
|
+
# conf.include_directories = %{_widgets _special_widgets}
|
45
|
+
# conf.database = 'mydb' # application specific
|
46
|
+
# end
|
47
|
+
# === Attributes:
|
48
|
+
#
|
49
|
+
# * include_directories
|
50
|
+
# An array of directories that will be automatically included if you call an action.
|
51
|
+
# If one of the directories is found in the path where the action file is located it will require
|
52
|
+
# all files found. (default: %w{_controllers _widgets _layouts} )
|
53
|
+
# * log_level - default 3
|
54
|
+
# * magic_prefix - default: '__default_' (see Cuca::Controller)
|
55
|
+
class Config < Hash
|
56
|
+
def method_missing(m, *params)
|
57
|
+
met = m.id2name
|
58
|
+
raise NoMethodError if met[met.size-1].chr != '='
|
59
|
+
self[met[0..met.size-2]] = params[0]
|
60
|
+
end
|
61
|
+
|
62
|
+
# some default stuff
|
63
|
+
def initialize
|
64
|
+
self['include_directories'] = %w{_controllers _widgets _layouts}
|
65
|
+
self['log_level'] = 3
|
66
|
+
self['magic_prefix'] = '__default_'
|
67
|
+
self['directory_seperator'] = '/' # future?
|
68
|
+
self['session_key'] = 'cuca_session'
|
69
|
+
self['session_prefix'] = 'cuca.'
|
70
|
+
self['session_valid'] = 3600*24
|
71
|
+
self['view_directoru'] = 'app/_view' # where to find views for the view/erb generator
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
attr_reader :conf, :cgi, :logger, :urlmap
|
78
|
+
|
79
|
+
@@app_config = Config.new
|
80
|
+
|
81
|
+
## Application configuration
|
82
|
+
public
|
83
|
+
def self.configure
|
84
|
+
yield @@app_config
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.config
|
88
|
+
@@app_config
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# cgi implementation test
|
93
|
+
private
|
94
|
+
def cgidump(cgi)
|
95
|
+
s = ""
|
96
|
+
s << "-----ENV------<br>"
|
97
|
+
cgi.env_table.each_pair { |k,v| s << "#{k} => #{v}<br>" }
|
98
|
+
s << "--------------<br>"
|
99
|
+
s << "Server Software: #{cgi.server_software}<br>"
|
100
|
+
s << "PATH INFO: #{cgi.path_info}<br>"
|
101
|
+
end
|
102
|
+
|
103
|
+
# Creates the @conf global variable - internal config
|
104
|
+
#
|
105
|
+
# APP_PATH = Path the the /app folder
|
106
|
+
# CGI = CGI object
|
107
|
+
# DEF_EXT = Default script extensions (.rb)
|
108
|
+
# DEF_ACTION = (like 'index')
|
109
|
+
# SCRIPT = Full path to the ruby script dealing with ACTION
|
110
|
+
# ACTION = Action called (=links to SCRIPT, eg 'show' or 'index')
|
111
|
+
# PATH_INFO = raw path info as from cgi
|
112
|
+
# PATH_TREE = array of path, without action
|
113
|
+
private
|
114
|
+
def mk_config
|
115
|
+
@conf = {}
|
116
|
+
|
117
|
+
# To have a look at ENV and cgi uncomment this
|
118
|
+
# @cgi.out { cgidump(@cgi) }
|
119
|
+
# raise "Debug stop"
|
120
|
+
|
121
|
+
|
122
|
+
# APP_PATH = Path the the /app folder
|
123
|
+
# @conf['APP_PATH'] = File.expand_path(File.dirname(__FILE__)+"/../app")
|
124
|
+
@conf['APP_PATH'] = $cuca_path + "/app"
|
125
|
+
|
126
|
+
# PUBLIC_PATH = Path to the /public folder
|
127
|
+
# @conf['PUBLIC_PATH'] = File.expand_path(File.dirname(__FILE__)+"/../public")
|
128
|
+
@conf['PUBLIC_PATH'] = $cuca_path + "/public"
|
129
|
+
|
130
|
+
# LOG_PATH = Path to logging directory
|
131
|
+
# @conf['LOG_PATH'] = File.expand_path(File.dirname(__FILE__)+"/../log")
|
132
|
+
@conf['LOG_PATH'] = $cuca_path + "/log"
|
133
|
+
|
134
|
+
# CGI = CGI object
|
135
|
+
@conf['CGI'] = @cgi
|
136
|
+
|
137
|
+
# DEF_EXT = Default script extensions
|
138
|
+
@conf['DEF_EXT'] = ".rb"
|
139
|
+
|
140
|
+
@conf['DEF_ACTION'] = 'index'
|
141
|
+
|
142
|
+
# Cookie name
|
143
|
+
@conf['COOKIE_NAME'] = 'cucahap'
|
144
|
+
|
145
|
+
# ACTION = Action called (=links to SCRIPT), eg 'index'
|
146
|
+
@conf['ACTION'] = @cgi.path_info.reverse[0].chr == '/' ? @conf['DEF_ACTION'] : @cgi.path_info.split('/').last.downcase
|
147
|
+
|
148
|
+
# PATH_INFO = raw path info as from cgi
|
149
|
+
@conf['PATH_INFO'] = @cgi.path_info
|
150
|
+
|
151
|
+
# PATH_TREE = path info as array - without action
|
152
|
+
# @conf['PATH_TREE'] = [''].concat(@conf['PATH_INFO'].split('/').collect { |e| (e==@conf['ACTION'] || e == '') ? nil : e }.compact)
|
153
|
+
|
154
|
+
|
155
|
+
require 'cuca/urlmap'
|
156
|
+
|
157
|
+
begin
|
158
|
+
mapping = URLMap.new(File.expand_path(@conf['APP_PATH']), @conf['PATH_INFO'])
|
159
|
+
@conf['SUBCALL'] = mapping.subcall
|
160
|
+
@conf['SCRIPT'] = mapping.script
|
161
|
+
@conf['ASSIGNS'] = mapping.assigns
|
162
|
+
@conf['ACTION'] = mapping.action
|
163
|
+
@urlmap = mapping
|
164
|
+
rescue RoutingError => r
|
165
|
+
@conf['SCRIPT'] = "#{@conf['PUBLIC_PATH']}/#{@conf['PATH_INFO']}"
|
166
|
+
return
|
167
|
+
# raise "RoutingError: #{r}"
|
168
|
+
end
|
169
|
+
|
170
|
+
# get the PATH_TREE for file inclusion!
|
171
|
+
|
172
|
+
c_ap = File.expand_path(@conf['APP_PATH'])
|
173
|
+
pt = @conf['SCRIPT'][c_ap.size..-1]
|
174
|
+
ac_script = @conf['SCRIPT'].split('/').last
|
175
|
+
@conf['PATH_TREE'] = [''].concat(pt.split('/').collect { |e| (e==ac_script || e == '') ? nil : e }.compact)
|
176
|
+
end
|
177
|
+
|
178
|
+
# will do a 'require' on all .rb files in path
|
179
|
+
private
|
180
|
+
def include_files(path)
|
181
|
+
return unless File.exist?(path)
|
182
|
+
pwd = Dir.pwd
|
183
|
+
Dir.chdir(path)
|
184
|
+
Dir['*.rb'].each do |f|
|
185
|
+
require "#{path}/#{f}"
|
186
|
+
end
|
187
|
+
Dir.chdir(pwd)
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
public
|
193
|
+
def initialize(cgi = nil)
|
194
|
+
|
195
|
+
$cgi = cgi || CGI.new
|
196
|
+
@cgi = $cgi
|
197
|
+
|
198
|
+
mk_config
|
199
|
+
|
200
|
+
@logger = Logger.new(@conf['LOG_PATH'] + "/messages")
|
201
|
+
$logger = @logger
|
202
|
+
$conf = @conf
|
203
|
+
$app = self
|
204
|
+
|
205
|
+
rescue RuntimeError => e
|
206
|
+
@cgi.out { "Error initializing the app: #{$!}" }
|
207
|
+
end
|
208
|
+
|
209
|
+
# this has to be public for testing scripts
|
210
|
+
public
|
211
|
+
def load_support_files # :nodoc:
|
212
|
+
# $stderr.puts "======== load support files ========="
|
213
|
+
# $stderr.puts "Inc Dir: #{App::config['include_directories'].inspect}"
|
214
|
+
# $stderr.puts "Path tr: #{@conf['PATH_TREE'].inspect}"
|
215
|
+
# $stderr.puts "====================================="
|
216
|
+
|
217
|
+
base_path = @conf['APP_PATH']
|
218
|
+
@conf['PATH_TREE'].each do |t|
|
219
|
+
base_path << "/#{t}"
|
220
|
+
(App::config['include_directories'] || []).each do |id|
|
221
|
+
include_files "#{base_path}/#{id}"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# (App::config['include_directories'] || []).each do |id|
|
226
|
+
# @conf['PATH_TREE'].each { |t| include_files @conf['APP_PATH'] + '/'+t+"/#{id}/"}
|
227
|
+
# end
|
228
|
+
end
|
229
|
+
|
230
|
+
public
|
231
|
+
def cgicall
|
232
|
+
script = @conf['SCRIPT']
|
233
|
+
|
234
|
+
|
235
|
+
#
|
236
|
+
# 1st priority: Serve a file if it exists in the 'public' folder
|
237
|
+
#
|
238
|
+
file = @conf['PUBLIC_PATH'] + '/' + @conf['PATH_INFO']
|
239
|
+
if File.exists?(file) && File.ftype(file) == 'file' then
|
240
|
+
require 'cuca/mimetypes'
|
241
|
+
mt = MimeTypes.new
|
242
|
+
f = File.new(file)
|
243
|
+
extension = file.scan(/.*\.(.*)$/)[0][0] if file.include?('.')
|
244
|
+
extension ||= 'html'
|
245
|
+
mime = mt[extension] || 'text/html'
|
246
|
+
@cgi.out(mime) { f.read }
|
247
|
+
f.close
|
248
|
+
return
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
#
|
253
|
+
# 2nd: Check if we have a script for requested action
|
254
|
+
#
|
255
|
+
if (!File.exists?(script)) then
|
256
|
+
@cgi.out { "Script not found: #{script}" }
|
257
|
+
return
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
# 3rd: Load additional files
|
262
|
+
load_support_files
|
263
|
+
|
264
|
+
|
265
|
+
# 4th: Now let's run the actual page script code
|
266
|
+
controller_class_name = @conf['ACTION'].capitalize+"Controller"
|
267
|
+
|
268
|
+
# Clear all hints
|
269
|
+
Widget::clear_hints()
|
270
|
+
|
271
|
+
# Load the code of the action into the module
|
272
|
+
controller_module = @urlmap.action_module
|
273
|
+
|
274
|
+
|
275
|
+
controller_module.module_eval(File.open(script).read, script) unless \
|
276
|
+
controller_module.const_defined?(controller_class_name.intern)
|
277
|
+
|
278
|
+
# Catch a common user error
|
279
|
+
if !controller_module.const_defined?(controller_class_name.intern) then
|
280
|
+
@cgi.out { "Could not find #{controller_class_name} defined in #{script}" }
|
281
|
+
return
|
282
|
+
end
|
283
|
+
|
284
|
+
#
|
285
|
+
# Load the controller
|
286
|
+
#
|
287
|
+
begin
|
288
|
+
|
289
|
+
result = Sandbox.run(controller_class_name, @urlmap.action_module, @conf['ASSIGNS'],
|
290
|
+
$cgi.request_method, @conf['SUBCALL'])
|
291
|
+
|
292
|
+
|
293
|
+
@cgi.out('type' => 'text/html') {result}
|
294
|
+
|
295
|
+
rescue Cuca::ApplicationException => e
|
296
|
+
err = "<b>Application Error: #{e}</b><br/>"
|
297
|
+
e.backtrace.each do |b|
|
298
|
+
err +="<br/>#{b}"
|
299
|
+
end
|
300
|
+
@cgi.out('status' => 'SERVER_ERROR') { err }
|
301
|
+
return
|
302
|
+
|
303
|
+
rescue => e
|
304
|
+
err = "<b>System Error: #{e}</b><br/>"
|
305
|
+
e.backtrace.each do |b|
|
306
|
+
err +="<br/>#{b}"
|
307
|
+
end
|
308
|
+
@cgi.out('status' => 'SERVER_ERROR') { err }
|
309
|
+
return
|
310
|
+
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
end # module
|
data/lib/cuca/cgi_emu.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# little fake cgi class that allows automated testing of cgi application
|
2
|
+
|
3
|
+
class CGIEmu < CGI
|
4
|
+
class EnvTable
|
5
|
+
def initialize(options)
|
6
|
+
@test_path_info = options['PATH_INFO'] || '/'
|
7
|
+
@test_query_params = options['QUERY_PARAMS'] || {}
|
8
|
+
|
9
|
+
the_env_table.each_pair { |k,v| ENV[k] = v }
|
10
|
+
|
11
|
+
# ENV.merge(the_env_table)
|
12
|
+
end
|
13
|
+
|
14
|
+
def query_string
|
15
|
+
r = []
|
16
|
+
@test_query_params.each_pair { |k,v| r << "#{k}=#{v}" }
|
17
|
+
return r.join('&')
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def the_env_table
|
22
|
+
{"SERVER_NAME"=>"localhost",
|
23
|
+
"PATH_INFO"=>@test_path_info,
|
24
|
+
"REMOTE_HOST"=>"localhost",
|
25
|
+
"HTTP_ACCEPT_ENCODING"=>"x-gzip, x-deflate, gzip, deflate",
|
26
|
+
"HTTP_USER_AGENT"=>"Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.8 (like Gecko)",
|
27
|
+
"SCRIPT_NAME"=>"",
|
28
|
+
"SERVER_PROTOCOL"=>"HTTP/1.1",
|
29
|
+
"HTTP_ACCEPT_LANGUAGE"=>"en",
|
30
|
+
"HTTP_HOST"=>"localhost:2000",
|
31
|
+
"REMOTE_ADDR"=>"127.0.0.1",
|
32
|
+
"SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.8.6/2007-06-07)",
|
33
|
+
"HTTP_REFERER"=>"http://localhost:2000/",
|
34
|
+
"HTTP_COOKIE"=>"cuca_session=285715682bf030a19cb24b2560aabc36",
|
35
|
+
"HTTP_ACCEPT_CHARSET"=>"utf-8, utf-8;q=0.5, *;q=0.5",
|
36
|
+
"REQUEST_URI"=>"http://localhost:2000" + @test_path_info,
|
37
|
+
"SERVER_PORT"=>"2000",
|
38
|
+
"GATEWAY_INTERFACE"=>"CGI/1.1",
|
39
|
+
"QUERY_STRING"=>query_string,
|
40
|
+
"HTTP_ACCEPT"=>"text/html, image/jpeg, image/png, text/*, image/*, */*",
|
41
|
+
"SCRIPT_FILENAME"=>"/home/bones/workspace/cuca/scripts/../public/dispatch.cgi",
|
42
|
+
"REQUEST_METHOD"=>"GET",
|
43
|
+
"HTTP_CONNECTION"=>"Keep-Alive"}
|
44
|
+
end
|
45
|
+
|
46
|
+
def [](key)
|
47
|
+
# $stderr.puts "get FROM ENV_TABLE(#{key}) ==> #{the_env_table[key]}"
|
48
|
+
return the_env_table[key] || nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def env_table
|
53
|
+
EnvTable.new(@test_options)
|
54
|
+
end
|
55
|
+
|
56
|
+
def stdinput
|
57
|
+
# $stderr.puts "Returning STDINPUT"
|
58
|
+
StringIO.new("")
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(options)
|
62
|
+
@test_options = options
|
63
|
+
super()
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
data/lib/cuca/cgi_fix.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
# We'll add three methods to the cgi class in order to separete get/post requests:
|
6
|
+
# * parameters -- all (mixed) get and post
|
7
|
+
# * query_parameters -- get
|
8
|
+
# * request_parameters -- post
|
9
|
+
#
|
10
|
+
class CGI # :nodoc:
|
11
|
+
class << self
|
12
|
+
def fix_env(ec)
|
13
|
+
if (ec['PATH_INFO'].nil? || ec['PATH_INFO'] == '') then
|
14
|
+
pi = ec['REQUEST_URI']
|
15
|
+
pi = pi[0..(pi.index('?')-1)] if pi.include?('?')
|
16
|
+
ec['PATH_INFO'] = pi
|
17
|
+
end
|
18
|
+
|
19
|
+
if (ec['QUERY_STRING'].nil? || ec['QUERY_STRING'] == '') then
|
20
|
+
ec['QUERY_STRING'] = ec['REQUEST_URI'].include?('?') ?
|
21
|
+
ec['REQUEST_URI'].scan(/.?\?(.*)/)[0][0] :
|
22
|
+
""
|
23
|
+
end
|
24
|
+
ec
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def env_table
|
30
|
+
CGI::fix_env(ENV)
|
31
|
+
ENV
|
32
|
+
end
|
33
|
+
|
34
|
+
def parameters
|
35
|
+
query_parameters.merge(request_parameters)
|
36
|
+
end
|
37
|
+
|
38
|
+
def query_parameters
|
39
|
+
res = {}
|
40
|
+
query_string.split(/[&;]/).each do |p|
|
41
|
+
k, v = p.split("=")
|
42
|
+
v = '' if v.nil?
|
43
|
+
res[CGI.unescape(k)] = CGI.unescape(v)
|
44
|
+
end
|
45
|
+
res
|
46
|
+
end
|
47
|
+
|
48
|
+
def request_parameters
|
49
|
+
if request_method == 'POST' then
|
50
|
+
res = {}
|
51
|
+
params.each_pair { |k,v| res[k] = v[0] }
|
52
|
+
return res
|
53
|
+
else
|
54
|
+
return {}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/cuca/const.rb
ADDED
@@ -0,0 +1,240 @@
|
|
1
|
+
module Cuca
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
class BreakControllerException < StandardError
|
5
|
+
# flags can be:
|
6
|
+
# :layout => 'new_layout' or false
|
7
|
+
# :redirect => '/to/new/url' = dont render, just redirect
|
8
|
+
# :error => "something happend" = display an application error
|
9
|
+
attr_reader :flags
|
10
|
+
def initialize(flags = {})
|
11
|
+
@flags = flags
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# === Cuca Controller
|
17
|
+
#
|
18
|
+
# A controller handles the get/post request for your website and has the ability to
|
19
|
+
# generate an output directly making use of other Widgets and Layouts.
|
20
|
+
#
|
21
|
+
# The Controller class itself behaves just like Cuca::Widget, but instead of overwriting
|
22
|
+
# the output method you must implement run or post or get or any combination.
|
23
|
+
#
|
24
|
+
# Additionally you can also define a wrapping layout (using the layout function) and you can
|
25
|
+
# run code before a certain action is called using before_filter's.
|
26
|
+
#
|
27
|
+
# == Naming
|
28
|
+
#
|
29
|
+
# When you open a url like: http://your.website.com/users/list cuca will load the file /users/list.rb
|
30
|
+
# from your application path (usually app/users/list.rb). Within that file you must define one
|
31
|
+
# class derrived from Cuca::Controller with name FilenameController. The /users/list.rb example must
|
32
|
+
# therefor implement the ListController class.
|
33
|
+
#
|
34
|
+
# == Subcalls
|
35
|
+
#
|
36
|
+
# Sometimes you might want to implement another URL that is logically joined with your
|
37
|
+
# current page ("Ajax"?). To do this you can implement a (get|post|run)_subcallname method within
|
38
|
+
# your controller. This one will get called if you open http://your.website.com/-controller-subcallname .
|
39
|
+
#
|
40
|
+
# == Routing / Pretty URL's
|
41
|
+
#
|
42
|
+
# Depending on your directory structure within the app folder you can define variable directory names
|
43
|
+
# that will hint you within the controller.
|
44
|
+
#
|
45
|
+
# Example:
|
46
|
+
# File: app/user/__default_username/show.rb will map to http://user/johnrambo/show and define instance variable
|
47
|
+
# @username within the Controller defined in show.rb
|
48
|
+
#
|
49
|
+
# The magic URL prefix (default: __default_) can be change within App::Config
|
50
|
+
#
|
51
|
+
# == Examples
|
52
|
+
#
|
53
|
+
# Hello world (index.rb):
|
54
|
+
#
|
55
|
+
# class IndexController < Cuca::Controller
|
56
|
+
# def run
|
57
|
+
# content << "Hello World"
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# Using filters and layouts and generate page using the Markaby generator (fancy.rb):
|
62
|
+
#
|
63
|
+
# require 'cuca/generators/markaby'
|
64
|
+
#
|
65
|
+
# class FancyController < Cuca::Controller
|
66
|
+
# include Cuca::Generator::Markaby
|
67
|
+
# layout 'fancy'
|
68
|
+
# before_filter 'set_title'
|
69
|
+
#
|
70
|
+
# def set_title
|
71
|
+
# @page_title = "A Fancy Page"
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# def get
|
75
|
+
# mab do
|
76
|
+
# h1 "Welcome to the Fancy Page called #{@page_title}"
|
77
|
+
# p { text "Welcome to my"; b { paragraph } }
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
#
|
83
|
+
class Controller < Widget
|
84
|
+
|
85
|
+
attr_reader :cancel_execution # this can be set by anyone,
|
86
|
+
# methods get/post/run will not be executed
|
87
|
+
|
88
|
+
def self.def_layout
|
89
|
+
begin
|
90
|
+
self.def_layout_name
|
91
|
+
rescue
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.layout(name)
|
97
|
+
define_attr_method(:def_layout_name, name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.before_filter(method)
|
101
|
+
begin
|
102
|
+
filters = self.def_before_filter
|
103
|
+
filters = "#{filters};#{method}"
|
104
|
+
define_attr_method(:def_before_filter, filters)
|
105
|
+
rescue => e
|
106
|
+
define_attr_method(:def_before_filter, method)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def run_before_filters
|
111
|
+
# $stderr.puts "Run before filters"
|
112
|
+
begin
|
113
|
+
before_filter = self.class.def_before_filter
|
114
|
+
rescue NoMethodError => e
|
115
|
+
# == no filters defined at all
|
116
|
+
# $stderr.puts " No before filters defined for #{self.class}"
|
117
|
+
return
|
118
|
+
end
|
119
|
+
before_filter.split(';').each do |filter|
|
120
|
+
begin
|
121
|
+
if (!self.methods.include?(filter)) then
|
122
|
+
raise ApplicationException.new("Before filter method not defined: '#{filter}'")
|
123
|
+
end
|
124
|
+
ret = self.send(filter)
|
125
|
+
@cancel_execution = true if ret == false
|
126
|
+
# rescue NoMethodError => e
|
127
|
+
# raise ApplicationException.new "Before filter method not defined: '#{filter}'"
|
128
|
+
rescue BreakControllerException => bc
|
129
|
+
handle_exception(bc)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# this method will stop execution of the controller
|
136
|
+
# it's usefull to break somewhere in the middle or to
|
137
|
+
# set a different layout
|
138
|
+
# flags can be
|
139
|
+
# :layout - Set a new layout
|
140
|
+
# :redirect - redirect to a different page
|
141
|
+
# :error - An error message (for application errors)
|
142
|
+
def stop(flags = {})
|
143
|
+
raise BreakControllerException.new(flags)
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
# this piece of code will handle a thrown exception BreakControllerException
|
148
|
+
def handle_exception(e)
|
149
|
+
if e.flags.has_key?(:layout) then
|
150
|
+
self.class.layout(e.flags[:layout])
|
151
|
+
end
|
152
|
+
|
153
|
+
if e.flags.has_key?(:redirect) then
|
154
|
+
self.class.layout false
|
155
|
+
to = e.flags[:redirect]
|
156
|
+
clear
|
157
|
+
@_content = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"><html><head><title>Redirecting...</title><meta http-equiv=\"REFRESH\" content=\"0;url=#{to}\"></HEAD></HTML>"
|
158
|
+
@cancel_execution = true
|
159
|
+
end
|
160
|
+
|
161
|
+
if e.flags.has_key?(:error) then
|
162
|
+
self.class.layout false
|
163
|
+
clear
|
164
|
+
@error_message = e.flags[:error]
|
165
|
+
@cancel_execution = true
|
166
|
+
mab { html { body { h2 "Error"; text @error_message }}}
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
# Overwrite this method to handle get and post events
|
172
|
+
def run
|
173
|
+
end
|
174
|
+
|
175
|
+
# Overwrite this method to handle get events
|
176
|
+
def get
|
177
|
+
end
|
178
|
+
|
179
|
+
# Overwrite this method to handle post events
|
180
|
+
def post
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
|
185
|
+
# This is the method as called from App. It will call 'get', 'post', 'run' and handle
|
186
|
+
# exceptions. what within [post,get,run]
|
187
|
+
# TOTHINKABOUT: run before filter from this method?
|
188
|
+
public
|
189
|
+
def _do(what, subcall)
|
190
|
+
return if @cancel_execution
|
191
|
+
|
192
|
+
method_name = what
|
193
|
+
method_name = "#{method_name}_#{subcall}" if subcall
|
194
|
+
begin
|
195
|
+
self.send(method_name) if self.methods.include?(method_name)
|
196
|
+
rescue BreakControllerException => e
|
197
|
+
handle_exception(e)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
def initialize(*args)
|
203
|
+
# set a global variable of the controller object.
|
204
|
+
# Widgets can make use of this to call or modify the controller class.
|
205
|
+
$controller_object = self
|
206
|
+
|
207
|
+
@cancel_execution = false
|
208
|
+
super(*args)
|
209
|
+
|
210
|
+
# FIXME: to think about...
|
211
|
+
get_assigns.each_pair do |k,v|
|
212
|
+
instance_variable_set("@#{k}", v)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
def load_layout
|
218
|
+
return nil if self.class.def_layout.nil?
|
219
|
+
begin
|
220
|
+
layout_class = Object::const_get(self.class.def_layout.capitalize+"Layout")
|
221
|
+
rescue => e
|
222
|
+
# fixme - if layout loading fails we should display some error
|
223
|
+
return nil
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
public
|
228
|
+
def output
|
229
|
+
layout_class = load_layout
|
230
|
+
return content unless layout_class
|
231
|
+
c = content.to_s
|
232
|
+
a = get_assigns
|
233
|
+
a[:content_for_layout] = c
|
234
|
+
@_content = layout_class.new(:assigns=>a)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
end # Module
|
240
|
+
|