cuca 0.06 → 0.07

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- # == Configure the application
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
- # * include_directories
16
- # An array of directories that will be automatically included if you call an action.
17
- # If one of the directories is found in the path where the action file is located it will require
18
- # all files found. (default: %w{_controllers _widgets _layouts} )
19
- # Optionally it's possible to autoload these support files. To do this you must pass an
20
- # array of hashes to the method with the following keys:
21
- # - :dir The name of the directory (e.g. _controllers)
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
@@ -13,7 +13,7 @@ attr_reader :flags
13
13
  end
14
14
 
15
15
 
16
- # === Cuca Controller
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: __default_) can be change within App::Config
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.methods.include?(filter)) then
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.methods.include?(method_name)
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
- # fixme - if layout loading fails we should display some error
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
@@ -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
- class AssignBindings # :nodoc:
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(AssignBindings.new(get_assigns, self).get_bindings)
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
-