actionpack 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (100) hide show
  1. data/CHANGELOG +604 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +418 -0
  4. data/RUNNING_UNIT_TESTS +14 -0
  5. data/examples/.htaccess +24 -0
  6. data/examples/address_book/index.rhtml +33 -0
  7. data/examples/address_book/layout.rhtml +8 -0
  8. data/examples/address_book_controller.cgi +9 -0
  9. data/examples/address_book_controller.fcgi +6 -0
  10. data/examples/address_book_controller.rb +52 -0
  11. data/examples/address_book_controller.rbx +4 -0
  12. data/examples/benchmark.rb +52 -0
  13. data/examples/benchmark_with_ar.fcgi +89 -0
  14. data/examples/blog_controller.cgi +53 -0
  15. data/examples/debate/index.rhtml +14 -0
  16. data/examples/debate/new_topic.rhtml +22 -0
  17. data/examples/debate/topic.rhtml +32 -0
  18. data/examples/debate_controller.cgi +57 -0
  19. data/install.rb +93 -0
  20. data/lib/action_controller.rb +47 -0
  21. data/lib/action_controller/assertions/action_pack_assertions.rb +166 -0
  22. data/lib/action_controller/assertions/active_record_assertions.rb +65 -0
  23. data/lib/action_controller/base.rb +626 -0
  24. data/lib/action_controller/benchmarking.rb +49 -0
  25. data/lib/action_controller/cgi_ext/cgi_ext.rb +43 -0
  26. data/lib/action_controller/cgi_ext/cgi_methods.rb +91 -0
  27. data/lib/action_controller/cgi_process.rb +123 -0
  28. data/lib/action_controller/filters.rb +279 -0
  29. data/lib/action_controller/flash.rb +65 -0
  30. data/lib/action_controller/layout.rb +143 -0
  31. data/lib/action_controller/request.rb +92 -0
  32. data/lib/action_controller/rescue.rb +94 -0
  33. data/lib/action_controller/response.rb +15 -0
  34. data/lib/action_controller/scaffolding.rb +183 -0
  35. data/lib/action_controller/session/active_record_store.rb +72 -0
  36. data/lib/action_controller/session/drb_server.rb +9 -0
  37. data/lib/action_controller/session/drb_store.rb +31 -0
  38. data/lib/action_controller/support/class_attribute_accessors.rb +57 -0
  39. data/lib/action_controller/support/class_inheritable_attributes.rb +37 -0
  40. data/lib/action_controller/support/clean_logger.rb +10 -0
  41. data/lib/action_controller/support/cookie_performance_fix.rb +121 -0
  42. data/lib/action_controller/support/inflector.rb +70 -0
  43. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +28 -0
  44. data/lib/action_controller/templates/rescues/diagnostics.rhtml +22 -0
  45. data/lib/action_controller/templates/rescues/layout.rhtml +29 -0
  46. data/lib/action_controller/templates/rescues/missing_template.rhtml +2 -0
  47. data/lib/action_controller/templates/rescues/template_error.rhtml +26 -0
  48. data/lib/action_controller/templates/rescues/unknown_action.rhtml +2 -0
  49. data/lib/action_controller/templates/scaffolds/edit.rhtml +6 -0
  50. data/lib/action_controller/templates/scaffolds/layout.rhtml +29 -0
  51. data/lib/action_controller/templates/scaffolds/list.rhtml +24 -0
  52. data/lib/action_controller/templates/scaffolds/new.rhtml +5 -0
  53. data/lib/action_controller/templates/scaffolds/show.rhtml +9 -0
  54. data/lib/action_controller/test_process.rb +194 -0
  55. data/lib/action_controller/url_rewriter.rb +153 -0
  56. data/lib/action_view.rb +40 -0
  57. data/lib/action_view/base.rb +253 -0
  58. data/lib/action_view/helpers/active_record_helper.rb +171 -0
  59. data/lib/action_view/helpers/date_helper.rb +223 -0
  60. data/lib/action_view/helpers/debug_helper.rb +17 -0
  61. data/lib/action_view/helpers/form_helper.rb +176 -0
  62. data/lib/action_view/helpers/form_options_helper.rb +169 -0
  63. data/lib/action_view/helpers/tag_helper.rb +59 -0
  64. data/lib/action_view/helpers/text_helper.rb +129 -0
  65. data/lib/action_view/helpers/url_helper.rb +72 -0
  66. data/lib/action_view/partials.rb +61 -0
  67. data/lib/action_view/template_error.rb +84 -0
  68. data/lib/action_view/vendor/builder.rb +13 -0
  69. data/lib/action_view/vendor/builder/blankslate.rb +21 -0
  70. data/lib/action_view/vendor/builder/xmlbase.rb +143 -0
  71. data/lib/action_view/vendor/builder/xmlevents.rb +63 -0
  72. data/lib/action_view/vendor/builder/xmlmarkup.rb +288 -0
  73. data/rakefile +105 -0
  74. data/test/abstract_unit.rb +9 -0
  75. data/test/controller/action_pack_assertions_test.rb +295 -0
  76. data/test/controller/active_record_assertions_test.rb +118 -0
  77. data/test/controller/cgi_test.rb +142 -0
  78. data/test/controller/cookie_test.rb +38 -0
  79. data/test/controller/filters_test.rb +159 -0
  80. data/test/controller/flash_test.rb +69 -0
  81. data/test/controller/layout_test.rb +49 -0
  82. data/test/controller/redirect_test.rb +44 -0
  83. data/test/controller/render_test.rb +169 -0
  84. data/test/controller/url_test.rb +318 -0
  85. data/test/fixtures/layouts/builder.rxml +3 -0
  86. data/test/fixtures/layouts/standard.rhtml +1 -0
  87. data/test/fixtures/test/_customer.rhtml +1 -0
  88. data/test/fixtures/test/greeting.rhtml +1 -0
  89. data/test/fixtures/test/hello.rxml +4 -0
  90. data/test/fixtures/test/hello_world.rhtml +1 -0
  91. data/test/fixtures/test/hello_xml_world.rxml +11 -0
  92. data/test/fixtures/test/list.rhtml +1 -0
  93. data/test/template/active_record_helper_test.rb +76 -0
  94. data/test/template/date_helper_test.rb +103 -0
  95. data/test/template/form_helper_test.rb +115 -0
  96. data/test/template/form_options_helper_test.rb +174 -0
  97. data/test/template/tag_helper_test.rb +18 -0
  98. data/test/template/text_helper_test.rb +62 -0
  99. data/test/template/url_helper_test.rb +35 -0
  100. metadata +154 -0
@@ -0,0 +1,72 @@
1
+ begin
2
+
3
+ require 'active_record'
4
+ require 'cgi'
5
+ require 'cgi/session'
6
+
7
+ # Contributed by Tim Bates
8
+ class CGI
9
+ class Session
10
+ # ActiveRecord database based session storage class.
11
+ #
12
+ # Implements session storage in a database using the ActiveRecord ORM library. Assumes that the database
13
+ # has a table called +sessions+ with columns +id+ (numeric, primary key), +sessid+ and +data+ (text).
14
+ # The session data is stored in the +data+ column in YAML format; the user is responsible for ensuring that
15
+ # only data that can be YAMLized is stored in the session.
16
+ class ActiveRecordStore
17
+ # The ActiveRecord class which corresponds to the database table.
18
+ class Session < ActiveRecord::Base
19
+ serialize :data
20
+ # Isn't this class definition beautiful?
21
+ end
22
+
23
+ # Create a new ActiveRecordStore instance. This constructor is used internally by CGI::Session.
24
+ # The user does not generally need to call it directly.
25
+ #
26
+ # +session+ is the session for which this instance is being created.
27
+ #
28
+ # +option+ is currently ignored as no options are recognized.
29
+ #
30
+ # This session's ActiveRecord database row will be created if it does not exist, or opened if it does.
31
+ def initialize(session, option=nil)
32
+ @session = Session.find_first(["sessid = '%s'", session.session_id])
33
+ if @session
34
+ @data = @session.data
35
+ else
36
+ @session = Session.new("sessid" => session.session_id, "data" => {})
37
+ end
38
+ end
39
+
40
+ # Update and close the session's ActiveRecord object.
41
+ def close
42
+ return unless @session
43
+ update
44
+ @session = nil
45
+ end
46
+
47
+ # Close and destroy the session's ActiveRecord object.
48
+ def delete
49
+ return unless @session
50
+ @session.destroy
51
+ @session = nil
52
+ end
53
+
54
+ # Restore session state from the session's ActiveRecord object.
55
+ def restore
56
+ return unless @session
57
+ @data = @session.data
58
+ end
59
+
60
+ # Save session state in the session's ActiveRecord object.
61
+ def update
62
+ return unless @session
63
+ @session.data = @data
64
+ @session.save
65
+ end
66
+ end #ActiveRecordStore
67
+ end #Session
68
+ end #CGI
69
+
70
+ rescue LoadError
71
+ # Couldn't load Active Record, so don't make this store available
72
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # This is a really simple session storage daemon, basically just a hash,
4
+ # which is enabled for DRb access.
5
+
6
+ require 'drb'
7
+
8
+ DRb.start_service('druby://127.0.0.1:9192', Hash.new)
9
+ DRb.thread.join
@@ -0,0 +1,31 @@
1
+ require 'cgi'
2
+ require 'cgi/session'
3
+ require 'drb'
4
+
5
+ class CGI #:nodoc:all
6
+ class Session
7
+ class DRbStore
8
+ @@session_data = DRbObject.new(nil, 'druby://localhost:9192')
9
+
10
+ def initialize(session, option=nil)
11
+ @session_id = session.session_id
12
+ end
13
+
14
+ def restore
15
+ @h = @@session_data[@session_id] || {}
16
+ end
17
+
18
+ def update
19
+ @@session_data[@session_id] = @h
20
+ end
21
+
22
+ def close
23
+ update
24
+ end
25
+
26
+ def delete
27
+ @@session_data.delete(@session_id)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ # Extends the class object with class and instance accessors for class attributes,
2
+ # just like the native attr* accessors for instance attributes.
3
+ class Class # :nodoc:
4
+ def cattr_reader(*syms)
5
+ syms.each do |sym|
6
+ class_eval <<-EOS
7
+ if ! defined? @@#{sym.id2name}
8
+ @@#{sym.id2name} = nil
9
+ end
10
+
11
+ def self.#{sym.id2name}
12
+ @@#{sym}
13
+ end
14
+
15
+ def #{sym.id2name}
16
+ @@#{sym}
17
+ end
18
+
19
+ def call_#{sym.id2name}
20
+ case @@#{sym.id2name}
21
+ when Symbol then send(@@#{sym})
22
+ when Proc then @@#{sym}.call(self)
23
+ when String then @@#{sym}
24
+ else nil
25
+ end
26
+ end
27
+ EOS
28
+ end
29
+ end
30
+
31
+ def cattr_writer(*syms)
32
+ syms.each do |sym|
33
+ class_eval <<-EOS
34
+ if ! defined? @@#{sym.id2name}
35
+ @@#{sym.id2name} = nil
36
+ end
37
+
38
+ def self.#{sym.id2name}=(obj)
39
+ @@#{sym.id2name} = obj
40
+ end
41
+
42
+ def self.set_#{sym.id2name}(obj)
43
+ @@#{sym.id2name} = obj
44
+ end
45
+
46
+ def #{sym.id2name}=(obj)
47
+ @@#{sym} = obj
48
+ end
49
+ EOS
50
+ end
51
+ end
52
+
53
+ def cattr_accessor(*syms)
54
+ cattr_reader(*syms)
55
+ cattr_writer(*syms)
56
+ end
57
+ end
@@ -0,0 +1,37 @@
1
+ # Allows attributes to be shared within an inheritance hierarchy, but where each descentent gets a copy of
2
+ # their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
3
+ # to, for example, an array without those additions being shared with either their parent, siblings, or
4
+ # children, which is unlike the regular class-level attributes that are shared across the entire hierarchy.
5
+ module ClassInheritableAttributes # :nodoc:
6
+ def self.append_features(base)
7
+ super
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods # :nodoc:
12
+ @@classes ||= {}
13
+
14
+ def inheritable_attributes
15
+ @@classes[self] ||= {}
16
+ end
17
+
18
+ def write_inheritable_attribute(key, value)
19
+ inheritable_attributes[key] = value
20
+ end
21
+
22
+ def write_inheritable_array(key, elements)
23
+ write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
24
+ write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
25
+ end
26
+
27
+ def read_inheritable_attribute(key)
28
+ inheritable_attributes[key]
29
+ end
30
+
31
+ private
32
+ def inherited(child)
33
+ @@classes[child] = inheritable_attributes.dup
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ require 'logger'
2
+
3
+ class Logger #:nodoc:
4
+ private
5
+ remove_const "Format"
6
+ Format = "%s\n"
7
+ def format_message(severity, timestamp, msg, progname)
8
+ Format % [msg]
9
+ end
10
+ end
@@ -0,0 +1,121 @@
1
+ CGI.module_eval { remove_const "Cookie" }
2
+
3
+ class CGI #:nodoc:
4
+ # This is a cookie class that fixes the performance problems with the default one that ships with 1.8.1 and below.
5
+ # It replaces the inheritance on SimpleDelegator with DelegateClass(Array) following the suggestion from Matz on
6
+ # http://groups.google.com/groups?th=e3a4e68ba042f842&seekm=c3sioe%241qvm%241%40news.cybercity.dk#link14
7
+ class Cookie < DelegateClass(Array)
8
+ # Create a new CGI::Cookie object.
9
+ #
10
+ # The contents of the cookie can be specified as a +name+ and one
11
+ # or more +value+ arguments. Alternatively, the contents can
12
+ # be specified as a single hash argument. The possible keywords of
13
+ # this hash are as follows:
14
+ #
15
+ # name:: the name of the cookie. Required.
16
+ # value:: the cookie's value or list of values.
17
+ # path:: the path for which this cookie applies. Defaults to the
18
+ # base directory of the CGI script.
19
+ # domain:: the domain for which this cookie applies.
20
+ # expires:: the time at which this cookie expires, as a +Time+ object.
21
+ # secure:: whether this cookie is a secure cookie or not (default to
22
+ # false). Secure cookies are only transmitted to HTTPS
23
+ # servers.
24
+ #
25
+ # These keywords correspond to attributes of the cookie object.
26
+ def initialize(name = "", *value)
27
+ options = if name.kind_of?(String)
28
+ { "name" => name, "value" => value }
29
+ else
30
+ name
31
+ end
32
+ unless options.has_key?("name")
33
+ raise ArgumentError, "`name' required"
34
+ end
35
+
36
+ @name = options["name"]
37
+ @value = Array(options["value"])
38
+ # simple support for IE
39
+ if options["path"]
40
+ @path = options["path"]
41
+ else
42
+ %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
43
+ @path = ($1 or "")
44
+ end
45
+ @domain = options["domain"]
46
+ @expires = options["expires"]
47
+ @secure = options["secure"] == true ? true : false
48
+
49
+ super(@value)
50
+ end
51
+
52
+ def __setobj__(obj)
53
+ @_dc_obj = obj
54
+ end
55
+
56
+ attr_accessor("name", "value", "path", "domain", "expires")
57
+ attr_reader("secure")
58
+
59
+ # Set whether the Cookie is a secure cookie or not.
60
+ #
61
+ # +val+ must be a boolean.
62
+ def secure=(val)
63
+ @secure = val if val == true or val == false
64
+ @secure
65
+ end
66
+
67
+ # Convert the Cookie to its string representation.
68
+ def to_s
69
+ buf = ""
70
+ buf += @name + '='
71
+
72
+ if @value.kind_of?(String)
73
+ buf += CGI::escape(@value)
74
+ else
75
+ buf += @value.collect{|v| CGI::escape(v) }.join("&")
76
+ end
77
+
78
+ if @domain
79
+ buf += '; domain=' + @domain
80
+ end
81
+
82
+ if @path
83
+ buf += '; path=' + @path
84
+ end
85
+
86
+ if @expires
87
+ buf += '; expires=' + CGI::rfc1123_date(@expires)
88
+ end
89
+
90
+ if @secure == true
91
+ buf += '; secure'
92
+ end
93
+
94
+ buf
95
+ end
96
+
97
+ # Parse a raw cookie string into a hash of cookie-name=>Cookie
98
+ # pairs.
99
+ #
100
+ # cookies = CGI::Cookie::parse("raw_cookie_string")
101
+ # # { "name1" => cookie1, "name2" => cookie2, ... }
102
+ #
103
+ def self.parse(raw_cookie)
104
+ cookies = Hash.new([])
105
+ return cookies unless raw_cookie
106
+
107
+ raw_cookie.split(/; /).each do |pairs|
108
+ name, values = pairs.split('=',2)
109
+ next unless name and values
110
+ name = CGI::unescape(name)
111
+ values ||= ""
112
+ values = values.split('&').collect{|v| CGI::unescape(v) }
113
+ unless cookies.has_key?(name)
114
+ cookies[name] = new({ "name" => name, "value" => values })
115
+ end
116
+ end
117
+
118
+ cookies
119
+ end
120
+ end # class Cookie
121
+ end
@@ -0,0 +1,70 @@
1
+ # The Inflector transforms words from singular to plural, class names to table names, modulized class names to ones without,
2
+ # and class names to foreign keys.
3
+ module Inflector
4
+ extend self
5
+
6
+ def pluralize(word)
7
+ result = word.dup
8
+ plural_rules.each do |(rule, replacement)|
9
+ break if result.gsub!(rule, replacement)
10
+ end
11
+ return result
12
+ end
13
+
14
+ def singularize(word)
15
+ result = word.dup
16
+ singular_rules.each do |(rule, replacement)|
17
+ break if result.gsub!(rule, replacement)
18
+ end
19
+ return result
20
+ end
21
+
22
+ def camelize(lower_case_and_underscored_word)
23
+ lower_case_and_underscored_word.gsub(/(^|_)(.)/){$2.upcase}
24
+ end
25
+
26
+ def underscore(camel_cased_word)
27
+ camel_cased_word.gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
28
+ end
29
+
30
+ def demodulize(class_name_in_module)
31
+ class_name_in_module.gsub(/^.*::/, '')
32
+ end
33
+
34
+ def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
35
+ Inflector.underscore(Inflector.demodulize(class_name)) +
36
+ (separate_class_name_and_id_with_underscore ? "_id" : "id")
37
+ end
38
+
39
+ private
40
+ def plural_rules #:doc:
41
+ [
42
+ [/(x|ch|ss)$/, '\1es'], # search, switch, fix, box, process, address
43
+ [/([^aeiouy]|qu)y$/, '\1ies'], # query, ability, agency
44
+ [/(?:([^f])fe|([lr])f)$/, '\1\2ves'], # half, safe, wife
45
+ [/sis$/, 'ses'], # basis, diagnosis
46
+ [/([ti])um$/, '\1a'], # datum, medium
47
+ [/person$/, 'people'], # person, salesperson
48
+ [/man$/, 'men'], # man, woman, spokesman
49
+ [/child$/, 'children'], # child
50
+ [/s$/, 's'], # no change (compatibility)
51
+ [/$/, 's']
52
+ ]
53
+ end
54
+
55
+ def singular_rules #:doc:
56
+ [
57
+ [/(x|ch|ss)es$/, '\1'],
58
+ [/([^aeiouy]|qu)ies$/, '\1y'],
59
+ [/([lr])ves$/, '\1f'],
60
+ [/([^f])ves$/, '\1fe'],
61
+ [/(analy|ba|diagno|parenthe|progno|synop|the)ses$/, '\1sis'],
62
+ [/([ti])a$/, '\1um'],
63
+ [/people$/, 'person'],
64
+ [/men$/, 'man'],
65
+ [/status$/, 'status'],
66
+ [/children$/, 'child'],
67
+ [/s$/, '']
68
+ ]
69
+ end
70
+ end
@@ -0,0 +1,28 @@
1
+ <%
2
+ base_dir = File.expand_path(File.dirname(__FILE__))
3
+
4
+ request_parameters_without_action = @request.parameters.clone
5
+ request_parameters_without_action.delete("action")
6
+ request_parameters_without_action.delete("controller")
7
+
8
+ request_dump = request_parameters_without_action.inspect.gsub(/,/, ",\n")
9
+ session_dump = @request.session.instance_variable_get("@data").inspect.gsub(/,/, ",\n")
10
+ response_dump = @response.inspect.gsub(/,/, ",\n")
11
+
12
+ template_assigns = @response.template.instance_variable_get("@assigns")
13
+ %w( response exception template session request template_root template_class url ignore_missing_templates logger cookies headers params ).each { |t| template_assigns.delete(t) }
14
+ template_dump = template_assigns.inspect.gsub(/,/, ",\n")
15
+ %>
16
+
17
+ <h2 style="margin-top: 30px">Request</h2>
18
+ <p><b>Parameters</b>: <%=h request_dump == "{}" ? "None" : request_dump %></p>
19
+
20
+ <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
21
+ <div id="session_dump" style="display:none"><%= debug(@request.session.instance_variable_get("@data")) %></div>
22
+
23
+
24
+ <h2 style="margin-top: 30px">Response</h2>
25
+ <b>Headers</b>: <%=h @response.headers.inspect.gsub(/,/, ",\n") %><br/>
26
+
27
+ <p><a href="#" onclick="document.getElementById('template_dump').style.display='block'; return false;">Show template parameters</a></p>
28
+ <div id="template_dump" style="display:none"><%= debug(template_assigns) %></div>
@@ -0,0 +1,22 @@
1
+ <%
2
+ base_dir = File.expand_path(File.dirname(__FILE__))
3
+
4
+ clean_backtrace = @exception.backtrace.collect { |line| line.gsub(base_dir, "").gsub("/../config/environments/../../", "") }
5
+ app_trace = clean_backtrace.reject { |line| line[0..6] == "vendor/" || line.include?("dispatch.cgi") }
6
+ framework_trace = clean_backtrace - app_trace
7
+ %>
8
+
9
+ <h1>
10
+ <%=h @exception.class.to_s %> in
11
+ <%=h @request.parameters["controller"].capitalize %>#<%=h @request.parameters["action"] %>
12
+ </h1>
13
+ <p><%=h @exception.message %></p>
14
+
15
+ <% unless app_trace.empty? %><pre><code><%=h app_trace.collect { |line| line.gsub("/../", "") }.join("\n") %></code></pre><% end %>
16
+
17
+ <% unless framework_trace.empty? %>
18
+ <a href="#" onclick="document.getElementById('framework_trace').style.display='block'; return false;">Show framework trace</a>
19
+ <pre id="framework_trace" style="display:none"><code><%=h framework_trace.join("\n") %></code></pre>
20
+ <% end %>
21
+
22
+ <%= render_file(@rescues_path + "/_request_and_response.rhtml", false) %>