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.
@@ -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
-