cuca 0.03 → 0.04

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.
@@ -0,0 +1,39 @@
1
+
2
+ * xxxx/xx/xx - 0.04
3
+ - Fixed error-message displaying traceback but not the actual exception
4
+ - Display trace if enabled on 'stop :error' events
5
+ - Option for autoloading support files (instead of bulk 'require')
6
+ - Fixed bug where session didn't close (saved to disk) if 'stop' was raised (fastcgi)
7
+ - 'url', 'base_path and 'path_tree' property added into urlmap class
8
+ - Priority option on filters.
9
+ - Mount option for external directories on application path
10
+ - Cleanup app.rb, removed some global variables
11
+ - get/post parameters are automaticaly visible within the session page container
12
+ - Added some unit tests
13
+ - Minor fixes in stdlib
14
+ - Some fixes in dispatchers
15
+ -
16
+
17
+
18
+ * 2008/05/18 - 0.03
19
+ - Fixed uncaught possible routing error exception
20
+ - Splitted load configuration to conf/config.rb and conf/environment.rb
21
+ - Allow setting the mime-type and http return status per controller action
22
+ - Added http 404 and 500 customizable error messages
23
+ - Option to switch off error tracebacks displayed in browser
24
+ - 'base_url' property added into the urlmap class
25
+ - Added 'layout' instance method within controller to overwrite
26
+ or disable default layout for current action
27
+ - fixed bug on fastcgi where the 'stop' method could remove the layout
28
+ definition from controller class
29
+ - Minor changes/fixes in application skeleton
30
+
31
+ * 2008/05/11 - 0.02
32
+ - Fixed bug in erb/view generator ("can't find view path")
33
+ - stdlib/listwidget:
34
+ - Fixed table joins not displaying content (dblist)
35
+ - Dynamic column sorting in both directions (ASC/DESC)
36
+ - Fixed session bug when using fastcgi
37
+ - minor documentation and stdlib improvements
38
+
39
+ * 2008/05/08 - 0.01 - Initial Release
@@ -2,7 +2,7 @@
2
2
  # Use this file to set initial cuca framework and application configuration
3
3
  # values - Only!
4
4
  #
5
- # You should NOT use this to load additional libraries or to modify cuca classes. Use
5
+ # You should not use this to load additional libraries or to modify cuca classes. Use
6
6
  # conf/environment.rb instead.
7
7
  #
8
8
  # The cuca framework itself isn't fully loaded at this point.
@@ -18,10 +18,20 @@ Cuca::App.configure do |config|
18
18
  ### future use?
19
19
  config.log_level = 3
20
20
 
21
- ### files within these directories will be automatically 'required' before
22
- ### your controller script get loaded.
21
+ ### Files within these directories will be automatically 'required' before
22
+ ### your controller script get loaded. Cuca will look for these directories
23
+ ### relatively to your action script. It will also require all these directories
24
+ ### in lower levels of your directory layout.
23
25
  config.include_directories = %w{_controllers _layouts _models _widgets}
24
26
 
27
+ ### This would be an autoload configuration
28
+ # config.include_directories = [
29
+ # { :dir => '_controllers', :class_naming = Proc.new { |f| f.capitalize+'Controller' } },
30
+ # { :dir => '_widgets', :class_naming = Proc.new { |f| f.capitalize+'Widget' } },
31
+ # { :dir => '_layouts', :class_naming = Proc.new { |f| f.capitalize+'Layout' } }
32
+ # { :dir => '_models', :class_naming = Proc.new { |f| f.capitalize } }
33
+ # ]
34
+
25
35
  ### For pretty url mapping
26
36
  # config.magic_action_prefix = '__default_'
27
37
 
@@ -38,12 +48,21 @@ Cuca::App.configure do |config|
38
48
  # config.http_500 = '500.html'
39
49
 
40
50
  ### display_errors: Instead of showing a http-500 page this will display an application
41
- ### trace (similar to php display-errors) on an error event. Switch that off in
42
- # production systems.
51
+ ### trace (similar to php display-errors) on an error event. Switch that off once in
52
+ ### production.
43
53
  # config.display_errors = true
44
54
 
45
55
  ### Default mime type to be sent within the http header unless specified by the
46
56
  ### Controller
47
57
  # config.default_mime_type = 'text/html'
48
58
 
59
+ ### Mount external Directories to the application path. This only works with
60
+ ### directories and have higher priority than local files.
61
+ ### Example:
62
+ # config.mount = { '/customer/special => "#{$cuca_path}/plugins/special",
63
+ # '/systeminfo/' => "/usr/cuca/static/sysinfo/" }
64
+
65
+ ### 'expires' http header for static content (only if served by the dispatcher), in seconds
66
+ # config.http_static_content_expires = 300
67
+
49
68
  end
@@ -1,31 +1,18 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
- # Set this Environment variable to load the cuca framework from a specific
4
- # Path. If not set it will try via rubygems.
5
-
6
- FRAMEWORK_PATH = ENV['CUCA_FRAMEWORK_PATH']
7
-
8
3
  $cuca_path = File.dirname(__FILE__)+"/../"
9
4
 
10
- if (FRAMEWORK_PATH) then
11
- $: << FRAMEWORK_PATH
12
- require 'cuca'
13
- require 'rubygems'
14
- else
15
- require 'rubygems'
16
- require 'cuca'
17
- end
5
+ require 'rubygems'
6
+ require 'cuca'
18
7
 
19
8
  Signal.trap("INT") do
20
9
  $stderr.puts "INT caught"
21
10
  exit
22
11
  end
23
12
 
24
-
25
13
  start = (Time.now.to_f * 1000).to_i
26
14
  application = Cuca::App.new
27
15
  application.cgicall
28
16
  stop = (Time.now.to_f * 1000).to_i
29
17
  dur_msec = stop - start
30
18
  application.logger.info("App::cgicall: #{dur_msec} msec [= #{(1000 / dur_msec).to_i} pages / second]")
31
-
@@ -1,18 +1,10 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
3
 
4
- FRAMEWORK_PATH = ENV['CUCA_FRAMEWORK_PATH']
5
-
6
4
  $cuca_path = File.dirname(__FILE__)+"/../"
7
5
 
8
- if (FRAMEWORK_PATH) then
9
- $: << FRAMEWORK_PATH
10
- require 'cuca'
11
- require 'rubygems'
12
- else
13
- require 'rubygems'
14
- require 'cuca'
15
- end
6
+ require 'rubygems'
7
+ require 'cuca'
16
8
 
17
9
  require "fcgi"
18
10
 
@@ -21,16 +13,13 @@ Signal.trap("INT") do
21
13
  exit
22
14
  end
23
15
 
24
- $cuca_path = File.dirname(__FILE__)+"/../"
25
-
16
+ application = Cuca::App.new
26
17
 
27
18
  FCGI.each_cgi do |cgi|
28
19
  CGI::fix_env(cgi.env_table)
29
20
  start = (Time.now.to_f * 1000).to_i
30
- application = Cuca::App.new(cgi)
31
- application.cgicall
21
+ application.cgicall(cgi)
32
22
  stop = (Time.now.to_f * 1000).to_i
33
23
  dur_msec = stop - start
34
24
  application.logger.info("App::cgicall (#{cgi.path_info}: #{dur_msec} msec [= #{(1000 / dur_msec).to_i} pages / second]")
35
25
  end
36
-
@@ -1,4 +1,4 @@
1
- # == Cuca - a widget-based web framework
1
+ # == Cuca - a ruby web framework
2
2
  #
3
3
  # Cuca is a small web development framework mainly designed to build
4
4
  # applications - there is little focus on design but more on coding
@@ -71,5 +71,9 @@ begin
71
71
  require $cuca_path+'/conf/environment'
72
72
  rescue LoadError => e
73
73
  $stderr.puts "WARN: Error loading conf/environment: #{e}"
74
+ rescue => e
75
+ e.backtrace.each do |b|
76
+ $stderr.puts" #{b}\n"
77
+ end
74
78
  end
75
79
 
@@ -1,13 +1,21 @@
1
1
  require 'logger'
2
+ require 'cuca/config'
3
+
4
+
5
+ def cuca_register_autoload(object, file)
6
+ autoload(object, file)
7
+ end
2
8
 
3
9
 
4
10
  module Cuca
5
11
 
12
+ module Objects
13
+ end
14
+
6
15
  # Sandbox is used internally run the action script defined by the controller
7
16
  # In future this can be extended to implement some security features etc..
8
17
  class Sandbox
9
18
 
10
-
11
19
  def self.run(controller_class_name, mod, assigns, request_method, subcall)
12
20
  self.class.send(:include, mod)
13
21
  controller_class = mod::const_get(controller_class_name)
@@ -16,7 +24,7 @@ class Sandbox
16
24
 
17
25
 
18
26
  controller.send('_do'.intern, 'run', subcall)
19
- case $cgi.request_method
27
+ case request_method
20
28
  when 'POST'
21
29
  controller.send('_do'.intern, 'post', subcall)
22
30
  when 'GET'
@@ -36,55 +44,12 @@ end
36
44
  # Normally you just create a Cuca::App object and run the cgicall function with optional with
37
45
  # a cgi object as paramenter.
38
46
  # Before doing that you must set $cuca_path to the root of your appication structure.
39
-
40
47
  class App
41
48
 
42
- # == Configure the application
43
- # App::Config is normally called from conf/environment.rb . The attributes below the framework
44
- # will make use of, but you can always define own ones.
45
- #
46
- # == Example
47
- #
48
- # Cuca::App.configure do |conf|
49
- # conf.include_directories = %{_widgets _special_widgets}
50
- # conf.database = 'mydb' # application specific
51
- # end
52
- # === Attributes:
53
- #
54
- # * include_directories
55
- # An array of directories that will be automatically included if you call an action.
56
- # If one of the directories is found in the path where the action file is located it will require
57
- # all files found. (default: %w{_controllers _widgets _layouts} )
58
- # * log_level - default 3
59
- # * magic_prefix - default: '__default_' (see Cuca::Controller)
60
- class Config < Hash
61
- def method_missing(m, *params)
62
- met = m.id2name
63
- raise NoMethodError if met[met.size-1].chr != '='
64
- self[met[0..met.size-2]] = params[0]
65
- end
66
-
67
- # some default stuff
68
- def initialize
69
- self['include_directories'] = %w{_controllers _widgets _layouts}
70
- self['log_level'] = 3
71
- self['magic_prefix'] = '__default_'
72
- self['session_key'] = 'cuca_session'
73
- self['session_prefix'] = 'cuca.'
74
- self['session_valid'] = 3600*24
75
- self['view_directory'] = 'app/_views' # where to find views for the view/erb generator
76
- self['http_404'] = '404.html'
77
- self['http_500'] = '500.html'
78
- self['default_mime_type'] = 'text/html'
79
- self['display_errors'] = true
80
- end
81
-
82
- end
83
49
 
84
- attr_reader :conf, :cgi, :logger, :urlmap
50
+ attr_reader :app_path, :log_path, :public_path, :urlmap, :cgi, :logger
85
51
 
86
- @@app_config = Config.new
87
- @@oncall = []
52
+ @@app_config = Cuca::Config.new
88
53
 
89
54
  ## Application configuration
90
55
  public
@@ -96,98 +61,32 @@ class App
96
61
  @@app_config
97
62
  end
98
63
 
99
- # if you should need to get notified on every
100
- # (cgi)-call you can register a proc here.
101
- # The SessionFlash/Page is makeing use of this
102
- def self.oncall(&block)
103
- @@oncall << block
104
- end
105
-
106
-
107
- # cgi implementation test
108
64
  private
109
- def cgidump(cgi)
110
- s = ""
111
- s << "-----ENV------<br>"
112
- cgi.env_table.each_pair { |k,v| s << "#{k} => #{v}<br>" }
113
- s << "--------------<br>"
114
- s << "Server Software: #{cgi.server_software}<br>"
115
- s << "PATH INFO: #{cgi.path_info}<br>"
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")
116
73
  end
117
74
 
118
- # Creates the @conf global variable - internal config
75
+
76
+ # Initializes settings to run the appliction
119
77
  #
120
- # APP_PATH = Path the the /app folder
121
- # CGI = CGI object
122
- # DEF_EXT = Default script extensions (.rb)
123
- # DEF_ACTION = (like 'index')
124
- # SCRIPT = Full path to the ruby script dealing with ACTION
125
- # ACTION = Action called (=links to SCRIPT, eg 'show' or 'index')
126
- # PATH_INFO = raw path info as from cgi
127
- # PATH_TREE = array of path, without action
78
+ # @xxx_path - path to /app directory
128
79
  private
129
- def mk_config
130
- @conf = {}
131
-
132
- # To have a look at ENV and cgi uncomment this
133
- # @cgi.out { cgidump(@cgi) }
134
- # raise "Debug stop"
135
-
136
-
137
- # APP_PATH = Path the the /app folder
138
- # @conf['APP_PATH'] = File.expand_path(File.dirname(__FILE__)+"/../app")
139
- @conf['APP_PATH'] = $cuca_path + "/app"
140
-
141
- # PUBLIC_PATH = Path to the /public folder
142
- # @conf['PUBLIC_PATH'] = File.expand_path(File.dirname(__FILE__)+"/../public")
143
- @conf['PUBLIC_PATH'] = $cuca_path + "/public"
144
-
145
- # LOG_PATH = Path to logging directory
146
- # @conf['LOG_PATH'] = File.expand_path(File.dirname(__FILE__)+"/../log")
147
- @conf['LOG_PATH'] = $cuca_path + "/log"
148
-
149
- # CGI = CGI object
150
- @conf['CGI'] = @cgi
151
-
152
- # DEF_EXT = Default script extensions
153
- @conf['DEF_EXT'] = ".rb"
154
-
155
- @conf['DEF_ACTION'] = 'index'
156
-
157
- # Cookie name
158
- @conf['COOKIE_NAME'] = 'cucahap'
159
-
160
- # ACTION = Action called (=links to SCRIPT), eg 'index'
161
- @conf['ACTION'] = @cgi.path_info.reverse[0].chr == '/' ? @conf['DEF_ACTION'] : @cgi.path_info.split('/').last.downcase
162
-
163
- # PATH_INFO = raw path info as from cgi
164
- @conf['PATH_INFO'] = @cgi.path_info
165
-
166
- # PATH_TREE = path info as array - without action
167
- # @conf['PATH_TREE'] = [''].concat(@conf['PATH_INFO'].split('/').collect { |e| (e==@conf['ACTION'] || e == '') ? nil : e }.compact)
168
-
169
-
80
+ def init_call(path_info)
170
81
  require 'cuca/urlmap'
171
82
 
172
83
  begin
173
- mapping = URLMap.new(File.expand_path(@conf['APP_PATH']), @conf['PATH_INFO'])
174
- @conf['SUBCALL'] = mapping.subcall
175
- @conf['SCRIPT'] = mapping.script
176
- @conf['ASSIGNS'] = mapping.assigns
177
- @conf['ACTION'] = mapping.action
178
- @urlmap = mapping
84
+ @urlmap = URLMap.new(@app_path, path_info)
179
85
 
180
86
  rescue RoutingError => r # no script found - maybe serve a static file?
181
- @conf['SCRIPT'] = nil
87
+ @urlmap = nil # == no script found
182
88
  return
183
89
  end
184
-
185
- # get the PATH_TREE for file inclusion!
186
-
187
- c_ap = File.expand_path(@conf['APP_PATH'])
188
- pt = @conf['SCRIPT'][c_ap.size..-1]
189
- ac_script = @conf['SCRIPT'].split('/').last
190
- @conf['PATH_TREE'] = [''].concat(pt.split('/').collect { |e| (e==ac_script || e == '') ? nil : e }.compact)
191
90
  end
192
91
 
193
92
  # will do a 'require' on all .rb files in path
@@ -201,47 +100,63 @@ class App
201
100
  end
202
101
  Dir.chdir(pwd)
203
102
  end
204
-
205
-
206
-
207
- public
208
- def initialize(cgi = nil)
209
-
210
- $cgi = cgi || CGI.new
211
- @cgi = $cgi
212
103
 
213
- mk_config
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
214
119
 
215
- @logger = Logger.new(@conf['LOG_PATH'] + "/messages")
216
- $logger = @logger
217
- $conf = @conf
120
+ public
121
+ def initialize
218
122
  $app = self
123
+ init_app
124
+ end
219
125
 
220
- # rescue RuntimeError => e
221
- # @cgi.out { "Error initializing the app: #{$!}" }
222
- rescue RoutingError => e
223
- @cgi.out { "Error initializing the app (RoutingError: #{$!})" }
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
224
138
  end
225
139
 
226
- # this has to be public for testing scripts
227
140
  public
228
- def load_support_files # :nodoc:
141
+ def load_support_files(urlmap) # :nodoc:
229
142
  # $stderr.puts "======== load support files ========="
230
143
  # $stderr.puts "Inc Dir: #{App::config['include_directories'].inspect}"
231
144
  # $stderr.puts "Path tr: #{@conf['PATH_TREE'].inspect}"
232
145
  # $stderr.puts "====================================="
233
146
 
234
- base_path = @conf['APP_PATH']
235
- @conf['PATH_TREE'].each do |t|
236
- base_path << "/#{t}"
237
- (App::config['include_directories'] || []).each do |id|
238
- include_files "#{base_path}/#{id}"
239
- end
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
240
159
  end
241
-
242
- # (App::config['include_directories'] || []).each do |id|
243
- # @conf['PATH_TREE'].each { |t| include_files @conf['APP_PATH'] + '/'+t+"/#{id}/"}
244
- # end
245
160
  end
246
161
 
247
162
 
@@ -249,8 +164,11 @@ class App
249
164
  private
250
165
  def get_error(title, exception, show_trace = true, file=nil)
251
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}"
252
169
  if (show_trace) then
253
170
  exception.backtrace.each do |b|
171
+ $stderr.puts " #{b}"
254
172
  err +="<br/>#{b}"
255
173
  end
256
174
  else
@@ -264,14 +182,14 @@ class App
264
182
 
265
183
 
266
184
  public
267
- def cgicall
268
- script = @conf['SCRIPT']
269
-
185
+ def cgicall(cgi = nil)
186
+ @cgi = cgi || CGI.new
270
187
 
188
+
271
189
  #
272
190
  # 1st priority: Serve a file if it exists in the 'public' folder
273
191
  #
274
- file = @conf['PUBLIC_PATH'] + '/' + @conf['PATH_INFO']
192
+ file = @public_path + '/' + @cgi.path_info
275
193
  if File.exists?(file) && File.ftype(file) == 'file' then
276
194
  require 'cuca/mimetypes'
277
195
  mt = MimeTypes.new
@@ -279,16 +197,19 @@ class App
279
197
  extension = file.scan(/.*\.(.*)$/)[0][0] if file.include?('.')
280
198
  extension ||= 'html'
281
199
  mime = mt[extension] || 'text/html'
282
- @cgi.out(mime) { f.read }
200
+ @cgi.out('type' => mime,
201
+ 'expires' => (Time.now+App.config['http_static_content_expires'])) { f.read }
283
202
  f.close
284
203
  return
285
204
  end
286
205
 
206
+ init_call(@cgi.path_info)
207
+
287
208
  # If config (urlmap) couldn't find a script then let's give up
288
209
  # with a file-not-found 404 error
289
- if script.nil? then
210
+ if @urlmap.nil? then
290
211
  begin
291
- file = "#{@conf['PUBLIC_PATH']}/#{Cuca::App.config['http_404']}"
212
+ file = "#{@public_path}/#{Cuca::App.config['http_404']}"
292
213
  c = File.open(file).read
293
214
  @cgi.out('status' => 'NOT_FOUND', 'type' => 'text/html') { c }
294
215
  rescue => e
@@ -297,6 +218,8 @@ class App
297
218
  return
298
219
  end
299
220
 
221
+ logger.info "CGICall on #{@urlmap.url} - #{@urlmap.script}"
222
+ script = @urlmap.script
300
223
 
301
224
  #
302
225
  # 2nd: Check if we have a script for requested action
@@ -306,15 +229,14 @@ class App
306
229
  return
307
230
  end
308
231
 
309
- # Notify who needs to know
310
- @@oncall.each { |p| p.call }
311
-
312
232
  # 3rd: Load additional files
313
- load_support_files
233
+ load_support_files(@urlmap)
234
+
235
+ test = ApplicationController.new
314
236
 
315
237
 
316
238
  # 4th: Now let's run the actual page script code
317
- controller_class_name = @conf['ACTION'].capitalize+"Controller"
239
+ controller_class_name = @urlmap.action.capitalize+"Controller"
318
240
 
319
241
  # Clear all hints
320
242
  Widget::clear_hints()
@@ -338,21 +260,26 @@ class App
338
260
  begin
339
261
 
340
262
  (status, mime, content) = Sandbox.run(controller_class_name,
341
- @urlmap.action_module, @conf['ASSIGNS'],
342
- $cgi.request_method, @conf['SUBCALL'])
263
+ @urlmap.action_module, @urlmap.assigns,
264
+ @cgi.request_method, @urlmap.subcall)
343
265
 
266
+ logger.info "CGICall OK"
344
267
  @cgi.out('type' => mime, 'status' => status) {content}
345
268
 
346
269
  rescue Cuca::ApplicationException => e
347
- err = get_error("Application Error", e,
270
+ err = get_error("Application Error", e,
348
271
  Cuca::App.config['display_errors'], Cuca::App.config['http_500'])
349
272
  @cgi.out('status' => 'SERVER_ERROR') { err }
273
+
274
+ logger.info "CGICall Application Error"
350
275
  return
351
276
 
352
277
  rescue => e
353
278
  err = get_error("System Error", e,
354
279
  Cuca::App.config['display_errors'], Cuca::App.config['http_500'])
355
280
  @cgi.out('status' => 'SERVER_ERROR') { err }
281
+
282
+ logger.info "CGICall System Error"
356
283
  return
357
284
 
358
285
  end