cuca 0.05 → 0.06

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