cuca 0.07 → 0.12

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +10 -0
  3. data/README.md +51 -0
  4. data/application_skeleton/app/test.rb +16 -0
  5. data/application_skeleton/conf/config.rb +8 -1
  6. data/application_skeleton/public/dispatch-fwdev.cgi +3 -1
  7. data/application_skeleton/public/dispatch.cgi +1 -1
  8. data/application_skeleton/public/dispatch.fcgi +1 -1
  9. data/application_skeleton/scripts/server-lighttpd-fcgi.rb +43 -4
  10. data/application_skeleton/scripts/server-lighttpd.rb +58 -5
  11. data/application_skeleton/scripts/server-webrick.rb +1 -1
  12. data/application_skeleton/tests/functional/basic.rb +32 -0
  13. data/bin/cuca +7 -3
  14. data/lib/cuca.rb +1 -1
  15. data/lib/cuca/app.rb +44 -31
  16. data/lib/cuca/cgi_emu.rb +17 -6
  17. data/lib/cuca/const.rb +1 -1
  18. data/lib/cuca/controller.rb +0 -2
  19. data/lib/cuca/generator/view.rb +7 -5
  20. data/lib/cuca/mimetypes.rb +2 -0
  21. data/lib/cuca/sessionpage.rb +18 -4
  22. data/lib/cuca/stdlib/README +2 -0
  23. data/lib/cuca/stdlib/form.rb +1 -1
  24. data/lib/cuca/stdlib/listwidget/dblist.rb +13 -2
  25. data/lib/cuca/stdlib/listwidget/querydef.rb +11 -17
  26. data/lib/cuca/test/helpers.rb +55 -2
  27. data/lib/cuca/urlmap.rb +18 -9
  28. data/tests/all.rb +4 -1
  29. data/tests/controller.rb +26 -9
  30. data/tests/test_app/app/test.rb +0 -0
  31. data/tests/urlmap.rb +7 -0
  32. data/tests/widget.rb +2 -2
  33. metadata +106 -128
  34. data/README +0 -43
  35. data/application_skeleton/public/dispatch.cgi-old +0 -18
  36. data/application_skeleton/public/dispatch.fcgi-old +0 -25
  37. data/application_skeleton/scripts/rack-test.rb +0 -52
  38. data/application_skeleton/scripts/server-rack-thin.rb +0 -40
  39. data/application_skeleton/scripts/server-rack-webrick.rb +0 -22
  40. data/lib/cuca/stdlib/old_arform.rb +0 -254
@@ -1,16 +1,23 @@
1
1
  # little fake cgi class that allows automated testing of cgi application
2
2
 
3
+ require 'stringio'
4
+
3
5
  class CGIEmu < CGI
4
6
 
5
7
  attr_reader :out_params
6
8
  attr_reader :out_content
9
+
10
+ attr_reader :output_cookies # made by CGI::Session
7
11
 
8
12
  class EnvTable
9
13
  def initialize(options)
10
14
  @test_path_info = options['PATH_INFO'] || '/'
11
15
  @test_query_params = options['QUERY_PARAMS'] || {}
16
+ @request_method = options['REQUEST_METHOD'] || 'GET'
17
+ @content_length = options['CONTENT_LENGTH'] || 0
18
+ @http_cookie = options['HTTP_COOKIE']
12
19
 
13
- the_env_table.each_pair { |k,v| ENV[k] = v }
20
+ the_env_table.each_pair { |k,v| ENV[k] = v.to_s }
14
21
 
15
22
  # ENV.merge(the_env_table)
16
23
  end
@@ -35,7 +42,6 @@ class CGIEmu < CGI
35
42
  "REMOTE_ADDR"=>"127.0.0.1",
36
43
  "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.8.6/2007-06-07)",
37
44
  "HTTP_REFERER"=>"http://localhost:2000/",
38
- "HTTP_COOKIE"=>"cuca_session=285715682bf030a19cb24b2560aabc36",
39
45
  "HTTP_ACCEPT_CHARSET"=>"utf-8, utf-8;q=0.5, *;q=0.5",
40
46
  "REQUEST_URI"=>"http://localhost:2000" + @test_path_info,
41
47
  "SERVER_PORT"=>"2000",
@@ -43,8 +49,12 @@ class CGIEmu < CGI
43
49
  "QUERY_STRING"=>query_string,
44
50
  "HTTP_ACCEPT"=>"text/html, image/jpeg, image/png, text/*, image/*, */*",
45
51
  "SCRIPT_FILENAME"=>"/home/bones/workspace/cuca/scripts/../public/dispatch.cgi",
46
- "REQUEST_METHOD"=>"GET",
47
- "HTTP_CONNECTION"=>"Keep-Alive"}
52
+ "REQUEST_METHOD"=>@request_method,
53
+ "HTTP_CONNECTION"=>"Keep-Alive",
54
+ "CONTENT_LENGTH" => @content_length,
55
+ "HTTP_COOKIE" => @http_cookie,
56
+ # 'CONTENT_TYPE' => "application/x-www-form-urlencoded"
57
+ }
48
58
  end
49
59
 
50
60
  def [](key)
@@ -58,11 +68,12 @@ class CGIEmu < CGI
58
68
  end
59
69
 
60
70
  def stdinput
61
- # $stderr.puts "Returning STDINPUT"
62
- StringIO.new("")
71
+ StringIO.new(@content)
63
72
  end
64
73
 
65
74
  def initialize(options)
75
+ @content = options['CONTENT'] || ''
76
+ options.delete('CONTENT')
66
77
  @test_options = options
67
78
  super()
68
79
  end
@@ -1,3 +1,3 @@
1
1
  module Cuca
2
- VERSION = '0.06'
2
+ VERSION = '0.11'
3
3
  end
@@ -330,8 +330,6 @@ class Controller < Widget
330
330
  self.send(method_name) if self.respond_to?(method_name.intern)
331
331
  rescue BreakControllerException => e
332
332
  handle_exception(e)
333
- rescue ApplicationException => e
334
- http_status "SERVER_ERROR"
335
333
  end
336
334
  end
337
335
 
@@ -44,15 +44,17 @@ module View
44
44
  # This will return the generated markup as a string
45
45
  def viewtext(filename=nil)
46
46
  view_dir = $cuca_path + '/' + App::config['view_directory']
47
+
47
48
  begin
48
- f = File.open(view_dir + "/#{filename}", 'r')
49
+ template = File.read(view_dir + "/#{filename}")
49
50
  rescue => e
50
- return "Error opening template: #{e}"
51
+ return "Error reading template: #{e}"
51
52
  end
52
-
53
- template = f.read
54
- f.close
55
53
 
54
+ if RUBY_VERSION > '1.8' && Cuca::App.config['view_encoding'] then
55
+ template.force_encoding(Cuca::App.config['view_encoding'])
56
+ end
57
+
56
58
  viewtext_p(template)
57
59
  end
58
60
 
@@ -1,5 +1,7 @@
1
1
  module Cuca
2
2
 
3
+ # This code whould take the data from /etc/mine.types, but not present on all systems
4
+ #
3
5
  #
4
6
  # MimeTypes is a self-writing hash.
5
7
  # Will take the data of the mime.types file
@@ -1,10 +1,19 @@
1
1
  module Cuca
2
2
 
3
3
  # A session page (access via session.page) is memory that is only valid
4
- # for the current action. Once you leave to another page it will
5
- # be erased. (see Cuca::Session)
4
+ # for the current action.
5
+ # Query and Request paramenters are automatically available in this container and
6
+ # remain available if removed due to a page refresh or similar.
7
+ #
8
+ # If you leave the page, they are NOT erased anymore (this used to be cuca behavior
9
+ # <= 0.7). They stay valid until EXPIRE_TIME_HOURS (default 4 hours).
10
+ #
11
+ # see Cuca::Session
6
12
  class SessionPage
7
13
 
14
+ LAKEY = :session_page_last_access
15
+ EXPIRE_TIME_HOURS = 4
16
+
8
17
  private
9
18
  def pagekey
10
19
  "Pk_#{$app.urlmap.script.gsub(/[\/\\]/, '_')}".intern
@@ -19,7 +28,8 @@ class SessionPage
19
28
  @ses = session
20
29
  @ses[:SessionPage] ||= {}
21
30
  pagemem[pagekey] ||= {}
22
- session.cgi.parameters.each_pair { |k,v| self[k] = v if v.kind_of?(String) }
31
+ pagemem[pagekey][LAKEY] = Time.now
32
+ session.cgi.parameters.each_pair { |k,v| self[k] = v.dup if v.kind_of?(String) }
23
33
  expire
24
34
  end
25
35
 
@@ -49,9 +59,13 @@ class SessionPage
49
59
  private
50
60
  def expire
51
61
  pagemem.each_pair do |k,v|
52
- pagemem.delete(k) if k != pagekey
62
+ next if k == pagekey
63
+ next unless pagemem[k][LAKEY]
64
+ next if pagemem[k][LAKEY] > (Time.now - (3600 * EXPIRE_TIME_HOURS))
65
+ pagemem.delete(k)
53
66
  end
54
67
  end
68
+
55
69
  end
56
70
 
57
71
  end
@@ -0,0 +1,2 @@
1
+ These are kind of example Widgets..
2
+
@@ -88,7 +88,7 @@ class FormWidget < Cuca::Widget
88
88
  # it will get the variables from the options[:default_values]
89
89
  def get_form_variables
90
90
  var = @options[:default_values] || {}
91
- params.each_pair { |k,v| var[k] = v } if posted? # request_method == 'POST'
91
+ params.each_pair { |k,v| var[k.to_s] = v } if posted? # request_method == 'POST'
92
92
  @variables = {}
93
93
  var.each_pair { |k,v| @variables[k.to_s] = v } # this allows is to pass symbols to default_values
94
94
  @variables
@@ -80,6 +80,7 @@ class DBListWidget < BaseList
80
80
  findstuff[:offset] = query_def.range.first
81
81
  findstuff[:limit] = query_def.range.last-query_def.range.first+1
82
82
  findstuff[:joins] = @joins || nil
83
+ findstuff[:group] = @group_by if @group_by.to_s != ''
83
84
  sel = @query_columns.collect do |c|
84
85
  ret = c.has_key?(:query) ? "#{c[:query]} as #{c[:id]}" : c[:id]
85
86
  ret = nil if c[:query] == false
@@ -88,9 +89,18 @@ class DBListWidget < BaseList
88
89
  findstuff[:select] = sel.compact.join(',')
89
90
  @data = @model_class.find(:all, findstuff)
90
91
  @additional_data = @data.dup
91
-
92
+
93
+ rowcount_findstuff = findstuff.dup
94
+ rowcount_findstuff.delete(:limit)
95
+ rowcount_findstuff.delete(:offset)
96
+ rowcount_findstuff.delete(:order)
97
+
92
98
  @data = normalize_result(@data)
93
- @total_rows= @model_class.count(:conditions => where_clause(query_def), :joins => @joins)
99
+
100
+ @total_rows= @model_class.count(rowcount_findstuff)
101
+ if @total_rows.kind_of?(Hash) then # if group_by is defined we'll get this
102
+ @total_rows = @total_rows.size
103
+ end
94
104
  end
95
105
 
96
106
  def setup
@@ -118,6 +128,7 @@ class DBListWidget < BaseList
118
128
  @columns = data_setup[:columns] || []
119
129
  @extra_conditions = data_setup[:conditons] || ""
120
130
  @joins = data_setup[:joins] || ""
131
+ @group_by = data_setup[:group_by] || ""
121
132
  @options ||= data_setup[:options] # can be used by 'setup'
122
133
  @model_class = model_class || nil
123
134
  setup
@@ -67,8 +67,8 @@ class QueryDef
67
67
  end
68
68
 
69
69
  #setter
70
- raise NoMethodError if met[met.size-1].chr != '='
71
- raise NoMethodError if params.size != 1
70
+ raise NoMethodError, met if met[met.size-1].chr != '='
71
+ raise NoMethodError, met if params.size != 1
72
72
  met = met[0..met.size-2] # cut '='
73
73
  if ATTRIBS.include?(met) then
74
74
  @data[met] = params[0]
@@ -79,9 +79,9 @@ class QueryDef
79
79
 
80
80
  # value enc/dec
81
81
  def ev_enc(name)
82
- # $stderr.puts "env_enc(#{name}) #{@data.inspect}"
82
+
83
83
  if ATTRIBS_ENCODE.has_key?(name) then
84
- ATTRIBS_ENCODE[name].call(@data[name] || '')
84
+ ATTRIBS_ENCODE[name].call(@data[name])
85
85
  else
86
86
  @data[name]
87
87
  end
@@ -89,7 +89,7 @@ class QueryDef
89
89
 
90
90
  def ev_dec(name, value)
91
91
  if ATTRIBS_DECODE.has_key?(name) then
92
- # $stderr.puts "ev_dec #{name} #{value}"
92
+ $stderr.puts "ev_dec #{name} #{value}"
93
93
  begin
94
94
  return ATTRIBS_DECODE[name].call(value)
95
95
  rescue
@@ -112,20 +112,20 @@ class QueryDef
112
112
 
113
113
  # returns an hash with the values and escaped names and attributes
114
114
  # attr and newval with swap a value for this return without changing the actual data
115
- def to_h(attr=nil, newval=nil, attr2=nil, newval2=nil)
116
- if attr then
115
+ def to_h(attr1=nil, newval1=nil, attr2=nil, newval2=nil)
116
+ if attr1 then
117
117
  @backup = @data.dup
118
- @data[attr] = newval
118
+ @data[attr1] = newval1
119
119
  @data[attr2] = newval2 unless attr2.nil?
120
120
  end
121
121
 
122
122
  u = {}
123
123
  ATTRIBS.each do |a|
124
- # $stderr.puts "Encoding: #{a} - #{@data[a]}"
124
+ # $stderr.puts "Encoding: #{a} - #{@data[a]}"
125
125
  u[at_enc(a)] = escape(ev_enc(a) || '')
126
126
  end
127
127
 
128
- if attr then @data = @backup end
128
+ if attr1 then @data = @backup end
129
129
 
130
130
  return u
131
131
  end
@@ -153,7 +153,7 @@ class QueryDef
153
153
  fname = p.scan(/\_filter\_(.*)/)[0][0]
154
154
  fdata = v
155
155
  @data['filters'][fname] = fdata
156
- # $stderr.puts "Filter update: #{@data['filters'].inspect}"
156
+ $stderr.puts "Filter update: #{@data['filters'].inspect}"
157
157
  end
158
158
  end
159
159
 
@@ -169,10 +169,4 @@ class QueryDef
169
169
  end
170
170
  end
171
171
 
172
- # qr = QueryDef.new("my_list")
173
- # qr.order_by = 'firstname'
174
- # qr.range = 100..200
175
- #
176
- # qr.filters = {'firstname' => 'martin', 'lastname' => 'boese' }
177
- # puts qr.to_url
178
172
 
@@ -1,5 +1,9 @@
1
1
 
2
2
  require 'test/unit'
3
+ require 'cuca/cgi_emu'
4
+ require 'cuca/urlmap'
5
+
6
+ require 'ostruct'
3
7
 
4
8
  module Cuca
5
9
  module Test
@@ -10,8 +14,6 @@ module Helpers
10
14
 
11
15
  # init the application. call this from the setup method.
12
16
  def init(app_path = '/', params = {})
13
- require 'cuca/cgi_emu'
14
- require 'cuca/urlmap'
15
17
  @cgi = CGIEmu.new({'PATH_INFO' => app_path, 'QUERY_PARAMS' => params})
16
18
  @app = Cuca::App.new
17
19
  @app.load_support_files(Cuca::URLMap.new($cuca_path+'/app', app_path))
@@ -39,6 +41,57 @@ def widget_p(widget_class, *args, &block)
39
41
  end
40
42
 
41
43
 
44
+ #
45
+ # Functional Tests
46
+ #
47
+ def cgi_status_to_text(status)
48
+ require 'cgi'
49
+ CGI::HTTP_STATUS[status] || status
50
+ end
51
+
52
+ def cgi_to_result(cgi, other = {})
53
+ op = cgi.out_params
54
+
55
+ result = OpenStruct.new
56
+ result.status = cgi_status_to_text(op['status'])
57
+ result.status_cgi = op['status']
58
+ result.type = op['type']
59
+ result.content = cgi.out_content
60
+ result.cookies = cgi.output_cookies
61
+ other.each { |k,v| result.send("#{k}=".intern, v) }
62
+ result
63
+ end
64
+
65
+
66
+ def measure_time
67
+ t = Time.now.to_f
68
+ yield
69
+ Time.now.to_f - t
70
+ end
71
+
72
+ def get(target, query_parameters= {})
73
+ @cgi = CGIEmu.new({'PATH_INFO' => target,
74
+ 'QUERY_PARAMS' => query_parameters,
75
+ 'HTTP_COOKIE' => @test_http_cookie})
76
+ @app = Cuca::App.new
77
+ t = measure_time { @app.cgicall(@cgi) }
78
+ cgi_to_result(@app.cgi, :time => t)
79
+ end
80
+
81
+ def post(target, request_parameters = {})
82
+ require 'uri'
83
+ body = URI.encode_www_form(request_parameters)
84
+ @cgi = CGIEmu.new({'PATH_INFO' => target,
85
+ 'REQUEST_METHOD' => 'POST',
86
+ 'CONTENT_LENGTH' => body.size,
87
+ 'CONTENT' => body,
88
+ 'HTTP_COOKIE' => @test_http_cookie})
89
+ @app = Cuca::App.new
90
+ t = measure_time { @app.cgicall(@cgi) }
91
+ cgi_to_result(@app.cgi, :time => t)
92
+ end
93
+
94
+
42
95
  end
43
96
  end
44
97
  end
@@ -1,7 +1,10 @@
1
1
 
2
2
  if __FILE__ == $0 then
3
3
  require 'rubygems'
4
- require 'cuca'
4
+ begin
5
+ require 'cuca'
6
+ rescue LoadError
7
+ end
5
8
  end
6
9
 
7
10
  module Cuca
@@ -115,7 +118,6 @@ class URLMap
115
118
  # it's real path
116
119
  private
117
120
  def scan_dir(base, file)
118
-
119
121
  striped = "#{base}/#{file}"[@base_path.length..-1]
120
122
  mount = Cuca::App.config['mount'] || {}
121
123
  # $stderr.puts "SCAN DIR: #{striped}"
@@ -133,7 +135,7 @@ class URLMap
133
135
  return file.empty? ? base : "#{base}/#{file}" # avoid returning double //
134
136
  end
135
137
 
136
- d = Dir["#{base}/#{DEF_ACT}*"].collect { |f| f.split('/').last }
138
+ d = Dir["#{base}/#{DEF_ACT}*"].map { |f| f.split('/').last }
137
139
 
138
140
  # puts "Directory not found, checking for default in #{base} - #{file}"
139
141
 
@@ -163,19 +165,26 @@ class URLMap
163
165
  end
164
166
 
165
167
 
168
+ # removes double slashes from a path-string
169
+ def clean_path(directory)
170
+ directory.gsub(/\/\//, '/')
171
+ end
172
+
166
173
  # scan will match an URI to a script and set assigns. (called from initialize)
167
174
  private
168
175
  def scan
169
176
  files = @path_info.split('/')
170
-
177
+
171
178
  files << '' if @path_info[@path_info.size-1].chr == '/' # add empty element if we point to a directory
172
179
 
173
180
  # files now contains something like:
174
- # [users, show, martin, contacts]
181
+ # ['', 'users', 'show', 'martin', 'contacts']
175
182
 
176
183
  # puts files.inspect
177
184
  real_path = @base_path.dup
178
185
 
186
+ @path_tree = [] if files.size > 1
187
+
179
188
  # scan directory
180
189
  files.each_index do |idx|
181
190
  next if idx >= (files.size-1) # skip last element
@@ -196,7 +205,7 @@ class URLMap
196
205
 
197
206
  raise RoutingError.new("Routing Error - script not found at #{real_path} - #{files.last}") if !r
198
207
 
199
- real_path = "#{real_path}/#{r}"
208
+ real_path = clean_path("#{real_path}/#{r}")
200
209
 
201
210
  @script = File.expand_path(real_path)
202
211
  # @path_tree = _tree(@base_path, @script)
@@ -251,14 +260,14 @@ class URLMap
251
260
 
252
261
  def initialize(base_path, path_info, default_actions = ['index'])
253
262
  @path_info = path_info
254
- @base_path = File.expand_path(base_path)
263
+ @base_path = clean_path(File.expand_path(base_path))
255
264
  @script = ''
256
265
  @subcall = nil
257
266
  @default_actions = default_actions
258
267
  @assigns = {}
259
268
  @action = ''
260
269
  @action_path = ''
261
- @path_tree = [base_path]
270
+ @path_tree = [@base_path]
262
271
  scan
263
272
  self
264
273
  end
@@ -273,7 +282,7 @@ end
273
282
  #
274
283
 
275
284
  if __FILE__ == $0 then
276
- require 'app'
285
+ require 'cuca/app'
277
286
 
278
287
  BASE = '/home/bones/src/cuca/app'
279
288
  URL = 'user/martin/somewhere/notexist/'