cuca 0.03 → 0.04

Sign up to get free protection for your applications and to get access to all the features.
@@ -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