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,42 @@
1
+ # A good context for generators. It will find and run widgets on method_missing.
2
+ #
3
+ class GeneratorContext
4
+ def get_bindings
5
+ binding
6
+ end
7
+
8
+ def initialize(assigns, base_object)
9
+ assigns.each_pair do |k,v|
10
+ instance_variable_set("@#{k}", v)
11
+ end
12
+ @base = base_object
13
+ end
14
+
15
+ def method_missing(sym, *args, &block )
16
+ class_name = sym.id2name
17
+
18
+
19
+ # 1st try to find method in the base widget
20
+ if @base.respond_to?(class_name.intern) then
21
+ return @base.send(class_name, *args, &block)
22
+ end
23
+ c = nil
24
+ # 2nd try to find a widget
25
+ if Object.const_defined?(class_name+'Widget') then
26
+ c = Object::const_get(class_name+'Widget')
27
+ else
28
+ # ...try to find in action namespace
29
+ mod = $app.urlmap.action_module
30
+ c = mod.const_get(class_name+'Widget') if mod.const_defined?(class_name+'Widget')
31
+ end
32
+
33
+ raise NameError.new "Undefined method: #{class_name}" unless c
34
+
35
+ widget = c.new({:args => args,
36
+ :assigns => @assigns },
37
+ &block)
38
+
39
+ return widget.to_s
40
+ end
41
+ end
42
+
data/lib/cuca/layout.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Cuca
2
2
 
3
- # === Layout
3
+ # = Layout
4
4
  # A Layout just behaves as a normal widget plus it will give you the @content_for_layout
5
5
  # instance variable with the content generated from the controller.
6
6
  #
@@ -0,0 +1,171 @@
1
+
2
+ require 'cgi/session'
3
+ require 'cgi/session/pstore'
4
+
5
+ require 'cuca/app'
6
+ require 'cuca/widget'
7
+ require 'cuca/sessionflash'
8
+ require 'cuca/sessionpage'
9
+
10
+ module Cuca
11
+
12
+
13
+ # == Description
14
+ # Session can be used to store stateful data. It is not loaded by default, to make
15
+ # use of it you must require this module and tell a controller to use a session.
16
+ #
17
+ # == Example Use (permanent data)
18
+ #
19
+ # Initialize (using a controller):
20
+ #
21
+ # class ApplicationController < Cuca::Controller
22
+ # use_session
23
+ # end
24
+ #
25
+ # Save and Load data (from any widgets e.g. controller):
26
+ #
27
+ # class IndexController < ApplicationController
28
+ #
29
+ # def run
30
+ # session[:stuff_to_remember] = "hello world"
31
+ # @stuff = session[:stuff_to_remember]
32
+ # end
33
+ # end
34
+ #
35
+ # == Flash Memory
36
+ #
37
+ # The flash memory can be used to store temporarily data for the current and
38
+ # next action. A typical example are messages after a post event, like:
39
+ #
40
+ # class LoginController < ApplicationController
41
+ # (...)
42
+ # def post
43
+ # if (params['username'] == 'root' && params['pass'] == 'stuff') then
44
+ # session.flash[:message] = "You are logged in"
45
+ # session[:username] = 'root'
46
+ # stop :redirect => 'index'
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # If you want to keep the flash memory for another cycle you can call:
52
+ #
53
+ # session.flash.keep
54
+ #
55
+ #
56
+ # == Page Memory
57
+ #
58
+ # Page memory is a container to store data only valid for the current action.
59
+ # It will be erased once you leave to a different action.
60
+ # Current request and query parameters (get/post) are automatically available in this
61
+ # container.
62
+ #
63
+ #
64
+ #
65
+ # == Configuration
66
+ #
67
+ # Session is using some values from App::Config :
68
+ #
69
+ # 'session_prefix'
70
+ # 'session_valid'
71
+ # 'session_key'
72
+ #
73
+ class Session
74
+ attr_reader :flash
75
+ attr_reader :page
76
+ attr_reader :cgi
77
+
78
+ private
79
+ def make_session
80
+ @sess = CGI::Session.new(@cgi, @session_parameters)
81
+ end
82
+
83
+ public
84
+ def reset
85
+ begin
86
+ @sess.delete
87
+ rescue
88
+ end
89
+ make_session
90
+ end
91
+
92
+
93
+ # returns true/false if a session exists
94
+ def exists?
95
+ begin
96
+ p = @session_parameters.clone
97
+ p['new_session'] = false
98
+ session = CGI::Session.new(cgi, p)
99
+ rescue ArgumentError
100
+ return false
101
+ end
102
+ return true
103
+ end
104
+
105
+ def initialize(cgi)
106
+ @cgi = cgi
107
+
108
+ @session_parameters = {
109
+ 'database_manager' => CGI::Session::PStore,
110
+ 'session_key' => App::config["session_key"],
111
+ 'session_path' => '/',
112
+ # 'new_session' => false,
113
+ 'session_expires' => Time.now + App::config["session_valid"].to_i,
114
+ 'prefix' => App::config["session_prefix"] }
115
+
116
+ make_session
117
+
118
+
119
+ @flash = SessionFlash.new(self)
120
+ @page = SessionPage.new(self)
121
+ end
122
+
123
+ def []=(key, value)
124
+ @sess[key] = value
125
+ end
126
+
127
+ def [](key)
128
+ return @sess[key]
129
+ end
130
+
131
+ def close
132
+ @sess.close
133
+ end
134
+
135
+ def update
136
+ @sess.update
137
+ end
138
+
139
+ def delete
140
+ @sess.delete
141
+ end
142
+
143
+
144
+ end
145
+
146
+ class Controller
147
+ # This will create filters that initialize the session before the action and
148
+ # close it afterwards.
149
+ def self.use_session
150
+ priority_before_filter('ses_initialize_session')
151
+ priority_after_filter('ses_close_session')
152
+ end
153
+
154
+ def ses_initialize_session
155
+ $session = Session.new($app.cgi)
156
+ end
157
+ def ses_close_session
158
+ $session.close
159
+ # $session = nil
160
+ end
161
+ end
162
+
163
+
164
+ class Widget
165
+ def session
166
+ $session
167
+ end
168
+ end
169
+
170
+ end
171
+
@@ -4,6 +4,7 @@ module Cuca
4
4
  # for the current action. Once you leave to another page it will
5
5
  # be erased. (see Cuca::Session)
6
6
  class SessionPage
7
+
7
8
  private
8
9
  def pagekey
9
10
  "Pk_#{$app.urlmap.script.gsub(/[\/\\]/, '_')}".intern
@@ -23,18 +24,28 @@ class SessionPage
23
24
  end
24
25
 
25
26
  def [](key)
26
- pagemem[pagekey][key] ? pagemem[pagekey][key] : nil
27
+ pagemem[pagekey][key]
27
28
  end
28
29
 
29
30
  def []=(key,value)
30
31
  pagemem[pagekey][key] = value
31
32
  end
32
-
33
+
33
34
  # remove a variable from page memory
34
35
  def delete(key)
35
36
  pagemem[pagekey].delete(key)
36
37
  end
37
38
 
39
+ # delete all from current page memory
40
+ def reset
41
+ pagemem[pagekey] = {}
42
+ end
43
+
44
+ # access to pagemem
45
+ def memory
46
+ pagemem[pagekey]
47
+ end
48
+
38
49
  private
39
50
  def expire
40
51
  pagemem.each_pair do |k,v|
@@ -28,6 +28,8 @@ class ARFormWidget < FormWidget
28
28
  # switch off fields on new records
29
29
  # * :diabled_on_update => ['field_name_1', 'field_name_2', ..]
30
30
  # switch off fields on existing records
31
+ # * :save_attribs => ['attr1', 'attr2']
32
+ # allow to call a setter even if it's not a db column
31
33
  # * .. options from FormWidgets ...
32
34
  def output(form_name, model, options = {})
33
35
  @model = model
@@ -35,8 +37,16 @@ class ARFormWidget < FormWidget
35
37
  @disabled_on_create = options[:disabled_on_create] || []
36
38
  @hidden_on_update = options[:hidden_on_update] || []
37
39
  @hidden_on_create = options[:hidden_on_create] || []
40
+ @save_attribs = options[:save_attribs] || []
41
+
42
+
43
+ setup if self.respond_to?(:setup) # you might want to use a method for setup
44
+
38
45
 
39
46
  options[:default_values] = model.attributes.merge(options[:default_values] || {})
47
+ @save_attribs.each do |sa|
48
+ options[:default_values][sa] = model.send(sa.intern) if model.respond_to?(sa.intern)
49
+ end
40
50
 
41
51
  super(form_name, options)
42
52
  end
@@ -73,8 +83,15 @@ class ARFormWidget < FormWidget
73
83
  @password_fields.each do |pwf|
74
84
  p.delete(pwf) if p[pwf].chomp.empty?
75
85
  end
76
-
77
- @model.attributes = p
86
+
87
+ # remove possible additional data that model doesn't support to
88
+ # p.delete_if { |k,v| !@mode.respond_to?("#{k}=") }
89
+
90
+ column_names = @model.class.columns.map { |c| c.name }
91
+ p.each do |k,v|
92
+ @model.send("#{k}=".intern, v) if (column_names.include?(k) && @model.respond_to?("#{k}=")) || @save_attribs.include?(k)
93
+ end
94
+ # @model.attributes = p
78
95
 
79
96
  return true if @model.valid?
80
97
 
@@ -71,7 +71,6 @@
71
71
  # end
72
72
  # end
73
73
  #
74
-
75
74
  class FormWidget < Cuca::Widget
76
75
 
77
76
  # Returns true if this form was posted
@@ -89,7 +88,7 @@ class FormWidget < Cuca::Widget
89
88
  # it will get the variables from the options[:default_values]
90
89
  def get_form_variables
91
90
  var = @options[:default_values] || {}
92
- params.each_pair { |k,v| var[k] = v } if posted?
91
+ params.each_pair { |k,v| var[k] = v } if posted? # request_method == 'POST'
93
92
  @variables = {}
94
93
  var.each_pair { |k,v| @variables[k.to_s] = v } # this allows is to pass symbols to default_values
95
94
  @variables
@@ -167,5 +166,9 @@ class FormWidget < Cuca::Widget
167
166
  form
168
167
  end
169
168
  end
169
+
170
+ def self.run(*args)
171
+ w = self.class.new
172
+ w.output(args)
173
+ end
170
174
  end
171
-
@@ -33,7 +33,6 @@ module FormElements
33
33
  fe_text(name, attribs)
34
34
  end
35
35
 
36
-
37
36
  def fe_textarea(name, attribs = {})
38
37
  a = { :rows => 5, :cols => 50 }.merge(attribs)
39
38
  "<textarea name='#{name}' #{a2p(a)}>#{get_value(name, attribs)}</textarea>\n"
@@ -41,7 +40,8 @@ module FormElements
41
40
 
42
41
  # build a form start tag
43
42
  def fe_formstart(attribs = {})
44
- a = {:name=>@form_name, :method=>'post', :action=>@post_to }.merge(attribs)
43
+ form_name = attribs[:form_name] || @form_name
44
+ a = {:name=>form_name, :method=>'post', :action=>@post_to }.merge(attribs)
45
45
  "<form #{a2p(a)}>\n"
46
46
  end
47
47
 
@@ -51,21 +51,46 @@ module FormElements
51
51
  attribs.delete(:showvalue)
52
52
  "<input type='password' name='#{name}' value='#{v}' #{a2p(attribs)}>\n"
53
53
  end
54
-
54
+
55
+ # :default_value is 'on' or 'off' for checkboxes
56
+ def fe_checkbox(name, attribs = {})
57
+ checker_name = "#{name}_checker"
58
+ newval = !!(request_parameters[name] || query_parameters[name])
59
+ posted = (request_parameters[checker_name] || query_parameters[checker_name])
60
+
61
+ # checkbox html element isn't sent to cgi if not selected
62
+ # so cuca's page variables won't work - this fixes it for most cases.
63
+ if (session.page && (posted && !newval)) then
64
+ session.page[name] = 'off'
65
+ end
66
+
67
+ checked = v[name] && (v[name].to_s != 'off')
68
+ checked = attribs[:default_value] == 'on' if (!posted and !v[name])
69
+
70
+ checkedval = checked ? ' CHECKED' : ''
71
+ "<input type='checkbox' name='#{name}' #{a2p(attribs)}#{checkedval}>"+
72
+ "<input type='hidden' name='#{checker_name}' value='1'>\n" # Needed to detect if element was submitted
73
+ end
74
+
75
+
55
76
 
56
77
  # this is to build a select box, example:
57
78
  # fe_select('gender', [['f', 'female'],['m','Male']]) or
79
+ # ...with options parameters...:
80
+ # fe_select('gender', [['f', 'female', { :id => 'f'} ],['m','Male', {:id=>'m'}]])
81
+ # ...simple...:
58
82
  # fe_select('gender', ['f','m'])
59
83
  def fe_select(name, options, attribs = {})
60
84
  r = "<select name='#{name}' #{a2p(attribs)}>\n"
61
85
  options.each do |o|
62
86
  ov = o.instance_of?(Array) ? o[0] : o
87
+ params = o[2].inject('') { |m,(k,v)| m << " #{k}='#{v}'" } rescue ''
63
88
  sel = ''
64
- sel = ' selected' if v[name] == ov
89
+ sel = ' selected' if get_value(name, attribs).to_s == ov.to_s
65
90
  if o.instance_of?(Array) then
66
- r+="<option value='#{o[0]}'#{sel}>#{o[1]}</option>\n"
91
+ r+="<option value='#{o[0]}'#{sel}#{params}>#{o[1]}</option>\n"
67
92
  else
68
- r+="<option value='#{o}'#{sel}>#{o}</option>\n"
93
+ r+="<option value='#{o}'#{sel}#{params}>#{o}</option>\n"
69
94
  end
70
95
  end
71
96
  r+="</select>\n"
@@ -86,8 +111,8 @@ end
86
111
 
87
112
 
88
113
  r << "\n<select name='#{name}' #{a2p(attribs)}>\n"
89
- r << "<option #{"selected" if v[name] == trueval} value='#{trueval}'>#{truename}</option>\n"
90
- r << "<option #{"selected" if v[name] == falseval} value='#{falseval}'>#{falsename}</option>\n"
114
+ r << "<option #{"selected" if get_value(name, attribs) == trueval} value='#{trueval}'>#{truename}</option>\n"
115
+ r << "<option #{"selected" if get_value(name, attribs) == falseval} value='#{falseval}'>#{falsename}</option>\n"
91
116
  r << "</select>\n"
92
117
  end
93
118
 
@@ -0,0 +1,157 @@
1
+ # mixin this module to build form elements
2
+
3
+ module Cuca
4
+ module FormElements
5
+
6
+ private
7
+ def a2p(attribs)
8
+ a = attribs.dup
9
+ a.delete(:default_value) # reserved attribute
10
+ a.inject([]) { |m,e| m << ((e[1].to_s != '') ? "#{e[0].to_s}='#{e[1].to_s}'" : e[0].to_s) }.join(' ')
11
+ end
12
+
13
+ private
14
+ def twodig(n)
15
+ n.to_s.length == 1 ? "0"+n.to_s : n.to_s
16
+ end
17
+
18
+ # this allows to pass a :default_value to the set of attribs
19
+ private
20
+ def get_value(name, attribs)
21
+ v[name] || (attribs[:default_value] || '')
22
+ end
23
+
24
+ def fe_text(name, attribs = {})
25
+ "<input type='text' name='#{name}' value='#{get_value(name, attribs)}' #{a2p(attribs)}>\n"
26
+ end
27
+
28
+ def fe_hidden(name, attribs = {})
29
+ "<input type='hidden' name='#{name}' value='#{get_value(name, attribs)}' #{a2p(attribs)}>\n"
30
+ end
31
+
32
+ def fe_int(name, attribs = {})
33
+ fe_text(name, attribs)
34
+ end
35
+
36
+ def fe_textarea(name, attribs = {})
37
+ a = { :rows => 5, :cols => 50 }.merge(attribs)
38
+ "<textarea name='#{name}' #{a2p(a)}>#{get_value(name, attribs)}</textarea>\n"
39
+ end
40
+
41
+ # build a form start tag
42
+ def fe_formstart(attribs = {})
43
+ form_name = attribs[:form_name] || @form_name
44
+ a = {:name=>form_name, :method=>'post', :action=>@post_to }.merge(attribs)
45
+ "<form #{a2p(a)}>\n"
46
+ end
47
+
48
+ # fe password doesn't show passwords content but default (unless :showvalue defined in attribs)
49
+ def fe_password(name, attribs = {})
50
+ v = attribs[:showvalue] ? get_value(name, attribs) : ''
51
+ attribs.delete(:showvalue)
52
+ "<input type='password' name='#{name}' value='#{v}' #{a2p(attribs)}>\n"
53
+ end
54
+
55
+ def fe_checkbox(name, attribs = {})
56
+ checker_name = "#{name}_checker"
57
+ newval = !!(request_parameters[name] || query_parameters[name])
58
+ posted = (request_parameters[checker_name] || query_parameters[checker_name])
59
+
60
+ # checkbox html element isn't sent to cgi if not selected
61
+ # so cuca's page variables won't work - this fixes it.
62
+ app.logger.info "TEST #{name} #{posted.inspect} #{newval.inspect}"
63
+
64
+ if (session.page && (posted && !newval)) then
65
+ app.logger.info "Setting OFF #{name} #{posted.inspect} #{newval.inspect}"
66
+ session.page[name] = 'off'
67
+ end
68
+
69
+ app.logger.info "Checkbox #{name} - #{get_value(name, attribs)}"
70
+ checked = v[name] != 'off'
71
+
72
+ checkedval = checked ? ' CHECKED' : ''
73
+ "<input type='checkbox' name='#{name}' #{a2p(attribs)}#{checkedval}>\n"
74
+ end
75
+
76
+
77
+
78
+ # this is to build a select box, example:
79
+ # fe_select('gender', [['f', 'female'],['m','Male']]) or
80
+ # fe_select('gender', ['f','m'])
81
+ def fe_select(name, options, attribs = {})
82
+ r = "<select name='#{name}' #{a2p(attribs)}>\n"
83
+ options.each do |o|
84
+ ov = o.instance_of?(Array) ? o[0] : o
85
+ sel = ''
86
+ sel = ' selected' if get_value(name, attribs).to_s == ov.to_s
87
+ if o.instance_of?(Array) then
88
+ r+="<option value='#{o[0]}'#{sel}>#{o[1]}</option>\n"
89
+ else
90
+ r+="<option value='#{o}'#{sel}>#{o}</option>\n"
91
+ end
92
+ end
93
+ r+="</select>\n"
94
+ end
95
+
96
+ def fe_bool(name,attribs = {})
97
+ r = ''
98
+ attribs = attribs.dup
99
+ truename = attribs[:true] || 'true'
100
+ falsename = attribs[:false] || 'false'
101
+ trueval = attribs.has_key?(:trueval) ? attribs[:trueval] : 't'
102
+ falseval = attribs.has_key?(:falseval) ? attribs[:falseval] : 'f'
103
+ $stderr.puts "#{name}: #{v[name].inspect} #{trueval.inspect} #{falseval.inspect}"
104
+ attribs.delete(:true)
105
+ attribs.delete(:false)
106
+ attribs.delete(:trueval)
107
+ attribs.delete(:falseval)
108
+
109
+
110
+ r << "\n<select name='#{name}' #{a2p(attribs)}>\n"
111
+ r << "<option #{"selected" if get_value(name, attribs) == trueval} value='#{trueval}'>#{truename}</option>\n"
112
+ r << "<option #{"selected" if get_value(name, attribs) == falseval} value='#{falseval}'>#{falsename}</option>\n"
113
+ r << "</select>\n"
114
+ end
115
+
116
+
117
+ def fe_submit(attribs = {})
118
+ a = { :value => 'Submit', :name=>@submit_name }.merge(attribs)
119
+ "<input type='submit' #{a2p(a)}>\n"
120
+ end
121
+
122
+
123
+ def fe_formend
124
+ "</form>\n"
125
+ end
126
+
127
+
128
+ def fe_datetime(name, attribs = {})
129
+ require 'date'
130
+ begin
131
+ val = v[name].instance_of?(Time) ? v[name] : DateTime.parse(v[name] || 'now')
132
+ rescue ArgumentError
133
+ val = DateTime.now
134
+ end
135
+ value = "#{val.year}/#{twodig(val.month)}/#{twodig(val.day)} #{val.hour}:#{val.min}"
136
+ "<input type='text' name='#{name}' value='#{value}' #{a2p(attribs)}>\n"
137
+ end
138
+
139
+ def fe_date(name, attribs = {})
140
+ require 'date'
141
+ if v[name].instance_of?(Date) then
142
+ value = "#{v[name].year}/#{twodig(v[name].month)}/#{twodig(v[name].day)}"
143
+ end
144
+ if v[name].nil? || (v[name].instance_of?(String) && v[name].empty?) then
145
+ value = ''
146
+ end
147
+ if value.nil? then
148
+ val = Date.today
149
+ value = "#{val.year}/#{twodig(val.month)}/#{twodig(val.day)}"
150
+ end
151
+
152
+ "<input type='text' name='#{name}' value='#{value}' #{a2p(attribs)}> (yyyy/mm/dd)\n"
153
+ end
154
+
155
+
156
+ end
157
+ end