cuca 0.07 → 0.12

Sign up to get free protection for your applications and to get access to all the features.
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/'