cuca 0.05 → 0.06

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,13 +1,22 @@
1
+ * 2009/03/04 - 0.06
2
+ - Fixed sorting bug of filters with priority specified
3
+ - Nicer pagination on listwidget for large lists
4
+ - Allowing to tune default query definitions on listwidget
5
+ - Fixed bad rescue of NameError which resulted in misleading exception tracebacks
6
+ - Catch exceptions on loading/parsing the controller code
7
+ - Changed FormWidget and ARFormWidget, added FormElements module
8
+ - 'stop' can switch off after filters with :no_after_filters option
9
+ - Console will automatically load root support files
10
+ - Delete method for session.page variables
1
11
 
2
- * 2009/08/20 - 0.05
3
- - Widget can be defined within the action controller file.
12
+ * 2008/08/20 - 0.05
13
+ - Widget definition can be done within the action controller file.
4
14
  - Fixed minor typos in application skeleton conf/config.rb
5
15
  - Fix to catch exceptions in conf/config and conf/environment
6
16
  - Fixes & minor changes in stdlib/form
7
17
  - Stdlib listwidget rewrite_hooks will pass named hash instead of plain array
8
18
  as row data
9
19
  - fixed script/console to load support files
10
- - Fixed bad error rescueing in generators that displayed irritating errors
11
20
 
12
21
  * 2008/06/17 - 0.04
13
22
  - Fixed error-message displaying traceback but not the actual exception
data/README ADDED
@@ -0,0 +1,34 @@
1
+
2
+ == Installing Cuca
3
+
4
+ To use the source code you can simply run:
5
+ sudo renew.sh
6
+
7
+ To install the gem from the internet use
8
+ gem install --remote cuca
9
+
10
+ See http://cuca.rubyforge.org for more information how to get started.
11
+
12
+
13
+ == Coding on the Framework
14
+
15
+ You can tell your application to load the cuca framework from a
16
+ different location instead from rubygems. For that create a new dispatcher
17
+ cgi script, a minimal version would be:
18
+
19
+ #!/usr/bin/ruby
20
+
21
+ FRAMEWORK_PATH = '/home/bones/path/to/cuca-framework/lib'
22
+ $: << FRAMEWORK_PATH
23
+
24
+ require 'cuca'
25
+ require 'rubygems'
26
+
27
+ application = Cuca::App.new
28
+ application.cgicall
29
+
30
+
31
+ And tell your webserver to use this new handler (or edit the script/server*
32
+ files).
33
+
34
+
@@ -17,7 +17,7 @@ class IndexController < ApplicationController
17
17
  h2 { "If you want to learn cuca" }
18
18
  ul do
19
19
  li { text "Have a look at the Demo Widgets: "; SLink('demo', 'Here') }
20
- li { text "Checkout the cuca website: "; SLink("http://cuca.rubyforge.org") }
20
+ li { text "Checkout the cuca website: "; SLink("http://cuca.rubyforge.net") }
21
21
  li { text "Read the source code of these examples" }
22
22
  end
23
23
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/ruby
2
2
  # A simple irb session that loads the cuca framework
3
3
 
4
- libs = " -r irb/completion -r rubygems -r cuca"
4
+ libs = " -r irb/completion -r rubygems -r cuca -r cuca_console"
5
5
  exec "irb #{libs} --simple-prompt"
data/lib/cuca/app.rb CHANGED
@@ -232,7 +232,7 @@ class App
232
232
  # 3rd: Load additional files
233
233
  load_support_files(@urlmap)
234
234
 
235
- # test = ApplicationController.new
235
+ test = ApplicationController.new
236
236
 
237
237
 
238
238
  # 4th: Now let's run the actual page script code
@@ -245,20 +245,18 @@ class App
245
245
  controller_module = @urlmap.action_module
246
246
 
247
247
 
248
- controller_module.module_eval(File.open(script).read, script) unless \
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 \
249
252
  controller_module.const_defined?(controller_class_name.intern)
250
253
 
251
- # Catch a common user error
252
- if !controller_module.const_defined?(controller_class_name.intern) then
253
- @cgi.out { "Could not find #{controller_class_name} defined in #{script}" }
254
- return
255
- end
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)
256
257
 
257
- #
258
- # Load the controller
259
- #
260
- begin
261
258
 
259
+ # run controller
262
260
  (status, mime, content) = Sandbox.run(controller_class_name,
263
261
  @urlmap.action_module, @urlmap.assigns,
264
262
  @cgi.request_method, @urlmap.subcall)
data/lib/cuca/const.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Cuca
2
- VERSION = '0.05'
2
+ VERSION = '0.06'
3
3
  end
@@ -197,7 +197,7 @@ class Controller < Widget
197
197
  filters << [mp[0], mp[1]]
198
198
  end
199
199
 
200
- sorted_filters = filters.sort { |a,b| a[1] <=> b[1] }
200
+ sorted_filters = filters.sort { |a,b| a[1].to_i <=> b[1].to_i }
201
201
 
202
202
  sorted_filters.each do |f|
203
203
  break if @cancel_execution
@@ -226,7 +226,8 @@ class Controller < Widget
226
226
  # run defined after_filters
227
227
  public
228
228
  def run_after_filters
229
- run_filters(:def_after_filter, 'After Filters')
229
+ run_filters(:def_after_filter, 'After Filters') \
230
+ unless @_stop_no_after_filters
230
231
  ce = @cancel_execution
231
232
  @cancel_execution = false
232
233
  run_filters(:def_priority_after_filter, 'Priority After Filters')
@@ -238,9 +239,10 @@ class Controller < Widget
238
239
  # it's usefull to break somewhere in the middle or to
239
240
  # set a different layout
240
241
  # flags can be
241
- # :layout - Set a new layout
242
+ # :layout - Set a new layout, or 'false' for no layout
242
243
  # :redirect - redirect to a different page
243
244
  # :error - An error message (for application errors)
245
+ # :no_after_filters - Do not execute any after filters defined
244
246
  def stop(flags = {})
245
247
  raise BreakControllerException.new(flags)
246
248
  end
@@ -252,6 +254,10 @@ class Controller < Widget
252
254
  @_layout = e.flags[:layout]
253
255
  end
254
256
 
257
+ if e.flags.has_key?(:no_after_filters) then
258
+ @_stop_no_after_filters = true
259
+ end
260
+
255
261
  if e.flags.has_key?(:redirect) then
256
262
  @_layout = false
257
263
  to = e.flags[:redirect]
@@ -8,21 +8,24 @@ class ::Markaby::Builder # :nodoc:
8
8
  def method_missing(sym, *args, &block ) # :nodoc:
9
9
  class_name = sym.id2name
10
10
 
11
- # give up immediately if we don't have a Constant capable method
12
- return old_method_missing(sym, *args, &block) if
13
- class_name[0].chr.upcase != class_name[0].chr
14
-
15
-
16
- widget_name = class_name+'Widget'
17
-
18
- c=Object::const_get(widget_name) if Object::const_defined?(widget_name)
19
-
20
- if (c.nil? && !$app.nil?) then
21
- c = $app.urlmap.action_module.const_get(widget_name) if \
22
- $app.urlmap.action_module.const_defined?(widget_name)
11
+ return old_method_missing(sym, *args, &block) if
12
+ (class_name[0].chr.upcase != class_name[0].chr)
13
+
14
+ if Object.const_defined?(class_name+'Widget') then
15
+ c = Object::const_get(class_name+"Widget")
16
+ else
17
+ # try to find the widget in the action namespace
18
+ return old_method_missing(sym, *args,&block) if $app.nil?
19
+
20
+ am = $app.urlmap.action_module
21
+ if am.const_defined?(class_name+'Widget') then
22
+ c = am.const_get(class_name+'Widget')
23
+ else
24
+ return old_method_missing(sym, *args, &block)
25
+ end
23
26
  end
24
27
 
25
- return old_method_missing(sym, *args, &block) if c.nil?
28
+ # $stderr.puts "Widget in markaby: Class: #{class_name}, \n\n assigns: #{@assigns.inspect} \n\n"
26
29
 
27
30
  widget = c.new({:args => args,
28
31
  :assigns => @assigns },
@@ -52,37 +52,36 @@ module View
52
52
  @base = base_object
53
53
  end
54
54
 
55
-
56
-
57
55
  def method_missing(sym, *args, &block )
58
56
  class_name = sym.id2name
59
-
60
- # $stderr.puts "View: Method missing: #{class_name}: #{@base.class.name}"
61
57
 
58
+
62
59
  # 1st try to find method in the base widget
63
60
  if @base.methods.include?(class_name) then
64
- return @base.send(class_name, *args, &block)
61
+ return @base.send(class_name, *args, &block)
65
62
  end
66
-
67
- # 2nd try to find a widget in root namespace and the controllers
68
- widget_name = class_name+'Widget'
69
- c=Object::const_get(widget_name) if Object.const_defined?(widget_name)
70
- if (c.nil? && !$app.nil?) then
71
- c = $app.urlmap.action_module.const_get(widget_name) if \
72
- $app.urlmap.action_module.const_defined?(widget_name)
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')
73
71
  end
74
-
75
- raise "Undefined method: #{sym.id2name}" if c.nil?
76
-
72
+
73
+ raise NameError.new "Undefined method: #{class_name}" unless c
74
+
77
75
  widget = c.new({:args => args,
78
76
  :assigns => @assigns },
79
77
  &block)
80
78
 
81
-
79
+ # $stderr.puts "Widget:" + widget.inspect
82
80
  return widget.to_s
81
+
82
+ # $stderr.puts "Good"
83
83
  end
84
- end
85
-
84
+ end
86
85
 
87
86
  # VIEW_DIR = $cuca_path+'app/_views'
88
87
 
@@ -18,7 +18,7 @@ class SessionPage
18
18
  @ses = session
19
19
  @ses[:SessionPage] ||= {}
20
20
  pagemem[pagekey] ||= {}
21
- session.cgi.parameters.each_pair { |k,v| self[k] = v }
21
+ session.cgi.parameters.each_pair { |k,v| self[k] = v if v.kind_of?(String) }
22
22
  expire
23
23
  end
24
24
 
@@ -29,6 +29,11 @@ class SessionPage
29
29
  def []=(key,value)
30
30
  pagemem[pagekey][key] = value
31
31
  end
32
+
33
+ # remove a variable from page memory
34
+ def delete(key)
35
+ pagemem[pagekey].delete(key)
36
+ end
32
37
 
33
38
  private
34
39
  def expire
@@ -1,64 +1,65 @@
1
1
  require 'cuca/stdlib/form'
2
2
  require 'cuca/stdlib/formerrors'
3
+ require 'cuca/stdlib/formelements'
3
4
  require 'cuca/generator/markaby'
4
5
 
5
6
  # == Form's for ActiveRecord
6
- # AR Form can work just by providing one model of ActiveRecord.
7
+ # AR Form build a form by providing one model of ActiveRecord.
7
8
  # Likly that you want to overwrite the form method to
8
- # run a custom layout. You can use the fe* methods to
9
- # build form elements for the model columns definition.
9
+ # run a custom layout.
10
10
  #
11
11
  # This Widget will call <form_name>_submit(model) if Form is submitted
12
- # and validation passed. You still have to ::save the model.
12
+ # and validation of the model is passed. You still have to save the model.
13
13
  #
14
14
  # = Example:
15
15
  #
16
16
  # ARForm('user_edit', User.find_by_username('bones'),
17
17
  # :disable_on_update => ['username', 'created'])
18
18
  #
19
+
20
+
19
21
  class ARFormWidget < FormWidget
20
22
 
21
23
  include Cuca::Generator::Markaby
24
+ include Cuca::FormElements
22
25
 
23
26
  # valid options
24
27
  # * :disabled_on_create => ['field_name_1', 'field_name_2', ..]
25
28
  # switch off fields on new records
26
29
  # * :diabled_on_update => ['field_name_1', 'field_name_2', ..]
27
30
  # switch off fields on existing records
31
+ # * .. options from FormWidgets ...
28
32
  def output(form_name, model, options = {})
29
33
  @model = model
30
34
  @disabled_on_update = options[:disabled_on_update] || []
31
35
  @disabled_on_create = options[:disabled_on_create] || []
32
36
  @hidden_on_update = options[:hidden_on_update] || []
33
37
  @hidden_on_create = options[:hidden_on_create] || []
38
+
39
+ options[:default_values] = model.attributes.merge(options[:default_values] || {})
40
+
34
41
  super(form_name, options)
35
42
  end
36
43
 
44
+
45
+ # On submit will pass the model to the callback on the controller
37
46
  def on_submit
38
47
  controller.send(@form_name+'_submit', @model) unless controller.nil?
39
-
40
- # this is to reload the form with the new values in case the formname_submit did
41
- # save something:
42
48
  clear
43
- form
44
49
  end
45
50
 
46
- # overwrite this method to perform modifications
47
- # before validateing the data
48
- def before_validate(variables)
49
- variables
50
- end
51
51
 
52
- def validate
52
+
53
+ #
54
+ # Validate will check on ActiveRecord validation errors
55
+ #
56
+ def validate(variables)
53
57
  form if @_content.empty? # password fields might write hints to the validator...
54
- @form_erros = {}
55
- p = request_parameters.dup
58
+ clear
59
+ @form_errors = {}
60
+ p = variables
56
61
  p.delete(@submit_name)
57
62
 
58
-
59
- p = before_validate(p)
60
-
61
-
62
63
  if @model.new_record? then
63
64
  @disabled_on_create.each { |d| p.delete(d) }
64
65
  @hidden_on_create.each { |d| p.delete(d) }
@@ -67,13 +68,12 @@ class ARFormWidget < FormWidget
67
68
  @hidden_on_update.each { |d| p.delete(d) }
68
69
  end
69
70
 
70
-
71
71
  # don't save empty passwords!!
72
72
  @password_fields ||= []
73
73
  @password_fields.each do |pwf|
74
74
  p.delete(pwf) if p[pwf].chomp.empty?
75
75
  end
76
- # $stderr.puts p.inspect
76
+
77
77
  @model.attributes = p
78
78
 
79
79
  return true if @model.valid?
@@ -82,20 +82,17 @@ class ARFormWidget < FormWidget
82
82
  @form_errors[k] = v
83
83
  end
84
84
  end
85
-
86
- # to make a 2 digit string out a number
87
- private
88
- def twodig(n)
89
- n.to_s.length == 1 ? "0"+n.to_s : n.to_s
90
- end
91
-
92
- def field_enable?(fname)
85
+
86
+
87
+
88
+ def field_enable?(fname)
93
89
  if @model.new_record? then
94
90
  return @disabled_on_create.include?(fname) ? false : true
95
91
  else
96
92
  return @disabled_on_update.include?(fname) ? false : true
97
93
  end
98
94
  end
95
+
99
96
  def field_hidden?(fname)
100
97
  if @model.new_record? then
101
98
  return @hidden_on_create.include?(fname) ? true : false
@@ -103,173 +100,52 @@ class ARFormWidget < FormWidget
103
100
  return @hidden_on_update.include?(fname) ? true : false
104
101
  end
105
102
  end
106
-
107
-
108
- private
109
- def fe_text(name, value,enabled)
110
- "<input type='text' name='#{name}' value='#{value}' #{'disabled' unless enabled}>"
111
- end
112
-
113
- private
114
- def fe_textarea(name, value, enabled, rows=4, cols = 50)
115
- "<textarea name='#{name}' rows='#{rows}' cols='#{cols}'>#{value}</textarea>"
116
- end
117
103
 
118
-
119
- # the fe-password is special: will never contain a value and will not save the
120
- # password if nothing was typed into the field
121
- private
122
- def fe_password(name,enabled)
123
- @password_fields ||= []
124
- @password_fields << name
125
- r = "<input type='password' name='#{name}' #{'disabled' unless enabled}>"
126
- if !@model.new_record? then
127
- r << "<br><small>Leave empty to keep current password</small>"
128
- end
129
- return r
130
- end
131
-
132
- # this is to build a select box, example:
133
- # fe_select('gender', [['f', 'female'],['m','Male']], true) or
134
- # fe_select('gender', ['f','m'], true)
135
- private
136
- def fe_select(name, options, enabled)
137
- r = "<select name='#{name}' #{'disabled' unless enabled}>\n"
138
- options.each do |o|
139
- ov = o.instance_of?(Array) ? o[0] : o
140
- sel = ''
141
- sel = ' selected' if @model.send(name.intern) == ov
142
- if o.instance_of?(Array) then
143
- r+="<option value='#{o[0]}'#{sel}>#{o[1]}</option>\n"
144
- else
145
- r+="<option value='#{o}'#{sel}>#{o}</option>\n"
146
- end
147
- end
148
- r+="</option>\n"
149
- end
150
-
151
-
152
- def fe_datetime(name, v,enabled)
153
- v = Time.now unless v
154
- v = "#{v.year}/#{twodig(v.month)}/#{twodig(v.day)} #{v.hour}:#{v.min}" if v.instance_of?(Time)
155
- r = <<-EOS
156
- <input type='text' name='#{name}' id='i_#{name}' value='#{v}' #{'disabled' unless enabled}>
157
- EOS
158
- if enabled then
159
- r << <<-EOS
160
- <input type='submit' id='s_#{name}' value='...'>
161
- <script type="text/javascript">
162
- Calendar.setup({
163
- inputField : "i_#{name}", // id of the input field
164
- ifFormat : "%Y/%m/%d %H:%M", // format of the input field
165
- showsTime : true, // will display a time selector
166
- button : "s_#{name}", // trigger for the calendar (button ID)
167
- singleClick : true, // double-click mode
168
- step : 1 // show all years in drop-down boxes (instead of every other year as default)
169
- });
170
- </script>
171
- </input>
172
- EOS
173
- end
174
- return r
175
- end
176
-
177
- # select date.. no time
178
- def fe_date(name, v,enabled)
179
- v = Time.now unless v
180
- r = <<-EOS
181
- <input type='text' name='#{name}' id='i_#{name}' value='#{v.year}/#{twodig(v.month)}/#{twodig(v.day)}' #{'disabled' unless enabled}>
182
- EOS
183
- if enabled then
184
- r << <<-EOS
185
- <input type='submit' id='s_#{name}' value='...'>
186
- <script type="text/javascript">
187
- Calendar.setup({
188
- inputField : "i_#{name}", // id of the input field
189
- ifFormat : "%Y/%m/%d", // format of the input field
190
- showsTime : false, // will display a time selector
191
- button : "s_#{name}", // trigger for the calendar (button ID)
192
- singleClick : true, // double-click mode
193
- step : 1 // show all years in drop-down boxes (instead of every other year as default)
194
- });
195
- </script>
196
- </input>
197
- EOS
198
- end
199
- return r
200
- end
201
-
202
- def fe_time(name,v,enabled)
203
- "<input name='#{name}' size=5 type='text' value='#{v || '0:00'}'#{' disabled' unless enabled}>"
204
- end
205
-
206
- def fe_bool(name,v,enabled)
207
- r = ''
208
- r << "<select name='#{name}' #{'disabled' unless enabled}>\n"
209
- r << "<option #{"selected" if v} value='t'>true</option>\n"
210
- r << "<option #{"selected" if !v} value='f'>false</option>\n"
211
- r << "</select>\n"
212
- end
213
-
214
- def fe_int(name,v,enabled)
215
- "<input type='text' name='#{name}' value='#{v}' #{'disabled' unless enabled} size=5>"
216
- end
217
-
218
- def fe(type, name, value='')
104
+
105
+
106
+ def fe(type, name)
219
107
  return '' if field_hidden?(name)
220
- enabled = field_enable?(name)
108
+ attribs = {}
109
+ attribs[:disabled] = '' unless field_enable?(name)
221
110
  r = ""
222
111
  case(type)
223
112
  when :string
224
- r << fe_text(name,value,enabled)
113
+ r << fe_text(name, attribs)
225
114
  when :boolean
226
- r << fe_bool(name,value,enabled)
115
+ r << fe_bool(name, attribs)
227
116
  when :integer
228
- r << fe_int(name,value,enabled)
117
+ r << fe_int(name, attribs)
229
118
  when :datetime
230
- r << fe_datetime(name,value,enabled)
119
+ r << fe_datetime(name, attribs)
231
120
  when :date
232
- r << fe_date(name,value,enabled)
233
- when :time
234
- r << fe_time(name, value, enabled)
121
+ r << fe_date(name, attribs)
235
122
  when :password
236
- r << fe_password(name, enabled)
123
+ r << fe_password(name, attribs)
237
124
  end
238
125
  return r
239
126
  end
240
-
241
- # build a form element for column name
242
- def fe_for(column_name, hint='')
243
- col = @model.column_for_attribute(column_name)
244
- fe(col.type, col.name, @model.send(column_name.intern))
245
- end
246
-
247
127
 
248
- # Load column values into instance variables
249
- def setup
250
- @model.class.columns.each do |col|
251
- self.instance_variable_set("@#{col.name}", @model.send(col.name.intern))
252
- end
128
+ # build a form element for column name
129
+ def fe_for(column_name)
130
+ col = @model.column_for_attribute(column_name)
131
+ fe(col.type, col.name)
253
132
  end
254
133
 
255
- # you might want to replace this method
134
+ # you might want to replace this method
256
135
  public
257
136
  def form
258
- r = mabtext { FormErrors(@form_errors) }
259
- r << "<form action='#{@post_to}' method='POST'>\n"
260
- r << "<table>"
261
- @model.class.columns.each do |col|
262
- next if field_hidden?(col.name)
263
- k = col.name
264
- v = @model.send(k.intern) # this allows us to overwrite accessors
265
- r << "<tr><td>#{k}</td><td>#{fe(col.type,k,v)}</td></tr>"
137
+ mab do
138
+ FormErrors(@form_errors)
139
+ fe_formstart
140
+ table do
141
+ @model.class.columns.each do |col|
142
+ next if field_hidden?(col.name)
143
+ tr { td { col.name }; td { fe(col.type, col.name) } }
144
+ end
145
+ tr { td {} ; td { fe_submit }}
146
+ end
147
+ fe_formend
266
148
  end
267
- r << "<tr><td><br/></td></tr>"
268
- r << "<tr><td></td><td><input type='submit' value=#{@model.new_record? ? 'Save' : 'Update'} name='#{@submit_name}'></td></tr>"
269
- r << "</table>\n</form>\n"
270
- @_content = r
271
149
  end
272
-
273
-
274
150
  end
275
151