halorgium-actionpack 3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. data/CHANGELOG +5179 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/lib/abstract_controller.rb +16 -0
  5. data/lib/abstract_controller/base.rb +158 -0
  6. data/lib/abstract_controller/callbacks.rb +113 -0
  7. data/lib/abstract_controller/exceptions.rb +12 -0
  8. data/lib/abstract_controller/helpers.rb +151 -0
  9. data/lib/abstract_controller/layouts.rb +250 -0
  10. data/lib/abstract_controller/localized_cache.rb +49 -0
  11. data/lib/abstract_controller/logger.rb +61 -0
  12. data/lib/abstract_controller/rendering_controller.rb +188 -0
  13. data/lib/action_controller.rb +72 -0
  14. data/lib/action_controller/base.rb +168 -0
  15. data/lib/action_controller/caching.rb +80 -0
  16. data/lib/action_controller/caching/actions.rb +163 -0
  17. data/lib/action_controller/caching/fragments.rb +116 -0
  18. data/lib/action_controller/caching/pages.rb +154 -0
  19. data/lib/action_controller/caching/sweeping.rb +97 -0
  20. data/lib/action_controller/deprecated.rb +4 -0
  21. data/lib/action_controller/deprecated/integration_test.rb +2 -0
  22. data/lib/action_controller/deprecated/performance_test.rb +1 -0
  23. data/lib/action_controller/dispatch/dispatcher.rb +57 -0
  24. data/lib/action_controller/metal.rb +129 -0
  25. data/lib/action_controller/metal/benchmarking.rb +73 -0
  26. data/lib/action_controller/metal/compatibility.rb +145 -0
  27. data/lib/action_controller/metal/conditional_get.rb +86 -0
  28. data/lib/action_controller/metal/configuration.rb +28 -0
  29. data/lib/action_controller/metal/cookies.rb +105 -0
  30. data/lib/action_controller/metal/exceptions.rb +55 -0
  31. data/lib/action_controller/metal/filter_parameter_logging.rb +77 -0
  32. data/lib/action_controller/metal/flash.rb +162 -0
  33. data/lib/action_controller/metal/head.rb +27 -0
  34. data/lib/action_controller/metal/helpers.rb +115 -0
  35. data/lib/action_controller/metal/hide_actions.rb +47 -0
  36. data/lib/action_controller/metal/http_authentication.rb +312 -0
  37. data/lib/action_controller/metal/layouts.rb +171 -0
  38. data/lib/action_controller/metal/mime_responds.rb +317 -0
  39. data/lib/action_controller/metal/rack_convenience.rb +27 -0
  40. data/lib/action_controller/metal/redirector.rb +22 -0
  41. data/lib/action_controller/metal/render_options.rb +103 -0
  42. data/lib/action_controller/metal/rendering_controller.rb +57 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +108 -0
  44. data/lib/action_controller/metal/rescuable.rb +13 -0
  45. data/lib/action_controller/metal/responder.rb +200 -0
  46. data/lib/action_controller/metal/session.rb +15 -0
  47. data/lib/action_controller/metal/session_management.rb +45 -0
  48. data/lib/action_controller/metal/streaming.rb +188 -0
  49. data/lib/action_controller/metal/testing.rb +39 -0
  50. data/lib/action_controller/metal/url_for.rb +41 -0
  51. data/lib/action_controller/metal/verification.rb +130 -0
  52. data/lib/action_controller/middleware.rb +38 -0
  53. data/lib/action_controller/notifications.rb +10 -0
  54. data/lib/action_controller/polymorphic_routes.rb +183 -0
  55. data/lib/action_controller/record_identifier.rb +91 -0
  56. data/lib/action_controller/testing/process.rb +111 -0
  57. data/lib/action_controller/testing/test_case.rb +345 -0
  58. data/lib/action_controller/translation.rb +13 -0
  59. data/lib/action_controller/url_rewriter.rb +204 -0
  60. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  61. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  62. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +176 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  65. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  66. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  67. data/lib/action_dispatch.rb +70 -0
  68. data/lib/action_dispatch/http/headers.rb +33 -0
  69. data/lib/action_dispatch/http/mime_type.rb +231 -0
  70. data/lib/action_dispatch/http/mime_types.rb +23 -0
  71. data/lib/action_dispatch/http/request.rb +539 -0
  72. data/lib/action_dispatch/http/response.rb +290 -0
  73. data/lib/action_dispatch/http/status_codes.rb +42 -0
  74. data/lib/action_dispatch/http/utils.rb +20 -0
  75. data/lib/action_dispatch/middleware/callbacks.rb +50 -0
  76. data/lib/action_dispatch/middleware/params_parser.rb +79 -0
  77. data/lib/action_dispatch/middleware/rescue.rb +26 -0
  78. data/lib/action_dispatch/middleware/session/abstract_store.rb +208 -0
  79. data/lib/action_dispatch/middleware/session/cookie_store.rb +235 -0
  80. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +47 -0
  81. data/lib/action_dispatch/middleware/show_exceptions.rb +143 -0
  82. data/lib/action_dispatch/middleware/stack.rb +116 -0
  83. data/lib/action_dispatch/middleware/static.rb +44 -0
  84. data/lib/action_dispatch/middleware/string_coercion.rb +29 -0
  85. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +24 -0
  86. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +26 -0
  87. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +10 -0
  88. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +29 -0
  89. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +2 -0
  90. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +10 -0
  91. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +21 -0
  92. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +2 -0
  93. data/lib/action_dispatch/routing.rb +381 -0
  94. data/lib/action_dispatch/routing/deprecated_mapper.rb +878 -0
  95. data/lib/action_dispatch/routing/mapper.rb +327 -0
  96. data/lib/action_dispatch/routing/route.rb +49 -0
  97. data/lib/action_dispatch/routing/route_set.rb +497 -0
  98. data/lib/action_dispatch/testing/assertions.rb +8 -0
  99. data/lib/action_dispatch/testing/assertions/dom.rb +35 -0
  100. data/lib/action_dispatch/testing/assertions/model.rb +19 -0
  101. data/lib/action_dispatch/testing/assertions/response.rb +145 -0
  102. data/lib/action_dispatch/testing/assertions/routing.rb +144 -0
  103. data/lib/action_dispatch/testing/assertions/selector.rb +639 -0
  104. data/lib/action_dispatch/testing/assertions/tag.rb +123 -0
  105. data/lib/action_dispatch/testing/integration.rb +504 -0
  106. data/lib/action_dispatch/testing/performance_test.rb +15 -0
  107. data/lib/action_dispatch/testing/test_request.rb +83 -0
  108. data/lib/action_dispatch/testing/test_response.rb +131 -0
  109. data/lib/action_pack.rb +24 -0
  110. data/lib/action_pack/version.rb +9 -0
  111. data/lib/action_view.rb +58 -0
  112. data/lib/action_view/base.rb +308 -0
  113. data/lib/action_view/context.rb +44 -0
  114. data/lib/action_view/erb/util.rb +48 -0
  115. data/lib/action_view/helpers.rb +62 -0
  116. data/lib/action_view/helpers/active_model_helper.rb +306 -0
  117. data/lib/action_view/helpers/ajax_helper.rb +68 -0
  118. data/lib/action_view/helpers/asset_tag_helper.rb +830 -0
  119. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  120. data/lib/action_view/helpers/cache_helper.rb +39 -0
  121. data/lib/action_view/helpers/capture_helper.rb +168 -0
  122. data/lib/action_view/helpers/date_helper.rb +988 -0
  123. data/lib/action_view/helpers/debug_helper.rb +38 -0
  124. data/lib/action_view/helpers/form_helper.rb +1102 -0
  125. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  126. data/lib/action_view/helpers/form_tag_helper.rb +495 -0
  127. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  128. data/lib/action_view/helpers/number_helper.rb +311 -0
  129. data/lib/action_view/helpers/prototype_helper.rb +1309 -0
  130. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  131. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  132. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  133. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  134. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  135. data/lib/action_view/helpers/tag_helper.rb +151 -0
  136. data/lib/action_view/helpers/text_helper.rb +594 -0
  137. data/lib/action_view/helpers/translation_helper.rb +39 -0
  138. data/lib/action_view/helpers/url_helper.rb +639 -0
  139. data/lib/action_view/locale/en.yml +117 -0
  140. data/lib/action_view/paths.rb +80 -0
  141. data/lib/action_view/render/partials.rb +342 -0
  142. data/lib/action_view/render/rendering.rb +134 -0
  143. data/lib/action_view/safe_buffer.rb +28 -0
  144. data/lib/action_view/template/error.rb +101 -0
  145. data/lib/action_view/template/handler.rb +36 -0
  146. data/lib/action_view/template/handlers.rb +52 -0
  147. data/lib/action_view/template/handlers/builder.rb +17 -0
  148. data/lib/action_view/template/handlers/erb.rb +53 -0
  149. data/lib/action_view/template/handlers/rjs.rb +18 -0
  150. data/lib/action_view/template/resolver.rb +165 -0
  151. data/lib/action_view/template/template.rb +131 -0
  152. data/lib/action_view/template/text.rb +38 -0
  153. data/lib/action_view/test_case.rb +163 -0
  154. metadata +236 -0
@@ -0,0 +1,38 @@
1
+ module ActionController
2
+ class Middleware < Metal
3
+ class ActionMiddleware
4
+ def initialize(controller, app)
5
+ @controller, @app = controller, app
6
+ end
7
+
8
+ def call(env)
9
+ @controller.build(@app).dispatch(:index, env)
10
+ end
11
+ end
12
+
13
+ class << self
14
+ alias build new
15
+
16
+ def new(app)
17
+ ActionMiddleware.new(self, app)
18
+ end
19
+ end
20
+
21
+ attr_internal :app
22
+
23
+ def process(action)
24
+ response = super
25
+ self.status, self.headers, self.response_body = response if response.is_a?(Array)
26
+ response
27
+ end
28
+
29
+ def initialize(app)
30
+ super()
31
+ @_app = app
32
+ end
33
+
34
+ def index
35
+ call(env)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,10 @@
1
+ require 'active_support/notifications'
2
+
3
+ ActiveSupport::Notifications.subscribe(/(read|write|cache|expire|exist)_(fragment|page)\??/) do |*args|
4
+ event = ActiveSupport::Notifications::Event.new(*args)
5
+
6
+ if logger = ActionController::Base.logger
7
+ human_name = event.name.to_s.humanize
8
+ logger.info("#{human_name} (%.1fms)" % event.duration)
9
+ end
10
+ end
@@ -0,0 +1,183 @@
1
+ module ActionController
2
+ # Polymorphic URL helpers are methods for smart resolution to a named route call when
3
+ # given an Active Record model instance. They are to be used in combination with
4
+ # ActionController::Resources.
5
+ #
6
+ # These methods are useful when you want to generate correct URL or path to a RESTful
7
+ # resource without having to know the exact type of the record in question.
8
+ #
9
+ # Nested resources and/or namespaces are also supported, as illustrated in the example:
10
+ #
11
+ # polymorphic_url([:admin, @article, @comment])
12
+ #
13
+ # results in:
14
+ #
15
+ # admin_article_comment_url(@article, @comment)
16
+ #
17
+ # == Usage within the framework
18
+ #
19
+ # Polymorphic URL helpers are used in a number of places throughout the Rails framework:
20
+ #
21
+ # * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
22
+ # <tt>url_for(@article)</tt>;
23
+ # * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
24
+ # <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
25
+ # action;
26
+ # * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
27
+ # <tt>redirect_to(post)</tt> in your controllers;
28
+ # * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
29
+ # for feed entries.
30
+ #
31
+ # == Prefixed polymorphic helpers
32
+ #
33
+ # In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
34
+ # number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
35
+ # in options. Those are:
36
+ #
37
+ # * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
38
+ # * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
39
+ #
40
+ # Example usage:
41
+ #
42
+ # edit_polymorphic_path(@post) # => "/posts/1/edit"
43
+ # polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf"
44
+ module PolymorphicRoutes
45
+ # Constructs a call to a named RESTful route for the given record and returns the
46
+ # resulting URL string. For example:
47
+ #
48
+ # # calls post_url(post)
49
+ # polymorphic_url(post) # => "http://example.com/posts/1"
50
+ # polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
51
+ # polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
52
+ # polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
53
+ # polymorphic_url(Comment) # => "http://example.com/comments"
54
+ #
55
+ # ==== Options
56
+ #
57
+ # * <tt>:action</tt> - Specifies the action prefix for the named route:
58
+ # <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
59
+ # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
60
+ # Default is <tt>:url</tt>.
61
+ #
62
+ # ==== Examples
63
+ #
64
+ # # an Article record
65
+ # polymorphic_url(record) # same as article_url(record)
66
+ #
67
+ # # a Comment record
68
+ # polymorphic_url(record) # same as comment_url(record)
69
+ #
70
+ # # it recognizes new records and maps to the collection
71
+ # record = Comment.new
72
+ # polymorphic_url(record) # same as comments_url()
73
+ #
74
+ # # the class of a record will also map to the collection
75
+ # polymorphic_url(Comment) # same as comments_url()
76
+ #
77
+ def polymorphic_url(record_or_hash_or_array, options = {})
78
+ if record_or_hash_or_array.kind_of?(Array)
79
+ record_or_hash_or_array = record_or_hash_or_array.compact
80
+ record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
81
+ end
82
+
83
+ record = extract_record(record_or_hash_or_array)
84
+ record = record.to_model if record.respond_to?(:to_model)
85
+
86
+ args = case record_or_hash_or_array
87
+ when Hash; [ record_or_hash_or_array ]
88
+ when Array; record_or_hash_or_array.dup
89
+ else [ record_or_hash_or_array ]
90
+ end
91
+
92
+ inflection = if options[:action].to_s == "new"
93
+ args.pop
94
+ :singular
95
+ elsif (record.respond_to?(:new_record?) && record.new_record?) ||
96
+ (record.respond_to?(:destroyed?) && record.destroyed?)
97
+ args.pop
98
+ :plural
99
+ elsif record.is_a?(Class)
100
+ args.pop
101
+ :plural
102
+ else
103
+ :singular
104
+ end
105
+
106
+ args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
107
+ named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
108
+
109
+ url_options = options.except(:action, :routing_type)
110
+ unless url_options.empty?
111
+ args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
112
+ end
113
+
114
+ __send__(named_route, *args)
115
+ end
116
+
117
+ # Returns the path component of a URL for the given record. It uses
118
+ # <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
119
+ def polymorphic_path(record_or_hash_or_array, options = {})
120
+ polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
121
+ end
122
+
123
+ %w(edit new).each do |action|
124
+ module_eval <<-EOT, __FILE__, __LINE__
125
+ def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
126
+ polymorphic_url( # polymorphic_url(
127
+ record_or_hash, # record_or_hash,
128
+ options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
129
+ end # end
130
+ #
131
+ def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
132
+ polymorphic_url( # polymorphic_url(
133
+ record_or_hash, # record_or_hash,
134
+ options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
135
+ end # end
136
+ EOT
137
+ end
138
+
139
+ private
140
+ def action_prefix(options)
141
+ options[:action] ? "#{options[:action]}_" : ''
142
+ end
143
+
144
+ def routing_type(options)
145
+ options[:routing_type] || :url
146
+ end
147
+
148
+ def build_named_route_call(records, inflection, options = {})
149
+ unless records.is_a?(Array)
150
+ record = extract_record(records)
151
+ route = ''
152
+ else
153
+ record = records.pop
154
+ route = records.inject("") do |string, parent|
155
+ if parent.is_a?(Symbol) || parent.is_a?(String)
156
+ string << "#{parent}_"
157
+ else
158
+ string << RecordIdentifier.__send__("plural_class_name", parent).singularize
159
+ string << "_"
160
+ end
161
+ end
162
+ end
163
+
164
+ if record.is_a?(Symbol) || record.is_a?(String)
165
+ route << "#{record}_"
166
+ else
167
+ route << RecordIdentifier.__send__("plural_class_name", record)
168
+ route = route.singularize if inflection == :singular
169
+ route << "_"
170
+ end
171
+
172
+ action_prefix(options) + route + routing_type(options).to_s
173
+ end
174
+
175
+ def extract_record(record_or_hash_or_array)
176
+ case record_or_hash_or_array
177
+ when Array; record_or_hash_or_array.last
178
+ when Hash; record_or_hash_or_array[:id]
179
+ else record_or_hash_or_array
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,91 @@
1
+ require 'active_support/core_ext/module'
2
+
3
+ module ActionController
4
+ # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
5
+ # Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate
6
+ # the view actions to a higher logical level. Example:
7
+ #
8
+ # # routes
9
+ # map.resources :posts
10
+ #
11
+ # # view
12
+ # <% div_for(post) do %> <div id="post_45" class="post">
13
+ # <%= post.body %> What a wonderful world!
14
+ # <% end %> </div>
15
+ #
16
+ # # controller
17
+ # def destroy
18
+ # post = Post.find(params[:id])
19
+ # post.destroy
20
+ #
21
+ # respond_to do |format|
22
+ # format.html { redirect_to(post) } # Calls polymorphic_url(post) which in turn calls post_url(post)
23
+ # format.js do
24
+ # # Calls: new Effect.fade('post_45');
25
+ # render(:update) { |page| page[post].visual_effect(:fade) }
26
+ # end
27
+ # end
28
+ # end
29
+ #
30
+ # As the example above shows, you can stop caring to a large extent what the actual id of the post is. You just know
31
+ # that one is being assigned and that the subsequent calls in redirect_to and the RJS expect that same naming
32
+ # convention and allows you to write less code if you follow it.
33
+ module RecordIdentifier
34
+ extend self
35
+
36
+ JOIN = '_'.freeze
37
+ NEW = 'new'.freeze
38
+
39
+ # The DOM class convention is to use the singular form of an object or class. Examples:
40
+ #
41
+ # dom_class(post) # => "post"
42
+ # dom_class(Person) # => "person"
43
+ #
44
+ # If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
45
+ #
46
+ # dom_class(post, :edit) # => "edit_post"
47
+ # dom_class(Person, :edit) # => "edit_person"
48
+ def dom_class(record_or_class, prefix = nil)
49
+ singular = singular_class_name(record_or_class)
50
+ prefix ? "#{prefix}#{JOIN}#{singular}" : singular
51
+ end
52
+
53
+ # The DOM id convention is to use the singular form of an object or class with the id following an underscore.
54
+ # If no id is found, prefix with "new_" instead. Examples:
55
+ #
56
+ # dom_id(Post.find(45)) # => "post_45"
57
+ # dom_id(Post.new) # => "new_post"
58
+ #
59
+ # If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
60
+ #
61
+ # dom_id(Post.find(45), :edit) # => "edit_post_45"
62
+ def dom_id(record, prefix = nil)
63
+ if record_id = record.id
64
+ "#{dom_class(record, prefix)}#{JOIN}#{record_id}"
65
+ else
66
+ dom_class(record, prefix || NEW)
67
+ end
68
+ end
69
+
70
+ # Returns the plural class name of a record or class. Examples:
71
+ #
72
+ # plural_class_name(post) # => "posts"
73
+ # plural_class_name(Highrise::Person) # => "highrise_people"
74
+ def plural_class_name(record_or_class)
75
+ model_name_from_record_or_class(record_or_class).plural
76
+ end
77
+
78
+ # Returns the singular class name of a record or class. Examples:
79
+ #
80
+ # singular_class_name(post) # => "post"
81
+ # singular_class_name(Highrise::Person) # => "highrise_person"
82
+ def singular_class_name(record_or_class)
83
+ model_name_from_record_or_class(record_or_class).singular
84
+ end
85
+
86
+ private
87
+ def model_name_from_record_or_class(record_or_class)
88
+ (record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,111 @@
1
+ require 'active_support/core_ext/object/conversions'
2
+ require "rack/test"
3
+
4
+ module ActionController #:nodoc:
5
+ # Essentially generates a modified Tempfile object similar to the object
6
+ # you'd get from the standard library CGI module in a multipart
7
+ # request. This means you can use an ActionController::TestUploadedFile
8
+ # object in the params of a test request in order to simulate
9
+ # a file upload.
10
+ #
11
+ # Usage example, within a functional test:
12
+ # post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png')
13
+ #
14
+ # Pass a true third parameter to ensure the uploaded file is opened in binary mode (only required for Windows):
15
+ # post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png', :binary)
16
+ TestUploadedFile = Rack::Test::UploadedFile
17
+
18
+ module TestProcess
19
+ def assigns(key = nil)
20
+ assigns = {}
21
+ @controller.instance_variable_names.each do |ivar|
22
+ next if ActionController::Base.protected_instance_variables.include?(ivar)
23
+ assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar)
24
+ end
25
+
26
+ key.nil? ? assigns : assigns[key.to_s]
27
+ end
28
+
29
+ def session
30
+ @request.session
31
+ end
32
+
33
+ def flash
34
+ @request.flash
35
+ end
36
+
37
+ def cookies
38
+ @request.cookies.merge(@response.cookies)
39
+ end
40
+
41
+ def redirect_to_url
42
+ @response.redirect_url
43
+ end
44
+
45
+ def html_document
46
+ xml = @response.content_type =~ /xml$/
47
+ @html_document ||= HTML::Document.new(@response.body, false, xml)
48
+ end
49
+
50
+ def find_tag(conditions)
51
+ html_document.find(conditions)
52
+ end
53
+
54
+ def find_all_tag(conditions)
55
+ html_document.find_all(conditions)
56
+ end
57
+
58
+ def method_missing(selector, *args, &block)
59
+ if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector)
60
+ @controller.send(selector, *args, &block)
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ # Shortcut for <tt>ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>:
67
+ #
68
+ # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
69
+ #
70
+ # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
71
+ # This will not affect other platforms:
72
+ #
73
+ # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary)
74
+ def fixture_file_upload(path, mime_type = nil, binary = false)
75
+ fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path)
76
+ ActionController::TestUploadedFile.new("#{fixture_path}#{path}", mime_type, binary)
77
+ end
78
+
79
+ # A helper to make it easier to test different route configurations.
80
+ # This method temporarily replaces ActionController::Routing::Routes
81
+ # with a new RouteSet instance.
82
+ #
83
+ # The new instance is yielded to the passed block. Typically the block
84
+ # will create some routes using <tt>map.draw { map.connect ... }</tt>:
85
+ #
86
+ # with_routing do |set|
87
+ # set.draw do |map|
88
+ # map.connect ':controller/:action/:id'
89
+ # assert_equal(
90
+ # ['/content/10/show', {}],
91
+ # map.generate(:controller => 'content', :id => 10, :action => 'show')
92
+ # end
93
+ # end
94
+ # end
95
+ #
96
+ def with_routing
97
+ real_routes = ActionController::Routing::Routes
98
+ ActionController::Routing.module_eval { remove_const :Routes }
99
+
100
+ temporary_routes = ActionController::Routing::RouteSet.new
101
+ ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
102
+
103
+ yield temporary_routes
104
+ ensure
105
+ if ActionController::Routing.const_defined? :Routes
106
+ ActionController::Routing.module_eval { remove_const :Routes }
107
+ end
108
+ ActionController::Routing.const_set(:Routes, real_routes) if real_routes
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,345 @@
1
+ require 'active_support/test_case'
2
+ require 'rack/session/abstract/id'
3
+
4
+ module ActionController
5
+ class TestRequest < ActionDispatch::TestRequest #:nodoc:
6
+ def initialize(env = {})
7
+ super
8
+
9
+ self.session = TestSession.new
10
+ self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => ActiveSupport::SecureRandom.hex(16))
11
+ end
12
+
13
+ class Result < ::Array #:nodoc:
14
+ def to_s() join '/' end
15
+ def self.new_escaped(strings)
16
+ new strings.collect {|str| URI.unescape str}
17
+ end
18
+ end
19
+
20
+ def assign_parameters(controller_path, action, parameters = {})
21
+ parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
22
+ extra_keys = ActionController::Routing::Routes.extra_keys(parameters)
23
+ non_path_parameters = get? ? query_parameters : request_parameters
24
+ parameters.each do |key, value|
25
+ if value.is_a? Fixnum
26
+ value = value.to_s
27
+ elsif value.is_a? Array
28
+ value = Result.new(value)
29
+ end
30
+
31
+ if extra_keys.include?(key.to_sym)
32
+ non_path_parameters[key] = value
33
+ else
34
+ path_parameters[key.to_s] = value
35
+ end
36
+ end
37
+
38
+ params = self.request_parameters.dup
39
+
40
+ %w(controller action only_path).each do |k|
41
+ params.delete(k)
42
+ params.delete(k.to_sym)
43
+ end
44
+
45
+ data = params.to_query
46
+ @env['CONTENT_LENGTH'] = data.length.to_s
47
+ @env['rack.input'] = StringIO.new(data)
48
+ end
49
+
50
+ def recycle!
51
+ @formats = nil
52
+ @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
53
+ @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
54
+ @env['action_dispatch.request.query_parameters'] = {}
55
+ end
56
+ end
57
+
58
+ class TestResponse < ActionDispatch::TestResponse
59
+ def recycle!
60
+ @status = 200
61
+ @header = {}
62
+ @writer = lambda { |x| @body << x }
63
+ @block = nil
64
+ @length = 0
65
+ @body = []
66
+ @charset = nil
67
+ @content_type = nil
68
+
69
+ @request = @template = nil
70
+ end
71
+ end
72
+
73
+ class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc:
74
+ DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS
75
+
76
+ def initialize(session = {})
77
+ replace(session.stringify_keys)
78
+ @loaded = true
79
+ end
80
+ end
81
+
82
+ # Superclass for ActionController functional tests. Functional tests allow you to
83
+ # test a single controller action per test method. This should not be confused with
84
+ # integration tests (see ActionController::IntegrationTest), which are more like
85
+ # "stories" that can involve multiple controllers and mutliple actions (i.e. multiple
86
+ # different HTTP requests).
87
+ #
88
+ # == Basic example
89
+ #
90
+ # Functional tests are written as follows:
91
+ # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate
92
+ # an HTTP request.
93
+ # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
94
+ # the controller's HTTP response, the database contents, etc.
95
+ #
96
+ # For example:
97
+ #
98
+ # class BooksControllerTest < ActionController::TestCase
99
+ # def test_create
100
+ # # Simulate a POST response with the given HTTP parameters.
101
+ # post(:create, :book => { :title => "Love Hina" })
102
+ #
103
+ # # Assert that the controller tried to redirect us to
104
+ # # the created book's URI.
105
+ # assert_response :found
106
+ #
107
+ # # Assert that the controller really put the book in the database.
108
+ # assert_not_nil Book.find_by_title("Love Hina")
109
+ # end
110
+ # end
111
+ #
112
+ # == Special instance variables
113
+ #
114
+ # ActionController::TestCase will also automatically provide the following instance
115
+ # variables for use in the tests:
116
+ #
117
+ # <b>@controller</b>::
118
+ # The controller instance that will be tested.
119
+ # <b>@request</b>::
120
+ # An ActionController::TestRequest, representing the current HTTP
121
+ # request. You can modify this object before sending the HTTP request. For example,
122
+ # you might want to set some session properties before sending a GET request.
123
+ # <b>@response</b>::
124
+ # An ActionController::TestResponse object, representing the response
125
+ # of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
126
+ # after calling +post+. If the various assert methods are not sufficient, then you
127
+ # may use this object to inspect the HTTP response in detail.
128
+ #
129
+ # (Earlier versions of Rails required each functional test to subclass
130
+ # Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
131
+ #
132
+ # == Controller is automatically inferred
133
+ #
134
+ # ActionController::TestCase will automatically infer the controller under test
135
+ # from the test class name. If the controller cannot be inferred from the test
136
+ # class name, you can explicitly set it with +tests+.
137
+ #
138
+ # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
139
+ # tests WidgetController
140
+ # end
141
+ #
142
+ # == Testing controller internals
143
+ #
144
+ # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
145
+ # can be used against. These collections are:
146
+ #
147
+ # * assigns: Instance variables assigned in the action that are available for the view.
148
+ # * session: Objects being saved in the session.
149
+ # * flash: The flash objects currently in the session.
150
+ # * cookies: Cookies being sent to the user on this request.
151
+ #
152
+ # These collections can be used just like any other hash:
153
+ #
154
+ # assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
155
+ # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
156
+ # assert flash.empty? # makes sure that there's nothing in the flash
157
+ #
158
+ # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To
159
+ # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
160
+ # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work.
161
+ #
162
+ # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url.
163
+ #
164
+ # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
165
+ # action call which can then be asserted against.
166
+ #
167
+ # == Manipulating the request collections
168
+ #
169
+ # The collections described above link to the response, so you can test if what the actions were expected to do happened. But
170
+ # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions
171
+ # and cookies, though. For sessions, you just do:
172
+ #
173
+ # @request.session[:key] = "value"
174
+ # @request.cookies["key"] = "value"
175
+ #
176
+ # == Testing named routes
177
+ #
178
+ # If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
179
+ # Example:
180
+ #
181
+ # assert_redirected_to page_url(:title => 'foo')
182
+ class TestCase < ActiveSupport::TestCase
183
+ include TestProcess
184
+
185
+ # Executes a request simulating GET HTTP method and set/volley the response
186
+ def get(action, parameters = nil, session = nil, flash = nil)
187
+ process(action, parameters, session, flash, "GET")
188
+ end
189
+
190
+ # Executes a request simulating POST HTTP method and set/volley the response
191
+ def post(action, parameters = nil, session = nil, flash = nil)
192
+ process(action, parameters, session, flash, "POST")
193
+ end
194
+
195
+ # Executes a request simulating PUT HTTP method and set/volley the response
196
+ def put(action, parameters = nil, session = nil, flash = nil)
197
+ process(action, parameters, session, flash, "PUT")
198
+ end
199
+
200
+ # Executes a request simulating DELETE HTTP method and set/volley the response
201
+ def delete(action, parameters = nil, session = nil, flash = nil)
202
+ process(action, parameters, session, flash, "DELETE")
203
+ end
204
+
205
+ # Executes a request simulating HEAD HTTP method and set/volley the response
206
+ def head(action, parameters = nil, session = nil, flash = nil)
207
+ process(action, parameters, session, flash, "HEAD")
208
+ end
209
+
210
+ def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
211
+ @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
212
+ @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
213
+ returning __send__(request_method, action, parameters, session, flash) do
214
+ @request.env.delete 'HTTP_X_REQUESTED_WITH'
215
+ @request.env.delete 'HTTP_ACCEPT'
216
+ end
217
+ end
218
+ alias xhr :xml_http_request
219
+
220
+ def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
221
+ # Sanity check for required instance variables so we can give an
222
+ # understandable error message.
223
+ %w(@controller @request @response).each do |iv_name|
224
+ if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
225
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
226
+ end
227
+ end
228
+
229
+ @request.recycle!
230
+ @response.recycle!
231
+ @controller.response_body = nil
232
+ @controller.formats = nil
233
+ @controller.params = nil
234
+
235
+ @html_document = nil
236
+ @request.env['REQUEST_METHOD'] = http_method
237
+
238
+ parameters ||= {}
239
+ @request.assign_parameters(@controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
240
+
241
+ @request.session = ActionController::TestSession.new(session) unless session.nil?
242
+ @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
243
+
244
+ @controller.request = @request
245
+ @controller.params.merge!(parameters)
246
+ build_request_uri(action, parameters)
247
+ Base.class_eval { include Testing }
248
+ @controller.process_with_new_base_test(@request, @response)
249
+ @response
250
+ end
251
+
252
+ include ActionDispatch::Assertions
253
+
254
+ # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
255
+ # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
256
+ # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
257
+ # than 0.0.0.0.
258
+ #
259
+ # The exception is stored in the exception accessor for further inspection.
260
+ module RaiseActionExceptions
261
+ def self.included(base)
262
+ base.class_eval do
263
+ attr_accessor :exception
264
+ protected :exception, :exception=
265
+ end
266
+ end
267
+
268
+ protected
269
+ def rescue_action_without_handler(e)
270
+ self.exception = e
271
+
272
+ if request.remote_addr == "0.0.0.0"
273
+ raise(e)
274
+ else
275
+ super(e)
276
+ end
277
+ end
278
+ end
279
+
280
+ setup :setup_controller_request_and_response
281
+
282
+ @@controller_class = nil
283
+
284
+ class << self
285
+ # Sets the controller class name. Useful if the name can't be inferred from test class.
286
+ # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
287
+ def tests(controller_class)
288
+ self.controller_class = controller_class
289
+ end
290
+
291
+ def controller_class=(new_class)
292
+ prepare_controller_class(new_class) if new_class
293
+ write_inheritable_attribute(:controller_class, new_class)
294
+ end
295
+
296
+ def controller_class
297
+ if current_controller_class = read_inheritable_attribute(:controller_class)
298
+ current_controller_class
299
+ else
300
+ self.controller_class = determine_default_controller_class(name)
301
+ end
302
+ end
303
+
304
+ def determine_default_controller_class(name)
305
+ name.sub(/Test$/, '').constantize
306
+ rescue NameError
307
+ nil
308
+ end
309
+
310
+ def prepare_controller_class(new_class)
311
+ new_class.send :include, RaiseActionExceptions
312
+ end
313
+ end
314
+
315
+ def setup_controller_request_and_response
316
+ @request = TestRequest.new
317
+ @response = TestResponse.new
318
+
319
+ if klass = self.class.controller_class
320
+ @controller ||= klass.new rescue nil
321
+ end
322
+
323
+ if @controller
324
+ @controller.request = @request
325
+ @controller.params = {}
326
+ end
327
+ end
328
+
329
+ # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
330
+ def rescue_action_in_public!
331
+ @request.remote_addr = '208.77.188.166' # example.com
332
+ end
333
+
334
+ private
335
+ def build_request_uri(action, parameters)
336
+ unless @request.env['REQUEST_URI']
337
+ options = @controller.__send__(:rewrite_options, parameters)
338
+ options.update(:only_path => true, :action => action)
339
+
340
+ url = ActionController::UrlRewriter.new(@request, parameters)
341
+ @request.request_uri = url.rewrite(options)
342
+ end
343
+ end
344
+ end
345
+ end